Jake Vanderwerf
2026-02-06 94de71140be2d0c80bf6a2e03cb9381b37736ed5
=Some minor CRUD.js and UploadManager.js tweaks
42 files modified
1 files added
1969 ■■■■ changed files
JVBase.php 2 ●●●●● patch | view | raw | blame | history
activate.php 1 ●●●● patch | view | raw | blame | history
assets/css/dash.css 1 ●●●● patch | view | raw | blame | history
assets/css/icons.css 2 ●●● patch | view | raw | blame | history
assets/js/concise/CRUD.js 124 ●●●●● patch | view | raw | blame | history
assets/js/concise/CRUDOld.js 4 ●●●● patch | view | raw | blame | history
assets/js/concise/ContentManager.js 4 ●●●● patch | view | raw | blame | history
assets/js/concise/FavouritesManager.js 8 ●●●● patch | view | raw | blame | history
assets/js/concise/NewsManager.js 2 ●●● patch | view | raw | blame | history
assets/js/concise/NotificationManager.js 2 ●●● patch | view | raw | blame | history
assets/js/concise/Notifications.js 6 ●●●● patch | view | raw | blame | history
assets/js/concise/Queue.js 93 ●●●● patch | view | raw | blame | history
assets/js/concise/UploadManager.js 97 ●●●● patch | view | raw | blame | history
assets/js/concise/UserSettings.js 2 ●●● patch | view | raw | blame | history
assets/js/min/ContentManager.min.js 2 ●●● patch | view | raw | blame | history
assets/js/min/crud.min.js 2 ●●● patch | view | raw | blame | history
assets/js/min/favouritesManager.min.js 2 ●●● patch | view | raw | blame | history
assets/js/min/news.min.js 2 ●●● patch | view | raw | blame | history
assets/js/min/notificationManager.min.js 2 ●●● patch | view | raw | blame | history
assets/js/min/notifications.min.js 2 ●●● patch | view | raw | blame | history
assets/js/min/queue.min.js 2 ●●● patch | view | raw | blame | history
assets/js/min/uploader.min.js 2 ●●● patch | view | raw | blame | history
inc/blocks/FormBlock.php 2 ●●● patch | view | raw | blame | history
inc/helpers/renderFields.php 33 ●●●●● patch | view | raw | blame | history
inc/managers/CRUDManager.php 2 ●●●●● patch | view | raw | blame | history
inc/managers/DashboardManager.php 43 ●●●● patch | view | raw | blame | history
inc/managers/queue/Progress.php 2 ●●●●● patch | view | raw | blame | history
inc/managers/queue/Queue.php 2 ●●● patch | view | raw | blame | history
inc/managers/queue/Storage.php 13 ●●●● patch | view | raw | blame | history
inc/managers/queue/_setup.php 1 ●●●● patch | view | raw | blame | history
inc/managers/queue/executors/ContentExecutor.php 42 ●●●●● patch | view | raw | blame | history
inc/managers/queue/executors/ContentTermExecutor.php 338 ●●●●● patch | view | raw | blame | history
inc/managers/queue/executors/UploadExecutor.php 306 ●●●●● patch | view | raw | blame | history
inc/meta/Form.php 36 ●●●●● patch | view | raw | blame | history
inc/meta/Storage.php 54 ●●●●● patch | view | raw | blame | history
inc/rest/PermissionHandler.php 8 ●●●● patch | view | raw | blame | history
inc/rest/_setup.php 1 ●●●● patch | view | raw | blame | history
inc/rest/routes/ContentRoutes.php 20 ●●●●● patch | view | raw | blame | history
inc/rest/routes/ContentTermsRoutes.php 551 ●●●●● patch | view | raw | blame | history
inc/rest/routes/LoginRoutes.php 89 ●●●● patch | view | raw | blame | history
inc/rest/routes/QueueRoutes.php 31 ●●●● patch | view | raw | blame | history
inc/rest/routes/UploadRoutes.php 7 ●●●● patch | view | raw | blame | history
inc/ui/CRUDSkeleton.php 24 ●●●● patch | view | raw | blame | history
JVBase.php
@@ -30,6 +30,7 @@
//use JVBase\rest\routes\BioRoutes;
use JVBase\rest\routes\SettingsRoutes;
//use JVBase\rest\routes\ShopRoutes;
use JVBase\rest\routes\ContentTermsRoutes;
use JVBase\rest\routes\SEORoutes;
use JVBase\rest\routes\QueueRoutes;
use JVBase\rest\routes\ErrorRoutes;
@@ -149,6 +150,7 @@
            $this->routes['content'] = new ContentRoutes();
//            $this->routes['bio']    = new BioRoutes();
//          $this->routes['shop'] = new ShopRoutes();
            $this->routes['contentTax'] = new ContentTermsRoutes();
            $this->routes['options'] = new OptionsRoutes();
        }
activate.php
@@ -203,7 +203,6 @@
        ARRAY_FILTER_USE_KEY
    );
    foreach ($options as $key => $value) {
        error_log('Deleting Option'.$key);
        delete_option($key);
    }
}
assets/css/dash.css
@@ -0,0 +1 @@
.icon-squares-four{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xMDQsNDJINTZBMTQsMTQsMCwwLDAsNDIsNTZ2NDhhMTQsMTQsMCwwLDAsMTQsMTRoNDhhMTQsMTQsMCwwLDAsMTQtMTRWNTZBMTQsMTQsMCwwLDAsMTA0LDQyWm0yLDYyYTIsMiwwLDAsMS0yLDJINTZhMiwyLDAsMCwxLTItMlY1NmEyLDIsMCwwLDEsMi0yaDQ4YTIsMiwwLDAsMSwyLDJabTk0LTYySDE1MmExNCwxNCwwLDAsMC0xNCwxNHY0OGExNCwxNCwwLDAsMCwxNCwxNGg0OGExNCwxNCwwLDAsMCwxNC0xNFY1NkExNCwxNCwwLDAsMCwyMDAsNDJabTIsNjJhMiwyLDAsMCwxLTIsMkgxNTJhMiwyLDAsMCwxLTItMlY1NmEyLDIsMCwwLDEsMi0yaDQ4YTIsMiwwLDAsMSwyLDJabS05OCwzNEg1NmExNCwxNCwwLDAsMC0xNCwxNHY0OGExNCwxNCwwLDAsMCwxNCwxNGg0OGExNCwxNCwwLDAsMCwxNC0xNFYxNTJBMTQsMTQsMCwwLDAsMTA0LDEzOFptMiw2MmEyLDIsMCwwLDEtMiwySDU2YTIsMiwwLDAsMS0yLTJWMTUyYTIsMiwwLDAsMSwyLTJoNDhhMiwyLDAsMCwxLDIsMlptOTQtNjJIMTUyYTE0LDE0LDAsMCwwLTE0LDE0djQ4YTE0LDE0LDAsMCwwLDE0LDE0aDQ4YTE0LDE0LDAsMCwwLDE0LTE0VjE1MkExNCwxNCwwLDAsMCwyMDAsMTM4Wm0yLDYyYTIsMiwwLDAsMS0yLDJIMTUyYTIsMiwwLDAsMS0yLTJWMTUyYTIsMiwwLDAsMSwyLTJoNDhhMiwyLDAsMCwxLDIsMloiLz48L3N2Zz4=');}.icon-rows{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMDgsMTM4SDQ4YTE0LDE0LDAsMCwwLTE0LDE0djQwYTE0LDE0LDAsMCwwLDE0LDE0SDIwOGExNCwxNCwwLDAsMCwxNC0xNFYxNTJBMTQsMTQsMCwwLDAsMjA4LDEzOFptMiw1NGEyLDIsMCwwLDEtMiwySDQ4YTIsMiwwLDAsMS0yLTJWMTUyYTIsMiwwLDAsMSwyLTJIMjA4YTIsMiwwLDAsMSwyLDJaTTIwOCw1MEg0OEExNCwxNCwwLDAsMCwzNCw2NHY0MGExNCwxNCwwLDAsMCwxNCwxNEgyMDhhMTQsMTQsMCwwLDAsMTQtMTRWNjRBMTQsMTQsMCwwLDAsMjA4LDUwWm0yLDU0YTIsMiwwLDAsMS0yLDJINDhhMiwyLDAsMCwxLTItMlY2NGEyLDIsMCwwLDEsMi0ySDIwOGEyLDIsMCwwLDEsMiwyWiIvPjwvc3ZnPg==');}.icon-table{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjQsNTBIMzJhNiw2LDAsMCwwLTYsNlYxOTJhMTQsMTQsMCwwLDAsMTQsMTRIMjE2YTE0LDE0LDAsMCwwLDE0LTE0VjU2QTYsNiwwLDAsMCwyMjQsNTBaTTM4LDExMEg4MnYzNkgzOFptNTYsMEgyMTh2MzZIOTRaTTIxOCw2MlY5OEgzOFY2MlpNMzgsMTkyVjE1OEg4MnYzNkg0MEEyLDIsMCwwLDEsMzgsMTkyWm0xNzgsMkg5NFYxNThIMjE4djM0QTIsMiwwLDAsMSwyMTYsMTk0WiIvPjwvc3ZnPg==');}.icon-infinity{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yNDYsMTI4YTU0LDU0LDAsMCwxLTkyLjE4LDM4LjE4LDMuMDcsMy4wNywwLDAsMS0uMjUtLjI2bC02MC02Ny43NGE0Miw0MiwwLDEsMCwwLDU5LjY0bDguNTctOS42N2E2LDYsMCwxLDEsOSw4bC04LjY5LDkuODFhMy4wNywzLjA3LDAsMCwxLS4yNS4yNiw1NCw1NCwwLDEsMSwwLTc2LjM2LDMuMDcsMy4wNywwLDAsMSwuMjUuMjZsNjAsNjcuNzRhNDIsNDIsMCwxLDAsMC01OS42NGwtOC41Nyw5LjY3YTYsNiwwLDEsMS05LThsOC42OS05LjgxYTMuMDcsMy4wNywwLDAsMSwuMjUtLjI2QTU0LDU0LDAsMCwxLDI0NiwxMjhaIi8+PC9zdmc+');}.icon-eye{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yNDUuNDgsMTI1LjU3Yy0uMzQtLjc4LTguNjYtMTkuMjMtMjcuMjQtMzcuODFDMjAxLDcwLjU0LDE3MS4zOCw1MCwxMjgsNTBTNTUsNzAuNTQsMzcuNzYsODcuNzZjLTE4LjU4LDE4LjU4LTI2LjksMzctMjcuMjQsMzcuODFhNiw2LDAsMCwwLDAsNC44OGMuMzQuNzcsOC42NiwxOS4yMiwyNy4yNCwzNy44QzU1LDE4NS40Nyw4NC42MiwyMDYsMTI4LDIwNnM3My0yMC41Myw5MC4yNC0zNy43NWMxOC41OC0xOC41OCwyNi45LTM3LDI3LjI0LTM3LjhBNiw2LDAsMCwwLDI0NS40OCwxMjUuNTdaTTEyOCwxOTRjLTMxLjM4LDAtNTguNzgtMTEuNDItODEuNDUtMzMuOTNBMTM0Ljc3LDEzNC43NywwLDAsMSwyMi42OSwxMjgsMTM0LjU2LDEzNC41NiwwLDAsMSw0Ni41NSw5NS45NEM2OS4yMiw3My40Miw5Ni42Miw2MiwxMjgsNjJzNTguNzgsMTEuNDIsODEuNDUsMzMuOTRBMTM0LjU2LDEzNC41NiwwLDAsMSwyMzMuMzEsMTI4QzIyNi45NCwxNDAuMjEsMTk1LDE5NCwxMjgsMTk0Wm0wLTExMmE0Niw0NiwwLDEsMCw0Niw0NkE0Ni4wNiw0Ni4wNiwwLDAsMCwxMjgsODJabTAsODBhMzQsMzQsMCwxLDEsMzQtMzRBMzQsMzQsMCwwLDEsMTI4LDE2MloiLz48L3N2Zz4=');}.icon-eye-slash{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik01Mi40NCwzNkE2LDYsMCwwLDAsNDMuNTYsNDRMNjQuNDQsNjdjLTM3LjI4LDIxLjktNTMuMjMsNTctNTMuOTIsNTguNTdhNiw2LDAsMCwwLDAsNC44OGMuMzQuNzcsOC42NiwxOS4yMiwyNy4yNCwzNy44QzU1LDE4NS40Nyw4NC42MiwyMDYsMTI4LDIwNmExMjQuOTEsMTI0LjkxLDAsMCwwLDUyLjU3LTExLjI1bDIzLDI1LjI5YTYsNiwwLDAsMCw4Ljg4LTguMDhabTQ4LjYyLDcxLjMyLDQ1LDQ5LjUyYTM0LDM0LDAsMCwxLTQ1LTQ5LjUyWk0xMjgsMTk0Yy0zMS4zOCwwLTU4Ljc4LTExLjQyLTgxLjQ1LTMzLjkzQTEzNC41NywxMzQuNTcsMCwwLDEsMjIuNjksMTI4YzQuMjktOC4yLDIwLjEtMzUuMTgsNTAtNTEuOTFMOTIuODksOTguM2E0Niw0NiwwLDAsMCw2MS4zNSw2Ny40OGwxNy44MSwxOS42QTExMy40NywxMTMuNDcsMCwwLDEsMTI4LDE5NFptNi40LTk5LjRhNiw2LDAsMCwxLDIuMjUtMTEuNzksNDYuMTcsNDYuMTcsMCwwLDEsMzcuMTUsNDAuODcsNiw2LDAsMCwxLTUuNDIsNi41M2wtLjU2LDBhNiw2LDAsMCwxLTYtNS40NUEzNC4xLDM0LjEsMCwwLDAsMTM0LjQsOTQuNlptMTExLjA4LDM1Ljg1Yy0uNDEuOTItMTAuMzcsMjMtMzIuODYsNDMuMTJhNiw2LDAsMSwxLTgtOC45NEExMzQuMDcsMTM0LjA3LDAsMCwwLDIzMy4zMSwxMjhhMTM0LjY3LDEzNC42NywwLDAsMC0yMy44Ni0zMi4wN0MxODYuNzgsNzMuNDIsMTU5LjM4LDYyLDEyOCw2MmExMjAuMTksMTIwLjE5LDAsMCwwLTE5LjY5LDEuNiw2LDYsMCwxLDEtMi0xMS44M0ExMzEuMTIsMTMxLjEyLDAsMCwxLDEyOCw1MGM0My4zOCwwLDczLDIwLjU0LDkwLjI0LDM3Ljc2LDE4LjU4LDE4LjU4LDI2LjksMzcsMjcuMjQsMzcuODFBNiw2LDAsMCwxLDI0NS40OCwxMzAuNDVaIi8+PC9zdmc+');}.icon-calendar{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMDgsMzRIMTgyVjI0YTYsNiwwLDAsMC0xMiwwVjM0SDg2VjI0YTYsNiwwLDAsMC0xMiwwVjM0SDQ4QTE0LDE0LDAsMCwwLDM0LDQ4VjIwOGExNCwxNCwwLDAsMCwxNCwxNEgyMDhhMTQsMTQsMCwwLDAsMTQtMTRWNDhBMTQsMTQsMCwwLDAsMjA4LDM0Wk00OCw0Nkg3NFY1NmE2LDYsMCwwLDAsMTIsMFY0Nmg4NFY1NmE2LDYsMCwwLDAsMTIsMFY0NmgyNmEyLDIsMCwwLDEsMiwyVjgySDQ2VjQ4QTIsMiwwLDAsMSw0OCw0NlpNMjA4LDIxMEg0OGEyLDIsMCwwLDEtMi0yVjk0SDIxMFYyMDhBMiwyLDAsMCwxLDIwOCwyMTBabS05OC05MHY2NGE2LDYsMCwwLDEtMTIsMFYxMjkuNzFsLTcuMzIsMy42NmE2LDYsMCwxLDEtNS4zNi0xMC43NGwxNi04QTYsNiwwLDAsMSwxMTAsMTIwWm01OS41NywyOS4yNUwxNDgsMTc4aDIwYTYsNiwwLDAsMSwwLDEySDEzNmE2LDYsMCwwLDEtNC44LTkuNkwxNjAsMTQyYTEwLDEwLDAsMSwwLTE2LjY1LTExQTYsNiwwLDEsMSwxMzMsMTI1YTIyLDIyLDAsMSwxLDM2LjYyLDI0LjI2WiIvPjwvc3ZnPg==');}.icon-sort-ascending{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xMjYsMTI4YTYsNiwwLDAsMS02LDZINDhhNiw2LDAsMCwxLDAtMTJoNzJBNiw2LDAsMCwxLDEyNiwxMjhaTTQ4LDcwSDE4NGE2LDYsMCwwLDAsMC0xMkg0OGE2LDYsMCwwLDAsMCwxMlptNTYsMTE2SDQ4YTYsNiwwLDAsMCwwLDEyaDU2YTYsNiwwLDAsMCwwLTEyWm0xMjQuMjQtMjIuMjRhNiw2LDAsMCwwLTguNDgsMEwxOTAsMTkzLjUxVjExMmE2LDYsMCwwLDAtMTIsMHY4MS41MWwtMjkuNzYtMjkuNzVhNiw2LDAsMCwwLTguNDgsOC40OGw0MCw0MGE2LDYsMCwwLDAsOC40OCwwbDQwLTQwQTYsNiwwLDAsMCwyMjguMjQsMTYzLjc2WiIvPjwvc3ZnPg==');}.icon-sort-descending{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik00MiwxMjhhNiw2LDAsMCwxLDYtNmg3MmE2LDYsMCwwLDEsMCwxMkg0OEE2LDYsMCwwLDEsNDIsMTI4Wm02LTU4aDU2YTYsNiwwLDAsMCwwLTEySDQ4YTYsNiwwLDAsMCwwLDEyWk0xODQsMTg2SDQ4YTYsNiwwLDAsMCwwLDEySDE4NGE2LDYsMCwwLDAsMC0xMlpNMjI4LjI0LDgzLjc2bC00MC00MGE2LDYsMCwwLDAtOC40OCwwbC00MCw0MGE2LDYsMCwwLDAsOC40OCw4LjQ4TDE3OCw2Mi40OVYxNDRhNiw2LDAsMCwwLDEyLDBWNjIuNDlsMjkuNzYsMjkuNzVhNiw2LDAsMCwwLDguNDgtOC40OFoiLz48L3N2Zz4=');}.icon-columns{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xMDQsMzRINjRBMTQsMTQsMCwwLDAsNTAsNDhWMjA4YTE0LDE0LDAsMCwwLDE0LDE0aDQwYTE0LDE0LDAsMCwwLDE0LTE0VjQ4QTE0LDE0LDAsMCwwLDEwNCwzNFptMiwxNzRhMiwyLDAsMCwxLTIsMkg2NGEyLDIsMCwwLDEtMi0yVjQ4YTIsMiwwLDAsMSwyLTJoNDBhMiwyLDAsMCwxLDIsMlpNMTkyLDM0SDE1MmExNCwxNCwwLDAsMC0xNCwxNFYyMDhhMTQsMTQsMCwwLDAsMTQsMTRoNDBhMTQsMTQsMCwwLDAsMTQtMTRWNDhBMTQsMTQsMCwwLDAsMTkyLDM0Wm0yLDE3NGEyLDIsMCwwLDEtMiwySDE1MmEyLDIsMCwwLDEtMi0yVjQ4YTIsMiwwLDAsMSwyLTJoNDBhMiwyLDAsMCwxLDIsMloiLz48L3N2Zz4=');}.icon-caret-double-down{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMTIuMjQsMTMxLjc2YTYsNiwwLDAsMSwwLDguNDhsLTgwLDgwYTYsNiwwLDAsMS04LjQ4LDBsLTgwLTgwYTYsNiwwLDAsMSw4LjQ4LTguNDhMMTI4LDIwNy41MWw3NS43Ni03NS43NUE2LDYsMCwwLDEsMjEyLjI0LDEzMS43NlptLTg4LjQ4LDguNDhhNiw2LDAsMCwwLDguNDgsMGw4MC04MGE2LDYsMCwwLDAtOC40OC04LjQ4TDEyOCwxMjcuNTEsNTIuMjQsNTEuNzZhNiw2LDAsMCwwLTguNDgsOC40OFoiLz48L3N2Zz4=');}.icon-caret-double-right{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xNDAuMjQsMTMyLjI0bC04MCw4MGE2LDYsMCwwLDEtOC40OC04LjQ4TDEyNy41MSwxMjgsNTEuNzYsNTIuMjRhNiw2LDAsMCwxLDguNDgtOC40OGw4MCw4MEE2LDYsMCwwLDEsMTQwLjI0LDEzMi4yNFptODAtOC40OC04MC04MGE2LDYsMCwwLDAtOC40OCw4LjQ4TDIwNy41MSwxMjhsLTc1Ljc1LDc1Ljc2YTYsNiwwLDEsMCw4LjQ4LDguNDhsODAtODBBNiw2LDAsMCwwLDIyMC4yNCwxMjMuNzZaIi8+PC9zdmc+');}.icon-door{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMzIsMjE4SDIwNlY0MGExNCwxNCwwLDAsMC0xNC0xNEg2NEExNCwxNCwwLDAsMCw1MCw0MFYyMThIMjRhNiw2LDAsMCwwLDAsMTJIMjMyYTYsNiwwLDAsMCwwLTEyWk02Miw0MGEyLDIsMCwwLDEsMi0ySDE5MmEyLDIsMCwwLDEsMiwyVjIxOEg2MlptMTA0LDkyYTEwLDEwLDAsMSwxLTEwLTEwQTEwLDEwLDAsMCwxLDE2NiwxMzJaIi8+PC9zdmc+');}.icon-book-bookmark{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMDgsMjZINzJBMzAsMzAsMCwwLDAsNDIsNTZWMjI0YTYsNiwwLDAsMCw2LDZIMTkyYTYsNiwwLDAsMCwwLTEySDU0di0yYTE4LDE4LDAsMCwxLDE4LTE4SDIwOGE2LDYsMCwwLDAsNi02VjMyQTYsNiwwLDAsMCwyMDgsMjZaTTExOCwzOGg1MnY3OEwxNDcuNTksOTkuMmE2LDYsMCwwLDAtNy4yLDBMMTE4LDExNlptODQsMTQ4SDcyYTI5Ljg3LDI5Ljg3LDAsMCwwLTE4LDZWNTZBMTgsMTgsMCwwLDEsNzIsMzhoMzR2OTBhNiw2LDAsMCwwLDkuNiw0LjhMMTQ0LDExMS41bDI4LjQxLDIxLjNBNiw2LDAsMCwwLDE4MiwxMjhWMzhoMjBaIi8+PC9zdmc+');}.icon-faders{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xMzQsMTIwdjk2YTYsNiwwLDAsMS0xMiwwVjEyMGE2LDYsMCwwLDEsMTIsMFptNjYsNzRhNiw2LDAsMCwwLTYsNnYxNmE2LDYsMCwwLDAsMTIsMFYyMDBBNiw2LDAsMCwwLDIwMCwxOTRabTI0LTMySDIwNlY0MGE2LDYsMCwwLDAtMTIsMFYxNjJIMTc2YTYsNiwwLDAsMCwwLDEyaDQ4YTYsNiwwLDAsMCwwLTEyWk01NiwxNjJhNiw2LDAsMCwwLTYsNnY0OGE2LDYsMCwwLDAsMTIsMFYxNjhBNiw2LDAsMCwwLDU2LDE2MlptMjQtMzJINjJWNDBhNiw2LDAsMCwwLTEyLDB2OTBIMzJhNiw2LDAsMCwwLDAsMTJIODBhNiw2LDAsMCwwLDAtMTJabTcyLTQ4SDEzNFY0MGE2LDYsMCwwLDAtMTIsMFY4MkgxMDRhNiw2LDAsMCwwLDAsMTJoNDhhNiw2LDAsMCwwLDAtMTJaIi8+PC9zdmc+');}.icon-robot{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMDAsNTBIMTM0VjE2YTYsNiwwLDAsMC0xMiwwVjUwSDU2QTMwLDMwLDAsMCwwLDI2LDgwVjE5MmEzMCwzMCwwLDAsMCwzMCwzMEgyMDBhMzAsMzAsMCwwLDAsMzAtMzBWODBBMzAsMzAsMCwwLDAsMjAwLDUwWm0xOCwxNDJhMTgsMTgsMCwwLDEtMTgsMThINTZhMTgsMTgsMCwwLDEtMTgtMThWODBBMTgsMTgsMCwwLDEsNTYsNjJIMjAwYTE4LDE4LDAsMCwxLDE4LDE4Wk03NCwxMDhhMTAsMTAsMCwxLDEsMTAsMTBBMTAsMTAsMCwwLDEsNzQsMTA4Wm04OCwwYTEwLDEwLDAsMSwxLDEwLDEwQTEwLDEwLDAsMCwxLDE2MiwxMDhabTIsMzBIOTJhMjYsMjYsMCwwLDAsMCw1Mmg3MmEyNiwyNiwwLDAsMCwwLTUyWm0tMjIsMTJ2MjhIMTE0VjE1MFpNNzgsMTY0YTE0LDE0LDAsMCwxLDE0LTE0aDEwdjI4SDkyQTE0LDE0LDAsMCwxLDc4LDE2NFptODYsMTRIMTU0VjE1MGgxMGExNCwxNCwwLDAsMSwwLDI4WiIvPjwvc3ZnPg==');}.icon-plugs-connected{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMzYuMjQsMTkuNzZhNiw2LDAsMCwwLTguNDgsMEwxNzMuOTQsNzMuNTdsLTYuNzktNi43OGEzMCwzMCwwLDAsMC00Mi40MiwwTDEwMCw5MS41MWwtNy43Ni03Ljc1YTYsNiwwLDAsMC04LjQ4LDguNDhMOTEuNTEsMTAwLDY2Ljc5LDEyNC43M2EzMCwzMCwwLDAsMCwwLDQyLjQybDYuNzgsNi43OUwxOS43NiwyMjcuNzZhNiw2LDAsMSwwLDguNDgsOC40OGw1My44Mi01My44MSw2Ljc5LDYuNzhhMzAsMzAsMCwwLDAsNDIuNDIsMEwxNTYsMTY0LjQ5bDcuNzYsNy43NWE2LDYsMCwwLDAsOC40OC04LjQ4TDE2NC40OSwxNTZsMjQuNzItMjQuNzNhMzAsMzAsMCwwLDAsMC00Mi40MmwtNi43OC02Ljc5LDUzLjgxLTUzLjgyQTYsNiwwLDAsMCwyMzYuMjQsMTkuNzZabS0xMTMuNDUsMTYxYTE4LDE4LDAsMCwxLTI1LjQ2LDBMNzUuMjcsMTU4LjY3YTE4LDE4LDAsMCwxLDAtMjUuNDZMMTAwLDEwOC40OSwxNDcuNTEsMTU2Wm01Ny45NC01Ny45NEwxNTYsMTQ3LjUxLDEwOC40OSwxMDBsMjQuNzItMjQuNzNhMTgsMTgsMCwwLDEsMjUuNDYsMGwyMi4wNiwyMi4wNmExOCwxOCwwLDAsMSwwLDI1LjQ2Wk05MC40MywzNC4yM2E2LDYsMCwwLDEsMTEuMTQtNC40Nmw4LDIwYTYsNiwwLDEsMS0xMS4xNCw0LjQ2Wm0tNjQsNTkuNTRhNiw2LDAsMCwxLDcuOC0zLjM0bDIwLDhhNiw2LDAsMSwxLTQuNDYsMTEuMTRsLTIwLThBNiw2LDAsMCwxLDI2LjQzLDkzLjc3Wm0yMDMuMTQsNjguNDZhNiw2LDAsMCwxLTcuOCwzLjM0bC0yMC04YTYsNiwwLDAsMSw0LjQ2LTExLjE0bDIwLDhBNiw2LDAsMCwxLDIyOS41NywxNjIuMjNabS02NCw1OS41NGE2LDYsMCwxLDEtMTEuMTQsNC40NmwtOC0yMGE2LDYsMCwwLDEsMTEuMTQtNC40NloiLz48L3N2Zz4=');}.icon-user-circle{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xMjgsMjZBMTAyLDEwMiwwLDEsMCwyMzAsMTI4LDEwMi4xMiwxMDIuMTIsMCwwLDAsMTI4LDI2Wk03MS40NCwxOThhNjYsNjYsMCwwLDEsMTEzLjEyLDAsODkuOCw4OS44LDAsMCwxLTExMy4xMiwwWk05NCwxMjBhMzQsMzQsMCwxLDEsMzQsMzRBMzQsMzQsMCwwLDEsOTQsMTIwWm05OS41MSw2OS42NGE3Ny41Myw3Ny41MywwLDAsMC00MC0zMS4zOCw0Niw0NiwwLDEsMC01MSwwLDc3LjUzLDc3LjUzLDAsMCwwLTQwLDMxLjM4LDkwLDkwLDAsMSwxLDEzMSwwWiIvPjwvc3ZnPg==');}.icon-password{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik00Niw1NlYyMDBhNiw2LDAsMCwxLTEyLDBWNTZhNiw2LDAsMCwxLDEyLDBabTk0LjU4LDU2LjQxTDExOCwxMTkuNzRWOTZhNiw2LDAsMCwwLTEyLDB2MjMuNzRsLTIyLjU4LTcuMzNhNiw2LDAsMSwwLTMuNzEsMTEuNDFsMjIuNTgsNy4zMy0xNCwxOS4yMWE2LDYsMCwxLDAsOS43LDcuMDZsMTQtMTkuMjEsMTQsMTkuMjFhNiw2LDAsMCwwLDkuNy03LjA2bC0xNC0xOS4yMSwyMi41OC03LjMzYTYsNiwwLDEsMC0zLjcxLTExLjQxWm0xMDMuNTYsMy44NWE2LDYsMCwwLDAtNy41Ni0zLjg1TDIxNCwxMTkuNzRWOTZhNiw2LDAsMCwwLTEyLDB2MjMuNzRsLTIyLjU4LTcuMzNhNiw2LDAsMSwwLTMuNzEsMTEuNDFsMjIuNTgsNy4zMy0xMy45NSwxOS4yMWE2LDYsMCwxLDAsOS43LDcuMDZsMTQtMTkuMjEsMTQsMTkuMjFhNiw2LDAsMCwwLDkuNy03LjA2bC0xMy45NS0xOS4yMSwyMi41OC03LjMzQTYsNiwwLDAsMCwyNDQuMTQsMTE2LjI2WiIvPjwvc3ZnPg==');}
assets/css/icons.css
@@ -1 +1 @@
.icon-google-logo{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjIsMTI4YTk0LDk0LDAsMSwxLTIxLjQ5LTU5LjgyLDYsNiwwLDEsMS05LjI1LDcuNjRBODIsODIsMCwxLDAsMjA5Ljc4LDEzNEgxMjhhNiw2LDAsMCwxLDAtMTJoODhBNiw2LDAsMCwxLDIyMiwxMjhaIi8+PC9zdmc+');}.icon-apple-logo{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMTkuNCwxNjcuODRDMjAxLjcxLDE1NS42OSwxOTgsMTM1LjEyLDE5OCwxMjBjMC0xOC40MiwxMy44Ni0zNC4yOSwyMi4xMi00Mi4xMmE2LDYsMCwwLDAsMC04LjcxQzIwOCw1Ny43LDE4Ny4wNyw1MCwxNjgsNTBhNzAuMjMsNzAuMjMsMCwwLDAtNDAsMTIuNTUsNjkuNiw2OS42LDAsMCwwLTg5LjMxLDguMDhBNzIuNjMsNzIuNjMsMCwwLDAsMTgsMTIzLjM1YTEyNS4xMSwxMjUuMTEsMCwwLDAsMzkuNTMsODguMzNBMzcuODUsMzcuODUsMCwwLDAsODMuNiwyMjJoODcuN0EzNy44MywzNy44MywwLDAsMCwxOTksMjEwLjA3YTEyMi42LDEyMi42LDAsMCwwLDE3LjU0LTI0LjJjNi41NS0xMiw1Ljc3LTEzLjc1LDUtMTUuNDhBNi4wNyw2LjA3LDAsMCwwLDIxOS40LDE2Ny44NFptLTI5LjIzLDM0QTI1LjgyLDI1LjgyLDAsMCwxLDE3MS4zLDIxMEg4My42QTI1Ljg1LDI1Ljg1LDAsMCwxLDY1Ljc4LDIwMywxMTMuMjEsMTEzLjIxLDAsMCwxLDMwLDEyM2E2MC41NSw2MC41NSwwLDAsMSwxNy4yMS00NEE1Ni44Miw1Ni44MiwwLDAsMSw4OCw2MmguODFhNTcuMzUsNTcuMzUsMCwwLDEsMzUuNDQsMTIuNzEsNiw2LDAsMCwwLDcuNSwwQTU3LjM5LDU3LjM5LDAsMCwxLDE2OCw2MmMxMy44OSwwLDI4LjgxLDQuNjgsMzkuMTEsMTItOS40NCwxMC4xNC0yMS4xLDI2LjU5LTIxLjEsNDYsMCwyMy43OCw3LjgxLDQyLjYsMjIuNjYsNTQuNzdBMTA3LjMzLDEwNy4zMywwLDAsMSwxOTAuMTcsMjAxLjg5Wm0tNjAtMTcxLjM5QTM4LDM4LDAsMCwxLDE2NywyaDFhNiw2LDAsMCwxLDAsMTJoLTFhMjYsMjYsMCwwLDAtMjUuMTgsMTkuNSw2LDYsMCwxLDEtMTEuNjItM1oiLz48L3N2Zz4=');}.icon-check-circle{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xNzIuMjQsOTkuNzZhNiw2LDAsMCwxLDAsOC40OGwtNTYsNTZhNiw2LDAsMCwxLTguNDgsMGwtMjQtMjRhNiw2LDAsMCwxLDguNDgtOC40OEwxMTIsMTUxLjUxbDUxLjc2LTUxLjc1QTYsNiwwLDAsMSwxNzIuMjQsOTkuNzZaTTIzMCwxMjhBMTAyLDEwMiwwLDEsMSwxMjgsMjYsMTAyLjEyLDEwMi4xMiwwLDAsMSwyMzAsMTI4Wm0tMTIsMGE5MCw5MCwwLDEsMC05MCw5MEE5MC4xLDkwLjEsMCwwLDAsMjE4LDEyOFoiLz48L3N2Zz4=');}.icon-cloud-slash{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik01Mi40NCwzNkE2LDYsMCwwLDAsNDMuNTYsNDRsNDAuMTgsNDQuMmMtLjQ1Ljg3LS45LDEuNzUtMS4zMiwyLjY0QTYyLDYyLDAsMSwwLDcyLDIxNGg4OGE4NS4yMyw4NS4yMywwLDAsMCwzMi4zNS02LjNMMjAzLjU2LDIyMGE2LDYsMCwwLDAsOC44OC04LjA4Wk0xNjAsMjAySDcyYTUwLDUwLDAsMSwxLDUuOS05OS42NEE4Ni4yNSw4Ni4yNSwwLDAsMCw3NCwxMjhhNiw2LDAsMCwwLDEyLDAsNzMuOTIsNzMuOTIsMCwwLDEsNi40NC0zMC4ybDkxLjIyLDEwMC4zNEE3My42NSw3My42NSwwLDAsMSwxNjAsMjAyWm04Ni03NGE4NS44NSw4NS44NSwwLDAsMS0yMS44NSw1Ny4yNyw2LDYsMCwwLDEtNC40NywyLDYsNiwwLDAsMS00LjQ3LTEwLDc0LDc0LDAsMCwwLTk5LTEwOC45Miw2LDYsMCwxLDEtNy4xMS05LjY3QTg2LDg2LDAsMCwxLDI0NiwxMjhaIi8+PC9zdmc+');}.icon-exclamation-mark{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xNDIsMjAwYTE0LDE0LDAsMSwxLTE0LTE0QTE0LDE0LDAsMCwxLDE0MiwyMDBabS0xNC00MmE2LDYsMCwwLDAsNi02VjQ4YTYsNiwwLDAsMC0xMiwwVjE1MkE2LDYsMCwwLDAsMTI4LDE1OFoiLz48L3N2Zz4=');}.icon-cloud-arrow-down{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yNDYsMTI4YTg1LjI3LDg1LjI3LDAsMCwxLTE3LjIsNTEuNiw2LDYsMCwxLDEtOS42LTcuMkE3NCw3NCwwLDEsMCw4NiwxMjhhNiw2LDAsMCwxLTEyLDAsODUuNTQsODUuNTQsMCwwLDEsMy45MS0yNS42NEE1MC42OCw1MC42OCwwLDAsMCw3MiwxMDJhNTAsNTAsMCwwLDAsMCwxMDBIOTZhNiw2LDAsMCwxLDAsMTJINzJBNjIsNjIsMCwxLDEsODIuNDMsOTAuODgsODYsODYsMCwwLDEsMjQ2LDEyOFptLTY2LjI0LDQzLjc2TDE1OCwxOTMuNTFWMTI4YTYsNiwwLDAsMC0xMiwwdjY1LjUxbC0yMS43Ni0yMS43NWE2LDYsMCwwLDAtOC40OCw4LjQ4bDMyLDMyYTYsNiwwLDAsMCw4LjQ4LDBsMzItMzJhNiw2LDAsMCwwLTguNDgtOC40OFoiLz48L3N2Zz4=');}.icon-caret-down{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMTIuMjQsMTAwLjI0bC04MCw4MGE2LDYsMCwwLDEtOC40OCwwbC04MC04MGE2LDYsMCwwLDEsOC40OC04LjQ4TDEyOCwxNjcuNTFsNzUuNzYtNzUuNzVhNiw2LDAsMCwxLDguNDgsOC40OFoiLz48L3N2Zz4=');}.icon-cloud-arrow-up{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xODguMjQsMTY0LjI0YTYsNiwwLDAsMS04LjQ4LDBMMTU4LDE0Mi40OVYyMDhhNiw2LDAsMCwxLTEyLDBWMTQyLjQ5bC0yMS43NiwyMS43NWE2LDYsMCwwLDEtOC40OC04LjQ4bDMyLTMyYTYsNiwwLDAsMSw4LjQ4LDBsMzIsMzJBNiw2LDAsMCwxLDE4OC4yNCwxNjQuMjRaTTE2MCw0MkE4Ni4xLDg2LjEsMCwwLDAsODIuNDMsOTAuODgsNjIsNjIsMCwxLDAsNzIsMjE0aDQwYTYsNiwwLDAsMCwwLTEySDcyYTUwLDUwLDAsMCwxLDAtMTAwLDUwLjY4LDUwLjY4LDAsMCwxLDUuOTEuMzZBODUuNTQsODUuNTQsMCwwLDAsNzQsMTI4YTYsNiwwLDAsMCwxMiwwLDc0LDc0LDAsMSwxLDEwMy42LDY3Ljg1LDYsNiwwLDAsMCw0LjgsMTFBODYsODYsMCwwLDAsMTYwLDQyWiIvPjwvc3ZnPg==');}.icon-cloud-check{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xNjAsNDJBODYuMTEsODYuMTEsMCwwLDAsODIuNDMsOTAuODgsNjIsNjIsMCwxLDAsNzIsMjE0aDg4YTg2LDg2LDAsMCwwLDAtMTcyWm0wLDE2MEg3MmE1MCw1MCwwLDAsMSwwLTEwMCw1MC42Nyw1MC42NywwLDAsMSw1LjkxLjM1QTg1LjYxLDg1LjYxLDAsMCwwLDc0LDEyOGE2LDYsMCwwLDAsMTIsMCw3NCw3NCwwLDEsMSw3NCw3NFptMzYuMjQtOTQuMjRhNiw2LDAsMCwxLDAsOC40OGwtNDgsNDhhNiw2LDAsMCwxLTguNDgsMGwtMjQtMjRhNiw2LDAsMCwxLDguNDgtOC40OEwxNDQsMTUxLjUxbDQzLjc2LTQzLjc1QTYsNiwwLDAsMSwxOTYuMjQsMTA3Ljc2WiIvPjwvc3ZnPg==');}.icon-cloud-warning{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xNjAsNDJBODYuMTEsODYuMTEsMCwwLDAsODIuNDMsOTAuODgsNjIsNjIsMCwxLDAsNzIsMjE0aDg4YTg2LDg2LDAsMCwwLDAtMTcyWm0wLDE2MEg3MmE1MCw1MCwwLDAsMSwwLTEwMCw1MC42Nyw1MC42NywwLDAsMSw1LjkxLjM1QTg1LjYxLDg1LjYxLDAsMCwwLDc0LDEyOGE2LDYsMCwwLDAsMTIsMCw3NCw3NCwwLDEsMSw3NCw3NFptLTYtNzRWODhhNiw2LDAsMCwxLDEyLDB2NDBhNiw2LDAsMCwxLTEyLDBabTE2LDM2YTEwLDEwLDAsMSwxLTEwLTEwQTEwLDEwLDAsMCwxLDE3MCwxNjRaIi8+PC9zdmc+');}.icon-syncing{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbDpzcGFjZT0icHJlc2VydmUiIHdpZHRoPSIzMiIgaGVpZ2h0PSIzMiIgZmlsbD0iY3VycmVudENvbG9yIiB2aWV3Qm94PSIwIDAgMjU2IDI1NiI+PHBhdGggaWQ9InJlZnJlc2giIGQ9Ik0xNjAuMDQ3IDEyMi44NzVhMzAuNzg0IDMwLjc4NCAwIDAgMC0yMS43NSA4Ljc5N2MtMi44NDIgMy4wMDMtLjQ2NyA0Ljk3MSAxLjMxMiAzLjE1NiAxMS4wNDMtMTAuNzg2IDI4LjcxLTEwLjY4IDM5LjYyNS4yMzRsNy4yMDMgNy4yMDRoLTEyLjg3NWMtMy4zNDcuMDA4LTMuMTY1IDMuODc1IDAgMy44NzVoMTYuMTFjMi4wNjIgMCAyLjU0LTEuNDE4IDIuNTYyLTQuOTdsLjA5NC0xNC45MjFjLjAyLTMuMjktMy40MzctMy4xNjUtMy40MzcgMHYxMi44NmwtNy4yMDMtNy4xODhhMzAuNzY4IDMwLjc2OCAwIDAgMC0yMS42NDEtOS4wNDd6bS0yOS41OTQgMzkuNzk3Yy0yLjA2MiAwLTIuNTI0IDEuNDAyLTIuNTQ3IDQuOTUzbC0uMDk0IDE0LjkyMmMtLjAyIDMuMjkgMy40MjIgMy4xNjQgMy40MjIgMHYtMTIuODZsNy4yMDMgNy4yMDRjMTEuOTU2IDExLjk1NSAzMS4zMTIgMTIuMDY0IDQzLjQwNy4yNSAyLjg0Mi0zLjAwMy40NTEtNC45ODgtMS4zMjgtMy4xNzItMTEuMDQzIDEwLjc4Ni0yOC43MSAxMC42OC0zOS42MjUtLjIzNWwtNy4xODgtNy4yMDNoMTIuODZjMy4zNDctLjAwOCAzLjE2NS0zLjg2IDAtMy44NmgtMTYuMTF6Ii8+PHBhdGggZD0iTTE2MCA0NGE4NC4xMSA4NC4xMSAwIDAgMC03Ni40MSA0OS4xMkE2MC43MSA2MC43MSAwIDAgMCA3MiA5MmE2MCA2MCAwIDAgMCAwIDEyMGg4OGE4NCA4NCAwIDAgMCAwLTE2OFptMCAxNjBINzJhNTIgNTIgMCAxIDEgOC41NS0xMDMuM0E4My42NiA4My42NiAwIDAgMCA3NiAxMjhhNCA0IDAgMCAwIDggMCA3NiA3NiAwIDEgMSA3NiA3NloiLz48L3N2Zz4=');}.icon-cloud-x{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xNjAsNDJBODYuMTEsODYuMTEsMCwwLDAsODIuNDMsOTAuODgsNjIsNjIsMCwxLDAsNzIsMjE0aDg4YTg2LDg2LDAsMCwwLDAtMTcyWm0wLDE2MEg3MmE1MCw1MCwwLDAsMSwwLTEwMCw1MC42Nyw1MC42NywwLDAsMSw1LjkxLjM1QTg1LjYxLDg1LjYxLDAsMCwwLDc0LDEyOGE2LDYsMCwwLDAsMTIsMCw3NCw3NCwwLDEsMSw3NCw3NFptMjguMjQtODUuNzZMMTY4LjQ4LDEzNmwxOS43NiwxOS43NmE2LDYsMCwxLDEtOC40OCw4LjQ4TDE2MCwxNDQuNDhsLTE5Ljc2LDE5Ljc2YTYsNiwwLDAsMS04LjQ4LTguNDhMMTUxLjUyLDEzNmwtMTkuNzYtMTkuNzZhNiw2LDAsMCwxLDguNDgtOC40OEwxNjAsMTI3LjUybDE5Ljc2LTE5Ljc2YTYsNiwwLDAsMSw4LjQ4LDguNDhaIi8+PC9zdmc+');}.icon-arrows-clockwise{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjIsNDhWOTZhNiw2LDAsMCwxLTYsNkgxNjhhNiw2LDAsMCwxLDAtMTJoMzMuNTJMMTgzLjQ3LDcyYTgxLjUxLDgxLjUxLDAsMCwwLTU3LjUzLTI0aC0uNDZBODEuNSw4MS41LDAsMCwwLDY4LjE5LDcxLjI4YTYsNiwwLDEsMS04LjM4LTguNTgsOTMuMzgsOTMuMzgsMCwwLDEsNjUuNjctMjYuNzZIMTI2YTkzLjQ1LDkzLjQ1LDAsMCwxLDY2LDI3LjUzbDE4LDE4VjQ4YTYsNiwwLDAsMSwxMiwwWk0xODcuODEsMTg0LjcyYTgxLjUsODEuNSwwLDAsMS01Ny4yOSwyMy4zNGgtLjQ2YTgxLjUxLDgxLjUxLDAsMCwxLTU3LjUzLTI0TDU0LjQ4LDE2Nkg4OGE2LDYsMCwwLDAsMC0xMkg0MGE2LDYsMCwwLDAtNiw2djQ4YTYsNiwwLDAsMCwxMiwwVjE3NC40OGwxOCwxOC4wNWE5My40NSw5My40NSwwLDAsMCw2NiwyNy41M2guNTJhOTMuMzgsOTMuMzgsMCwwLDAsNjUuNjctMjYuNzYsNiw2LDAsMSwwLTguMzgtOC41OFoiLz48L3N2Zz4=');}.icon-share-fat{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMzYuMjQsMTA3Ljc2bC04MC04MEE2LDYsMCwwLDAsMTQ2LDMyVjc0LjJjLTU0LjQ4LDMuNTktMTIwLjM5LDU1LTEyNy45MywxMjAuNjZhMTAsMTAsMCwwLDAsMTcuMjMsOGgwQzQ2LjU2LDE5MC44NSw4NywxNTIuNiwxNDYsMTUwLjEzVjE5MmE2LDYsMCwwLDAsMTAuMjQsNC4yNGw4MC04MEE2LDYsMCwwLDAsMjM2LjI0LDEwNy43NlpNMTU4LDE3Ny41MlYxNDRhNiw2LDAsMCwwLTYtNmMtMjcuNzMsMC01NC43Niw3LjI1LTgwLjMyLDIxLjU1YTE5My4zOCwxOTMuMzgsMCwwLDAtNDAuODEsMzAuNjVjNC43LTI2LjU2LDIwLjE2LTUyLDQ0LTcyLjI3Qzk4LjQ3LDk3Ljk0LDEyNy4yOSw4NiwxNTIsODZhNiw2LDAsMCwwLDYtNlY0Ni40OUwyMjMuNTEsMTEyWiIvPjwvc3ZnPg==');}.icon-trash{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMTYsNTBIMTc0VjQwYTIyLDIyLDAsMCwwLTIyLTIySDEwNEEyMiwyMiwwLDAsMCw4Miw0MFY1MEg0MGE2LDYsMCwwLDAsMCwxMkg1MFYyMDhhMTQsMTQsMCwwLDAsMTQsMTRIMTkyYTE0LDE0LDAsMCwwLDE0LTE0VjYyaDEwYTYsNiwwLDAsMCwwLTEyWk05NCw0MGExMCwxMCwwLDAsMSwxMC0xMGg0OGExMCwxMCwwLDAsMSwxMCwxMFY1MEg5NFpNMTk0LDIwOGEyLDIsMCwwLDEtMiwySDY0YTIsMiwwLDAsMS0yLTJWNjJIMTk0Wk0xMTAsMTA0djY0YTYsNiwwLDAsMS0xMiwwVjEwNGE2LDYsMCwwLDEsMTIsMFptNDgsMHY2NGE2LDYsMCwwLDEtMTIsMFYxMDRhNiw2LDAsMCwxLDEyLDBaIi8+PC9zdmc+');}.icon-star{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMzcuMjgsOTcuODdBMTQuMTgsMTQuMTgsMCwwLDAsMjI0Ljc2LDg4bC02MC4yNS00Ljg3LTIzLjIyLTU2LjJhMTQuMzcsMTQuMzcsMCwwLDAtMjYuNTgsMEw5MS40OSw4My4xMSwzMS4yNCw4OGExNC4xOCwxNC4xOCwwLDAsMC0xMi41Miw5Ljg5QTE0LjQzLDE0LjQzLDAsMCwwLDIzLDExMy4zMkw2OSwxNTIuOTNsLTE0LDU5LjI1YTE0LjQsMTQuNCwwLDAsMCw1LjU5LDE1LDE0LjEsMTQuMSwwLDAsMCwxNS45MS42TDEyOCwxOTYuMTJsNTEuNTgsMzEuNzFhMTQuMSwxNC4xLDAsMCwwLDE1LjkxLS42LDE0LjQsMTQuNCwwLDAsMCw1LjU5LTE1bC0xNC01OS4yNUwyMzMsMTEzLjMyQTE0LjQzLDE0LjQzLDAsMCwwLDIzNy4yOCw5Ny44N1ptLTEyLjE0LDYuMzctNDguNjksNDJhNiw2LDAsMCwwLTEuOTIsNS45MmwxNC44OCw2Mi43OWEyLjM1LDIuMzUsMCwwLDEtLjk1LDIuNTcsMi4yNCwyLjI0LDAsMCwxLTIuNi4xTDEzMS4xNCwxODRhNiw2LDAsMCwwLTYuMjgsMEw3MC4xNCwyMTcuNjFhMi4yNCwyLjI0LDAsMCwxLTIuNi0uMSwyLjM1LDIuMzUsMCwwLDEtMS0yLjU3bDE0Ljg4LTYyLjc5YTYsNiwwLDAsMC0xLjkyLTUuOTJsLTQ4LjY5LTQyYTIuMzcsMi4zNywwLDAsMS0uNzMtMi42NSwyLjI4LDIuMjgsMCwwLDEsMi4wNy0xLjY1bDYzLjkyLTUuMTZhNiw2LDAsMCwwLDUuMDYtMy42OWwyNC42My01OS42YTIuMzUsMi4zNSwwLDAsMSw0LjM4LDBsMjQuNjMsNTkuNmE2LDYsMCwwLDAsNS4wNiwzLjY5bDYzLjkyLDUuMTZhMi4yOCwyLjI4LDAsMCwxLDIuMDcsMS42NUEyLjM3LDIuMzcsMCwwLDEsMjI1LjE0LDEwNC4yNFoiLz48L3N2Zz4=');}.icon-alphabetical{--icon:url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIGZpbGw9ImN1cnJlbnRDb2xvciIgdmVyc2lvbj0iMS4xIiB2aWV3Qm94PSIwIDAgMTgzLjc4IDE4NC4wNiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJtNTkuNTg2IDY5Ljc0MmMtMC44NTEzIDAtMS40NjEgMC4xOTY1Ni0xLjgzNjYgMC41OTcxOC0wLjM1MDU0IDAuMzc1NTgtMC41Mjk1OCAxLjAyMjktMC41Mjk1OCAxLjk0OTNzMC4xNzkwMyAxLjU5MzcgMC41Mjk1OCAxLjk5NDRjMC4zNzU1OCAwLjM3NTU4IDAuOTg1MjkgMC41NjMzOCAxLjgzNjYgMC41NjMzOGg3LjAxOTdsLTEyLjQyOCAzNC4zNjZoLTIuMTA3Yy0wLjg1MTMgMC0xLjQ2MSAwLjE5NjU2LTEuODM2NiAwLjU5NzE4LTAuMzUwNTQgMC4zNzU1OC0wLjUyOTU3IDEuMDM0MS0wLjUyOTU3IDEuOTYwNiAwIDAuOTI2NDQgMC4xNzkwMyAxLjU4MjUgMC41Mjk1NyAxLjk4MyAwLjM3NTU4IDAuMzc1NTkgMC45ODUyOSAwLjU2MzM4IDEuODM2NiAwLjU2MzM4aDEyLjU1MmMwLjg1MTMgMCAxLjQ1MjItMC4xODc3OSAxLjgwMjgtMC41NjMzOCAwLjM3NTU4LTAuNDAwNjIgMC41NjMzNy0xLjA1NjYgMC41NjMzNy0xLjk4MyAwLTAuOTI2NDUtMC4xODc3OS0xLjU4NS0wLjU2MzM3LTEuOTYwNi0wLjM1MDU0LTAuNDAwNjItMC45NTE0Ny0wLjU5NzE4LTEuODAyOC0wLjU5NzE4aC00LjU1MjFsMy4xMjExLTguOTM0OWgxOC4yMmwzLjA3NiA4LjkzNDloLTUuMDcwNGMtMC44NTEzIDAtMS40NjEgMC4xOTY1Ni0xLjgzNjYgMC41OTcxOC0wLjM1MDU0IDAuMzc1NTgtMC41Mjk1OCAxLjAzNDEtMC41Mjk1OCAxLjk2MDYgMCAwLjkyNjQ0IDAuMTc5MDMgMS41ODI1IDAuNTI5NTggMS45ODMgMC4zNzU1OCAwLjM3NTU5IDAuOTg1MjkgMC41NjMzOCAxLjgzNjYgMC41NjMzOGgxMy4yOTZjMC44NTEzIDAgMS40NTIyLTAuMTg3NzkgMS44MDI4LTAuNTYzMzggMC4zNzU1OC0wLjQwMDYyIDAuNTYzMzctMS4wNTY2IDAuNTYzMzctMS45ODMgMC0wLjkyNjQ1LTAuMTg3NzktMS41ODUtMC41NjMzNy0xLjk2MDYtMC4zNTA1NC0wLjQwMDYyLTAuOTUxNDctMC41OTcxOC0xLjgwMjgtMC41OTcxOGgtMi4yODczbC0xMy4yNjItMzcuMDM2Yy0wLjMwMDQ3LTAuODUxMy0wLjc1OTk0LTEuNDYxLTEuMzg1OS0xLjgzNjYtMC42MDA5My0wLjQwMDYyLTEuNDA5Ny0wLjU5NzE4LTIuNDExMy0wLjU5NzE4em00NC4xNDYgMGMtMC44NTEzIDAtMS40NzIzIDAuMTk2NTYtMS44NDc4IDAuNTk3MTgtMC4zNTA1NSAwLjM3NTU4LTAuNTE4MyAxLjAyMjktMC41MTgzIDEuOTQ5M3YxMS45MWMwIDAuODc2MzMgMC4yMDUzMiAxLjUwNjEgMC42MzA5OCAxLjg4MTcgMC40MjU2NiAwLjM3NTU4IDEuMTU5MyAwLjU2MzM3IDIuMTg1OSAwLjU2MzM3czEuNzQ5LTAuMTg3NzkgMi4xNzQ3LTAuNTYzMzdjMC40MjU2OS0wLjM3NTU4IDAuNjQyMjYtMS4wMDUzIDAuNjQyMjYtMS44ODE3di05LjM1MTdoMTguODUxbC0yNC43NTQgMzUuMzAxYy0wLjM1MDU0IDAuNTI1ODItMC41MTgzMSAxLjA3MTctMC41MTgzMSAxLjYyMjYgMCAwLjkyNjQ1IDAuMTY3NzcgMS41ODI1IDAuNTE4MzEgMS45ODMxIDAuMzc1NTggMC4zNzU1OCAwLjk5NjU0IDAuNTYzMzggMS44NDc4IDAuNTYzMzhoMjguNzY2YzAuODUxMyAwIDEuNDUyMi0wLjE4NzggMS44MDI4LTAuNTYzMzggMC4zNzU1OC0wLjQwMDYyIDAuNTYzMzgtMS4wNTY2IDAuNTYzMzgtMS45ODMxdi0xMi42NjVjMC0wLjg3NjMzLTAuMjE2NTgtMS40OTQ4LTAuNjQyMjUtMS44NzA0LTAuNDI1NjYtMC4zNzU1OC0xLjE0OC0wLjU2MzM4LTIuMTc0Ny0wLjU2MzM4LTEuMDI2NiAwLTEuNzQ5IDAuMTg3NzktMi4xNzQ3IDAuNTYzMzgtMC40MjU2NiAwLjM3NTU4LTAuNjQyMjQgMC45OTQwMi0wLjY0MjI0IDEuODcwNHYxMC4xMDdoLTE5Ljk3OGwyNC45MDEtMzUuNDU5YzAuMjUwMzktMC4zNTA1NCAwLjM3MTgzLTAuODM4ODMgMC4zNzE4My0xLjQ2NDggMC0wLjkyNjQ1LTAuMTg3OC0xLjU3MzctMC41NjMzOC0xLjk0OTMtMC4zNTA1NS0wLjQwMDYyLTAuOTUxNDctMC41OTcxOC0xLjgwMjgtMC41OTcxOHptLTMxLjc1MiA1LjEwNDJoMC43MDk4NWw2Ljk4NTkgMjAuMzE1aC0xNC43MTZ6bS0zNy43MjMtNDkuMTgzYy00LjczNDIgMC04LjYzMTMgMy44OTctOC42MzEzIDguNjMxM3YxMTUuNDdjMCA0LjczNDIgMy44OTcgOC42MzEzIDguNjMxMyA4LjYzMTNoMTE1LjI2YzQuNzM0MiAwIDguNjQyMS0zLjg5NyA4LjY0MjEtOC42MzEzdi0xMTUuNDdjMC00LjczNDItMy45MDgyLTguNjMxMy04LjY0MjEtOC42MzEzem0wIDUuNzI0aDExNS4yNmMxLjY1OCAwIDIuOTA3IDEuMjQ5MSAyLjkwNyAyLjkwNzF2MTE1LjQ3YzAgMS42NTgtMS4yNDkxIDIuOTA3LTIuOTA3IDIuOTA3aC0xMTUuMjZjLTEuNjU4IDAtMi44OTU4LTEuMjQ5MS0yLjg5NTgtMi45MDd2LTExNS40N2MwLTEuNjU4IDEuMjM3OC0yLjkwNzEgMi44OTU4LTIuOTA3MXoiIGZpbGw9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIuNzIxMTQiLz48L3N2Zz4=');}.icon-scribble{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMDQuMjUsMTg4LjI0YTE2LjYzLDE2LjYzLDAsMCwwLDAsMjMuNTIsNiw2LDAsMSwxLTguNDgsOC40OCwyOC42MSwyOC42MSwwLDAsMSwwLTQwLjQ4bDkuMzctOS4zOGExNi42MywxNi42MywwLDAsMC0yMy41Mi0yMy41MWwtNjYuNzUsNjYuNzVhMjguNjMsMjguNjMsMCwwLDEtNDAuNDktNDAuNDlsOTguNzYtOTguNzVhMTYuNjMsMTYuNjMsMCwwLDAtMjMuNTItMjMuNTFMODIuODYsMTE3LjYyQTI4LjYzLDI4LjYzLDAsMCwxLDQyLjM3LDc3LjEzTDgzLjc1LDM1Ljc2YTYsNiwwLDEsMSw4LjQ5LDguNDhMNTAuODYsODUuNjJhMTYuNjMsMTYuNjMsMCwwLDAsMjMuNTIsMjMuNTFsNjYuNzUtNjYuNzVhMjguNjMsMjguNjMsMCwwLDEsNDAuNDksNDAuNDlMODIuODYsMTgxLjYyYTE2LjYzLDE2LjYzLDAsMCwwLDIzLjUyLDIzLjUxbDY2Ljc2LTY2Ljc1YTI4LjYzLDI4LjYzLDAsMCwxLDQwLjQ5LDQwLjQ5WiIvPjwvc3ZnPg==');}.icon-brackets-angle{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik04NS4wNiw0My4yMiwzMS4xMSwxMjhsNTQsODQuNzhhNiw2LDAsMCwxLTEuODQsOC4yOCw2LDYsMCwwLDEtOC4yOC0xLjg0bC01Ni04OGE2LDYsMCwwLDEsMC02LjQ0bDU2LTg4YTYsNiwwLDAsMSwxMC4xMiw2LjQ0Wm0xNTIsODEuNTYtNTYtODhhNiw2LDAsMSwwLTEwLjEyLDYuNDRMMjI0Ljg5LDEyOGwtNTMuOTUsODQuNzhhNiw2LDAsMCwwLDEuODQsOC4yOCw2LDYsMCwwLDAsOC4yOC0xLjg0bDU2LTg4QTYsNiwwLDAsMCwyMzcuMDYsMTI0Ljc4WiIvPjwvc3ZnPg==');}.icon-brain{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yNDYsMTI0YTU0LjEzLDU0LjEzLDAsMCwwLTMyLTQ5LjMzVjcyYTQ2LDQ2LDAsMCwwLTg2LTIyLjY3QTQ2LDQ2LDAsMCwwLDQyLDcydjIuNjdhNTQsNTQsMCwwLDAsMCw5OC42M1YxNzZhNDYsNDYsMCwwLDAsODYsMjIuNjdBNDYsNDYsMCwwLDAsMjE0LDE3NnYtMi43QTU0LjA3LDU0LjA3LDAsMCwwLDI0NiwxMjRaTTg4LDIxMGEzNCwzNCwwLDAsMS0zNC0zMi45NEE1My42Nyw1My42NywwLDAsMCw2NCwxNzhoOGE2LDYsMCwwLDAsMC0xMkg2NEE0Miw0MiwwLDAsMSw1MCw4NC4zOWE2LDYsMCwwLDAsNC01LjY2VjcyYTM0LDM0LDAsMCwxLDY4LDB2NzMuMDVBNDUuODksNDUuODksMCwwLDAsODgsMTMwYTYsNiwwLDAsMCwwLDEyLDM0LDM0LDAsMCwxLDAsNjhabTEwNC00NGgtOGE2LDYsMCwwLDAsMCwxMmg4YTUzLjY3LDUzLjY3LDAsMCwwLDEwLS45NEEzNCwzNCwwLDEsMSwxNjgsMTQyYTYsNiwwLDAsMCwwLTEyLDQ1Ljg5LDQ1Ljg5LDAsMCwwLTM0LDE1LjA1VjcyYTM0LDM0LDAsMCwxLDY4LDB2Ni43M2E2LDYsMCwwLDAsNCw1LjY2QTQyLDQyLDAsMCwxLDE5MiwxNjZabTE0LTU0YTYsNiwwLDAsMS02LDZoLTRhMzQsMzQsMCwwLDEtMzQtMzRWODBhNiw2LDAsMCwxLDEyLDB2NGEyMiwyMiwwLDAsMCwyMiwyMmg0QTYsNiwwLDAsMSwyMDYsMTEyWk02MCwxMThINTZhNiw2LDAsMCwxLDAtMTJoNEEyMiwyMiwwLDAsMCw4Miw4NFY4MGE2LDYsMCwwLDEsMTIsMHY0QTM0LDM0LDAsMCwxLDYwLDExOFoiLz48L3N2Zz4=');}.icon-palette{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xOTkuMzcsNTUuMzFBMTAxLjMyLDEwMS4zMiwwLDAsMCwxMjgsMjZoLTFBMTAyLDEwMiwwLDAsMCwyNiwxMjhjMCw0Mi4wOSwyNi4wNyw3Ny40NCw2OCw5Mi4yNkEzMC4yMSwzMC4yMSwwLDAsMCwxMDQuMTEsMjIyLDMwLjA2LDMwLjA2LDAsMCwwLDEzNCwxOTJhMTgsMTgsMCwwLDEsMTgtMThoNDYuMjFhMjkuODIsMjkuODIsMCwwLDAsMjkuMjUtMjMuMzFBMTAyLjcxLDEwMi43MSwwLDAsMCwyMzAsMTI3LjExLDEwMS4yNSwxMDEuMjUsMCwwLDAsMTk5LjM3LDU1LjMxWk0yMTUuNzYsMTQ4YTE3Ljg5LDE3Ljg5LDAsMCwxLTE3LjU1LDE0SDE1MmEzMCwzMCwwLDAsMC0zMCwzMCwxOCwxOCwwLDAsMS0yNCwxN0M2MSwxOTUuODYsMzgsMTY0Ljg1LDM4LDEyOGE5MCw5MCwwLDAsMSw4OS4wNy05MEgxMjhhOTAuMzQsOTAuMzQsMCwwLDEsOTAsODkuMjJBOTAuNDYsOTAuNDYsMCwwLDEsMjE1Ljc2LDE0OFpNMTM4LDc2YTEwLDEwLDAsMSwxLTEwLTEwQTEwLDEwLDAsMCwxLDEzOCw3NlpNOTQsMTAwQTEwLDEwLDAsMSwxLDg0LDkwLDEwLDEwLDAsMCwxLDk0LDEwMFptMCw1NmExMCwxMCwwLDEsMS0xMC0xMEExMCwxMCwwLDAsMSw5NCwxNTZabTg4LTU2YTEwLDEwLDAsMSwxLTEwLTEwQTEwLDEwLDAsMCwxLDE4MiwxMDBaIi8+PC9zdmc+');}.icon-pen-nib{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yNDYsOTIuNjhhMTMuOTQsMTMuOTQsMCwwLDAtNC4xLTkuOUwxNzMuMjEsMTQuMWExNCwxNCwwLDAsMC0xOS44LDBMMTI0LjY4LDQyLjgzLDY2LjIyLDY0Ljc2YTE0LDE0LDAsMCwwLTguOSwxMC44TDM0LjA4LDIxNUE2LDYsMCwwLDAsNDAsMjIyYTYuNjEsNi42MSwwLDAsMCwxLS4wOGwxMzkuNDQtMjMuMjRhMTQsMTQsMCwwLDAsMTAuODEtOC45bDIxLjkyLTU4LjQ2LDI4Ljc0LTI4Ljc0QTEzLjkyLDEzLjkyLDAsMCwwLDI0Niw5Mi42OFptLTY2LDkyLjg5YTIsMiwwLDAsMS0xLjU0LDEuMjdMNTcuNDksMjA3bDUyLjg3LTUyLjg4YTI2LDI2LDAsMSwwLTguNDgtOC40OEw0OSwxOTguNTNsMjAuMTctMTIxQTIsMiwwLDAsMSw3MC40Myw3Nmw1Ni4wNi0yMUwyMDEsMTI5LjUxWk0xMTAsMTMyYTE0LDE0LDAsMSwxLDE0LDE0QTE0LDE0LDAsMCwxLDExMCwxMzJaTTIzMy40MSw5NC4xLDIwOCwxMTkuNTEsMTM2LjQ4LDQ4LDE2MS45LDIyLjU4YTIsMiwwLDAsMSwyLjgzLDBsNjguNjgsNjguNjlhMiwyLDAsMCwxLDAsMi44M1oiLz48L3N2Zz4=');}.icon-question{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xMzgsMTgwYTEwLDEwLDAsMSwxLTEwLTEwQTEwLDEwLDAsMCwxLDEzOCwxODBaTTEyOCw3NGMtMjEsMC0zOCwxNS4yNS0zOCwzNHY0YTYsNiwwLDAsMCwxMiwwdi00YzAtMTIuMTMsMTEuNjYtMjIsMjYtMjJzMjYsOS44NywyNiwyMi0xMS42NiwyMi0yNiwyMmE2LDYsMCwwLDAtNiw2djhhNiw2LDAsMCwwLDEyLDB2LTIuNDJjMTguMTEtMi41OCwzMi0xNi42NiwzMi0zMy41OEMxNjYsODkuMjUsMTQ5LDc0LDEyOCw3NFptMTAyLDU0QTEwMiwxMDIsMCwxLDEsMTI4LDI2LDEwMi4xMiwxMDIuMTIsMCwwLDEsMjMwLDEyOFptLTEyLDBhOTAsOTAsMCwxLDAtOTAsOTBBOTAuMSw5MC4xLDAsMCwwLDIxOCwxMjhaIi8+PC9zdmc+');}.icon-city{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yNDAsMjEwSDIzMFY4OGE2LDYsMCwwLDAtNi02SDE2MGE2LDYsMCwwLDAtNiw2djQySDEwMlY0MGE2LDYsMCwwLDAtNi02SDMyYTYsNiwwLDAsMC02LDZWMjEwSDE2YTYsNiwwLDAsMCwwLDEySDI0MGE2LDYsMCwwLDAsMC0xMlpNMTY2LDk0aDUyVjIxMEgxNjZabS0xMiw0OHY2OEgxMDJWMTQyWk0zOCw0Nkg5MFYyMTBIMzhaTTcwLDcyVjg4YTYsNiwwLDAsMS0xMiwwVjcyYTYsNiwwLDAsMSwxMiwwWm0wLDQ4djE2YTYsNiwwLDAsMS0xMiwwVjEyMGE2LDYsMCwwLDEsMTIsMFptMCw0OHYxNmE2LDYsMCwwLDEtMTIsMFYxNjhhNiw2LDAsMCwxLDEyLDBabTUyLDE2VjE2OGE2LDYsMCwwLDEsMTIsMHYxNmE2LDYsMCwwLDEtMTIsMFptNjQsMFYxNjhhNiw2LDAsMCwxLDEyLDB2MTZhNiw2LDAsMCwxLTEyLDBabTAtNDhWMTIwYTYsNiwwLDAsMSwxMiwwdjE2YTYsNiwwLDAsMS0xMiwwWiIvPjwvc3ZnPg==');}.icon-folder{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMTYsNzRIMTMwLjQ5bC0yNy45LTI3LjlhMTMuOTQsMTMuOTQsMCwwLDAtOS45LTQuMUg0MEExNCwxNCwwLDAsMCwyNiw1NlYyMDAuNjJBMTMuMzksMTMuMzksMCwwLDAsMzkuMzgsMjE0SDIxNi44OUExMy4xMiwxMy4xMiwwLDAsMCwyMzAsMjAwLjg5Vjg4QTE0LDE0LDAsMCwwLDIxNiw3NFpNNDAsNTRIOTIuNjlhMiwyLDAsMCwxLDEuNDEuNTlMMTEzLjUxLDc0SDM4VjU2QTIsMiwwLDAsMSw0MCw1NFpNMjE4LDIwMC44OWExLjExLDEuMTEsMCwwLDEtMS4xMSwxLjExSDM5LjM4QTEuNCwxLjQsMCwwLDEsMzgsMjAwLjYyVjg2SDIxNmEyLDIsMCwwLDEsMiwyWiIvPjwvc3ZnPg==');}.icon-hash{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjQsOTBIMTczbDguODktNDguOTNhNiw2LDAsMSwwLTExLjgtMi4xNEwxNjAuODEsOTBIMTA5bDguODktNDguOTNhNiw2LDAsMCwwLTExLjgtMi4xNEw5Ni44MSw5MEg0OGE2LDYsMCwwLDAsMCwxMkg5NC42M2wtOS40Niw1MkgzMmE2LDYsMCwwLDAsMCwxMkg4M0w3NC4xLDIxNC45M2E2LDYsMCwwLDAsNC44Myw3QTUuNjQsNS42NCwwLDAsMCw4MCwyMjJhNiw2LDAsMCwwLDUuODktNC45M0w5NS4xOSwxNjZIMTQ3bC04Ljg5LDQ4LjkzYTYsNiwwLDAsMCw0LjgzLDcsNS42NCw1LjY0LDAsMCwwLDEuMDguMSw2LDYsMCwwLDAsNS44OS00LjkzTDE1OS4xOSwxNjZIMjA4YTYsNiwwLDAsMCwwLTEySDE2MS4zN2w5LjQ2LTUySDIyNGE2LDYsMCwwLDAsMC0xMlptLTc0LjgzLDY0SDk3LjM3bDkuNDYtNTJoNTEuOFoiLz48L3N2Zz4=');}.icon-shapes{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik02OS42OSw2Mi4xYTYsNiwwLDAsMC0xMS4zOCwwbC00MCwxMjBBNiw2LDAsMCwwLDI0LDE5MGg4MGE2LDYsMCwwLDAsNS42OS03LjlaTTMyLjMyLDE3OCw2NCw4M2wzMS42OCw5NVpNMjA2LDc2YTUwLDUwLDAsMSwwLTUwLDUwQTUwLjA2LDUwLjA2LDAsMCwwLDIwNiw3NlptLTg4LDBhMzgsMzgsMCwxLDEsMzgsMzhBMzgsMzgsMCwwLDEsMTE4LDc2Wm0xMDYsNzBIMTM2YTYsNiwwLDAsMC02LDZ2NTZhNiw2LDAsMCwwLDYsNmg4OGE2LDYsMCwwLDAsNi02VjE1MkE2LDYsMCwwLDAsMjI0LDE0NlptLTYsNTZIMTQyVjE1OGg3NloiLz48L3N2Zz4=');}.icon-diamonds-four{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xMjMuNzYsMTA4LjI0YTYsNiwwLDAsMCw4LjQ4LDBsNDAtNDBhNiw2LDAsMCwwLDAtOC40OGwtNDAtNDBhNiw2LDAsMCwwLTguNDgsMGwtNDAsNDBhNiw2LDAsMCwwLDAsOC40OFpNMTI4LDMyLjQ5LDE1OS41MSw2NCwxMjgsOTUuNTEsOTYuNDksNjRabTQuMjQsMTE1LjI3YTYsNiwwLDAsMC04LjQ4LDBsLTQwLDQwYTYsNiwwLDAsMCwwLDguNDhsNDAsNDBhNiw2LDAsMCwwLDguNDgsMGw0MC00MGE2LDYsMCwwLDAsMC04LjQ4Wk0xMjgsMjIzLjUxLDk2LjQ5LDE5MiwxMjgsMTYwLjQ5LDE1OS41MSwxOTJabTEwOC4yNC05OS43NS00MC00MGE2LDYsMCwwLDAtOC40OCwwbC00MCw0MGE2LDYsMCwwLDAsMCw4LjQ4bDQwLDQwYTYsNiwwLDAsMCw4LjQ4LDBsNDAtNDBBNiw2LDAsMCwwLDIzNi4yNCwxMjMuNzZaTTE5MiwxNTkuNTEsMTYwLjQ5LDEyOCwxOTIsOTYuNDksMjIzLjUxLDEyOFptLTgzLjc2LTM1Ljc1LTQwLTQwYTYsNiwwLDAsMC04LjQ4LDBsLTQwLDQwYTYsNiwwLDAsMCwwLDguNDhsNDAsNDBhNiw2LDAsMCwwLDguNDgsMGw0MC00MEE2LDYsMCwwLDAsMTA4LjI0LDEyMy43NlpNNjQsMTU5LjUxLDMyLjQ5LDEyOCw2NCw5Ni40OSw5NS41MSwxMjhaIi8+PC9zdmc+');}.icon-crosshair-simple{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xMjgsMjZBMTAyLDEwMiwwLDEsMCwyMzAsMTI4LDEwMi4xMiwxMDIuMTIsMCwwLDAsMTI4LDI2Wm02LDE5MS44VjE4NGE2LDYsMCwwLDAtMTIsMHYzMy44QTkwLjE1LDkwLjE1LDAsMCwxLDM4LjIsMTM0SDcyYTYsNiwwLDAsMCwwLTEySDM4LjJBOTAuMTUsOTAuMTUsMCwwLDEsMTIyLDM4LjJWNzJhNiw2LDAsMCwwLDEyLDBWMzguMkE5MC4xNSw5MC4xNSwwLDAsMSwyMTcuOCwxMjJIMTg0YTYsNiwwLDAsMCwwLDEyaDMzLjhBOTAuMTUsOTAuMTUsMCwwLDEsMTM0LDIxNy44WiIvPjwvc3ZnPg==');}.icon-circle-notch{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMzAsMTI4YTEwMiwxMDIsMCwwLDEtMjA0LDBjMC00MC4xOCwyMy4zNS03Ni44Niw1OS41LTkzLjQ1YTYsNiwwLDAsMSw1LDEwLjlDNTguNjEsNjAuMDksMzgsOTIuNDksMzgsMTI4YTkwLDkwLDAsMCwwLDE4MCwwYzAtMzUuNTEtMjAuNjEtNjcuOTEtNTIuNS04Mi41NWE2LDYsMCwwLDEsNS0xMC45QzIwNi42NSw1MS4xNCwyMzAsODcuODIsMjMwLDEyOFoiLz48L3N2Zz4=');}.icon-cards-three{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMDgsOTBINDhhMTQsMTQsMCwwLDAtMTQsMTR2OTZhMTQsMTQsMCwwLDAsMTQsMTRIMjA4YTE0LDE0LDAsMCwwLDE0LTE0VjEwNEExNCwxNCwwLDAsMCwyMDgsOTBabTIsMTEwYTIsMiwwLDAsMS0yLDJINDhhMiwyLDAsMCwxLTItMlYxMDRhMiwyLDAsMCwxLDItMkgyMDhhMiwyLDAsMCwxLDIsMlpNNTAsNjRhNiw2LDAsMCwxLDYtNkgyMDBhNiw2LDAsMCwxLDAsMTJINTZBNiw2LDAsMCwxLDUwLDY0Wk02NiwzMmE2LDYsMCwwLDEsNi02SDE4NGE2LDYsMCwwLDEsMCwxMkg3MkE2LDYsMCwwLDEsNjYsMzJaIi8+PC9zdmc+');}.icon-logo{--icon:url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+PHN2ZyAgICB3aWR0aD0iMzIiICAgIGhlaWdodD0iMzIiICAgIHZpZXdCb3g9IjAgMCAzMiAzMiIgICAgdmVyc2lvbj0iMS4xIiAgICB4bWw6c3BhY2U9InByZXNlcnZlIiAgICBzdHlsZT0iY2xpcC1ydWxlOmV2ZW5vZGQ7ZmlsbC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlLWxpbmVjYXA6c3F1YXJlO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoxLjUiICAgIGlkPSJzdmcxNCIgICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiAgICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZGVmcyAgICBpZD0iZGVmczE0IiAvPjxwYXRoICAgIGQ9Ik0gMTYuNTgwMDc4LDIuMTMyODEyNSBDIDguODY0ODQ0OSwyLjEzMjgxMjUgMS40NDA2MDIxLDguMTQ2NjIxOCAyLjAzMzIwMzEsMTUuODM5ODQ0IDIuNTk0NDU4OCwyMy4xMjY2NjYgOC45NzEyMDEyLDI5LjAyNTE1NSAxNS45NzA3MDMsMjkuNzg3MTA5IDI0LjIyMTIyNCwzMC42ODUyNCAzMC40NDA5MTEsMjMuODM0Mjc3IDI5Ljk3NDYwOSwxNS43OTg4MjggMjkuNTI3ODEzLDguMDk5ODY1NSAyNC4yOTE1NiwyLjEzMjgxMjUgMTYuNTgwMDc4LDIuMTMyODEyNSBaIG0gMCwwLjYwNzQyMTkgYyAwLjAxMDQ2LDAgMC4wMjA4MywwIDAuMDMxMjUsMCBWIDI5LjIzMjQyMiBjIC0wLjE5MDMyMywtMC4wMTIxOCAtMC4zODE1MjEsLTAuMDI3ODMgLTAuNTc0MjE5LC0wLjA0ODgzIEMgOS4zMTMwNDUzLDI4LjQ1MTYxNSAzLjE3Nzg3NzUsMjIuNzkzMDQ0IDIuNjM4NjcxOSwxNS43OTI5NjkgMi4wNzI0NTYsOC40NDIwMTUzIDkuMjA4MTAwOCwyLjc0MDIzNDQgMTYuNTgwMDc4LDIuNzQwMjM0NCBaIE0gMTYuMDkxNzk3LDMuODg0NzY1NiAxNiwzLjg4ODY3MTkgQyAxMi43MjU0NTQsNC4wMTgzNDg5IDkuNTUyMzM3OSw1LjM2NDY4MzggNy4yNTU4NTk0LDcuNSA0Ljk1OTM4MDksOS42MzUzMTYyIDMuNTQwMjcwMywxMi41NjQ5NzIgMy43OTI5Njg4LDE1Ljg0NTcwMyA0LjI4NDc3MzksMjIuMjMwMDQ1IDkuODY0NDgxMiwyNy4zODM2MDYgMTUuOTk0MTQxLDI4LjA1MjczNCBsIDAuMDg5ODQsMC4wMDk4IDAuMDIxNDgsLTAuMTgxNjQxIC0wLjA5MTgsLTAuMDA5OCBDIDkuOTcyNjc0OSwyNy4yMTE2NDQgNC40NTg4NjkxLDIyLjExNjQ2OCAzLjk3NDYwOTQsMTUuODMwMDc4IDMuNzI2NTU1OSwxMi42MDk2NTEgNS4xMTU4MDg0LDkuNzM5MDQzNyA3LjM3ODkwNjIsNy42MzQ3NjU2IDkuNjQyMDA0MSw1LjUzMDQ4NzUgMTIuNzc4NTM5LDQuMTk4MTk2OCAxNi4wMDc4MTIsNC4wNzAzMTI1IGwgMC4wOTE4LC0wLjAwMzkxIHogbSAwLDEuNTAxOTUzMSBMIDE2LDUuMzkwNjI1IEMgMTMuMTMxOTQ5LDUuNTA0MjA0NyAxMC4zNTMyOTgsNi42ODQzNDE1IDguMzQxNzk2OSw4LjU1NDY4NzUgNi4zMzAyOTYyLDEwLjQyNTAzMyA1LjA4NzE5MjksMTIuOTkwODE5IDUuMzA4NTkzNywxNS44NjUyMzQgNS43MzkzOTQsMjEuNDU3NjY5IDEwLjYyNTE2MSwyNS45NzA1NDcgMTUuOTk0MTQxLDI2LjU1NjY0MSBsIDAuMDg5ODQsMC4wMDk4IDAuMDIxNDgsLTAuMTc5Njg3IC0wLjA5MTgsLTAuMDA5OCBDIDEwLjczMzM1NCwyNS44MDA1MzMgNS45MTM0ODkyLDIxLjM0NjA0NiA1LjQ5MDIzNDQsMTUuODUxNTYzIDUuMjczNDc4NCwxMy4wMzc0NTEgNi40ODY3MjM3LDEwLjUyNjgwOCA4LjQ2NDg0MzgsOC42ODc1IDEwLjQ0Mjk2NCw2Ljg0ODE5MjIgMTMuMTg1MDMyLDUuNjg0MDUyNiAxNi4wMDc4MTIsNS41NzIyNjU2IGwgMC4wOTE4LC0wLjAwMzkxIHogbSAwLDEuNTAzOTA2MyBMIDE2LDYuODkyNTc4MSBjIC0yLjQ2MTU3NywwLjA5NzQ4MyAtNC44NDU3MjgsMS4xMTE0MTc0IC02LjU3MjI2NTYsMi43MTY3OTY5IC0xLjcyNjUzOCwxLjYwNTM4IC0yLjc5NTU3NDEsMy44MDcyODIgLTIuNjA1NDY4OCw2LjI3NTM5MSAwLjM2OTc5ODYsNC44MDA1NCA0LjU2MzUzMzQsOC42NzQ2NzQgOS4xNzE4NzU0LDkuMTc3NzM0IGwgMC4wODk4NCwwLjAwOTggMC4wMjE0OCwtMC4xODE2NDEgLTAuMDkxOCwtMC4wMDk4IEMgMTEuNDk0MDM3LDI0LjM4NzQ4MSA3LjM2NjE1NTcsMjAuNTczNjM0IDcuMDAzOTA2MiwxNS44NzEwOTQgNi44MTg0NDgxLDEzLjQ2MzMyIDcuODU3NjQwNSwxMS4zMTY1MTMgOS41NTA3ODEzLDkuNzQyMTg3NSAxMS4yNDM5MjIsOC4xNjc4NjE4IDEzLjU5MTUyOSw3LjE3MTg2MDggMTYuMDA3ODEyLDcuMDc2MTcxOSBsIDAuMDkxOCwtMC4wMDM5MSB6IG0gMCwxLjUwMTk1MzEgTCAxNiw4LjM5NjQ4NDQgYyAtMi4wNTUwNzMsMC4wODEzODQgLTQuMDQ0Nzc1LDAuOTI1MjMzNCAtNS40ODYzMjgsMi4yNjU2MjQ2IC0xLjQ0MTU1MzUsMS4zNDAzOTMgLTIuMzM0NTg4MSwzLjE4MjM3OSAtMi4xNzU3ODE0LDUuMjQ0MTQxIDAuMzA4NzkyLDQuMDA4NTc5IDMuODA4NjA1NCw3LjI0MDEzNiA3LjY1NjI1MDQsNy42NjAxNTYgbCAwLjA4OTg0LDAuMDA5OCAwLjAwNzgsLTAuMDY4MzYgdiAtMC4wMDIgbCAwLjAwMiwtMC4wMDk4IGMgOS40OWUtNCwtMC4wMDM0IDAuMDAzNSwtMC4wMDYyIDAuMDAzOSwtMC4wMDk4IDYuNDJlLTQsLTAuMDA2NyA4LjAyZS00LC0wLjAxMzAxIDAsLTAuMDE5NTMgbCAwLjAwNzgsLTAuMDcyMjcgLTAuMDkxOCwtMC4wMDk4IEMgMTIuMjU0NjY3LDIyLjk3NDQyMiA4LjgyMDc3OTYsMTkuODAzMjMxIDguNTE5NTMxMywxNS44OTI1NzggOC4zNjUzNjgyLDEzLjg5MTEwNSA5LjIyODUzNzMsMTIuMTA2MjM3IDEwLjYzNjcxOSwxMC43OTY4NzUgMTIuMDQ0OSw5LjQ4NzUxMyAxMy45OTc5OTksOC42NTc3MTcgMTYuMDA3ODEyLDguNTc4MTI1IGwgMC4wOTE4LC0wLjAwMzkxIHogbSAwLDEuNTAxOTUzMiBMIDE2LDkuODk4NDM3NSBjIC0xLjY0ODU4OCwwLjA2NTI4NyAtMy4yNDU3NjEsMC43NDI5Mzg1IC00LjQwMjM0NCwxLjgxODM1OTUgLTEuMTU2NTgyLDEuMDc1NDIxIC0xLjg3MTY1MDYsMi41NTM1MzggLTEuNzQ0MTQwNCw0LjIwODk4NCAwLjI0Nzc4ODQsMy4yMTY2NjkgMy4wNTM2NDE0LDUuODA5NTAxIDYuMTQwNjI1NCw2LjE0NjQ4NSBsIDAuMDg5ODQsMC4wMDk4IDAuMDIxNDgsLTAuMTgxNjQgLTAuMDkxOCwtMC4wMDk4IGMgLTIuOTk4MzQ0LC0wLjMyNzMwOCAtNS43MzgyNzMsLTIuODU5Nzk4IC01Ljk3ODUxNiwtNS45Nzg1MTYgLTAuMTIyODY0NSwtMS41OTUxNDIgMC41NjQyOTgsLTMuMDE4MTE3IDEuNjg3NSwtNC4wNjI1IDEuMTIzMjAyLC0xLjA0NDM4MiAyLjY4MTgzOSwtMS43MDYwMzcgNC4yODUxNTYsLTEuNzY5NTMxIGwgMC4wOTE4LC0wLjAwMzkgeiBtIDAsMS41MDM5MDY3IC0wLjA5MTgsMC4wMDIgYyAtMS4yNDIwOTUsMC4wNDkxOSAtMi40NDQ4LDAuNTYwNjUyIC0zLjMxNjQwNiwxLjM3MTA5MyAtMC44NzE2MDYsMC44MTA0NDIgLTEuNDEyNjE5LDEuOTI0NzEzIC0xLjMxNjQwNiwzLjE3MzgyOSAwLjE4Njc4MywyLjQyNDczMiAyLjMwMDY0Myw0LjM3NjkxMyA0LjYyNjk1Myw0LjYzMDg1OSBsIDAuMDg5ODQsMC4wMDk4IDAuMDIxNDgsLTAuMTgxNjQgLTAuMDkxOCwtMC4wMDk4IGMgLTIuMjM3NjkyLC0wLjI0NDI3MiAtNC4yODU2MDQsLTIuMTM2MDgzIC00LjQ2NDg0NCwtNC40NjI4OSAtMC4wOTE1NywtMS4xODg4MjYgMC40MjE1MzIsLTIuMjQ3OTMzIDEuMjU5NzY2LC0zLjAyNzM0NCAwLjgzODIzNCwtMC43Nzk0MTEgMi4wMDIzODMsLTEuMjcyOTE2IDMuMTk5MjE4LC0xLjMyMDMxMyBsIDAuMDkxOCwtMC4wMDM5IHogbSAwLDEuNTAxOTUzIC0wLjA5MTgsMC4wMDM5IGMgLTAuODM1NjEzLDAuMDMzMDkgLTEuNjQzODMxLDAuMzc0NDUyIC0yLjIzMDQ2OSwwLjkxOTkyMiAtMC41ODY2MzcsMC41NDU0NyAtMC45NTE2MzYsMS4yOTk3NzggLTAuODg2NzE4LDIuMTQyNTc4IDAuMTI1NzgsMS42MzI4MjEgMS41NDU2NzMsMi45NDIzNyAzLjExMTMyOCwzLjExMzI4MSAtMC4wMDI2LC0yLjhlLTQgLTAuMDA1MywyLjg2ZS00IC0wLjAwNzgsMCAwLjAwMyw2LjAzZS00IDAuMDA2NiwwLjAwMTcgMC4wMDk4LDAuMDAyIDAuMDAzMSwzLjA4ZS00IDAuMDA2NywxLjJlLTUgMC4wMDk4LDAgbCAwLjA3ODEzLDAuMDA5OCAwLjAyMTQ4LC0wLjE4MTY0MSAtMC4wOTE4LC0wLjAwOTggYyAtMS40NzcwMTUsLTAuMTYxMjM1IC0yLjgzMDk4NCwtMS40MTIzOTYgLTIuOTQ5MjE5LC0yLjk0NzI2NiAtMC4wNjAyNywtMC43ODI0OTYgMC4yNzY4MjIsLTEuNDc5NzA5IDAuODMwMDc4LC0xLjk5NDE0MSAwLjU1MzI1NywtMC41MTQ0MzEgMS4zMjI5MzksLTAuODQxNzQ4IDIuMTEzMjgxLC0wLjg3MzA0NiBsIDAuMDkxOCwtMC4wMDM5IHogbSAwLDEuNTAxOTUzIEwgMTYsMTQuNDA2MjUgYyAtMC40MjkxMTcsMC4wMTY5OSAtMC44NDI4NzMsMC4xOTIxNjYgLTEuMTQ0NTMxLDAuNDcyNjU2IC0wLjMwMTY1OSwwLjI4MDQ5MSAtMC40OTA2NDgsMC42NzA5NTUgLTAuNDU3MDMxLDEuMTA3NDIyIDAuMDY0NzcsMC44NDA4ODYgMC43OTA3MjksMS41MTE3MzIgMS41OTU3MDMsMS41OTk2MDkgbCAwLjA4OTg0LDAuMDA5OCAwLjAyMTQ4LC0wLjE4MTY0MSAtMC4wOTE4LC0wLjAwOTggYyAtMC43MTYzNTcsLTAuMDc4MiAtMS4zNzYzNjIsLTAuNjg4NjgxIC0xLjQzMzU5NCwtMS40MzE2NDEgLTAuMDI4OTcsLTAuMzc2MTc5IDAuMTMyMTA1LC0wLjcxMTQ3NyAwLjQwMDM5MSwtMC45NjA5MzcgMC4yNjgyODYsLTAuMjQ5NDYxIDAuNjQzNDg1LC0wLjQwODYyNyAxLjAyNzM0MywtMC40MjM4MjggbCAwLjA5MTgsLTAuMDAzOSB6IiAgICBzdHlsZT0iYmFzZWxpbmUtc2hpZnQ6YmFzZWxpbmU7Y2xpcC1ydWxlOm5vbnplcm87ZGlzcGxheTppbmxpbmU7b3ZlcmZsb3c6dmlzaWJsZTt2ZWN0b3ItZWZmZWN0Om5vbmU7ZmlsbDojMjIyMjIyO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO2VuYWJsZS1iYWNrZ3JvdW5kOmFjY3VtdWxhdGU7c3RvcC1jb2xvcjojMDAwMDAwO3N0b3Atb3BhY2l0eToxIiAgICBpZD0icGF0aDI3IiAvPjwvc3ZnPg==');}.icon-jakevan{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbDpzcGFjZT0icHJlc2VydmUiIHN0eWxlPSJjbGlwLXJ1bGU6ZXZlbm9kZDtmaWxsLXJ1bGU6ZXZlbm9kZDtzdHJva2UtbGluZWpvaW46cm91bmQ7c3Ryb2tlLW1pdGVybGltaXQ6MiIgdmlld0JveD0iMCAwIDMyIDMyIj48cGF0aCBkPSJNMTcuODggMTQuNjhIMTIuOWwtLjQzLTEuNjNIOS41OGwtLjQ1IDEuNjNINi41bDIuODktOC43NGgzLjJsMi44OSA4LjY0di04LjZoMi40djMuNzhjLjEtLjIuMjItLjM4LjM1LS41Ny4xMy0uMi4yNi0uMzcuMzktLjU0bDEuODYtMi42N2g3Ljh2MS44OUgyNS40djEuMzdoMi42NXYxLjg4SDI1LjR2MS42NWgyLjg2djEuOTFoLTcuOTNsLTEuNzUtMy4zMi0uNy40MXptNS4xMy04LjU5LTIuNyAzLjc5IDIuNyA0Ljc0em0tMTEuMDUgNS4wMy0uMzgtMS40M2ExMzYuODYgMTM2Ljg2IDAgMCAwLS40LTEuNTVMMTEgNy4zOGExNy43NiAxNy43NiAwIDAgMS0uMzYgMS42bC0uMTguNzEtLjM5IDEuNDN6bS04LjU4IDYuM2E1Ljc0IDUuNzQgMCAwIDEtMS4yNC0uMTN2LTEuODNsLjQxLjA4Yy4xNS4wMy4zLjA1LjQ3LjA1LjMgMCAuNTEtLjA2LjY3LS4xN2EuOTIuOTIgMCAwIDAgLjM0LS41MmMuMDYtLjIzLjEtLjUyLjEtLjg2VjUuOThoMi40djcuODVjMCAuODgtLjEzIDEuNTctLjQgMi4xLS4yNi41Mi0uNjMuOS0xLjEgMS4xNC0uNDguMjMtMS4wMy4zNS0xLjY1LjM1WiIgc3R5bGU9ImZpbGw6Y3VycmVudENvbG9yO3N0cm9rZS13aWR0aDouMDE4NDM5MiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMS40IC42Nikgc2NhbGUoLjk2MDUwMTM0KSIvPjxwYXRoIGQ9Ik0yMi44MiAyMi4yN2gtNC4wNmwtLjM3LTEuNEgxNS45bC0uMzkgMS40aC0yLjI2bDIuNDktNy41M2gyLjc1bDIuNDkgNy40NHYtNy40MWgyLjdsMi43NyA1LjIxaC4wM2E0MS4xIDQxLjEgMCAwIDEtLjA3LTEuODJ2LTMuMzloMS44M3Y3LjVoLTIuN2wtMi43OS01LjI4aC0uMDRhMTIuODMgMTIuODMgMCAwIDEgLjA4IDEuMjZsLjAyLjY0em0tNC44Ni0zLjA3LS4zMy0xLjIzYTg5LjA3IDg5LjA3IDAgMCAwLS4zNS0xLjM0bC0uMTQtLjY1YTE1LjA0IDE1LjA0IDAgMCAxLS4zMSAxLjM3bC0uMTYuNjItLjMzIDEuMjN6bS0zLjg1LTQuNDMtMi41IDcuNUg5LjJsLTIuNS03LjVoMi4zMmwxLjA0IDMuOGExNS4wMyAxNS4wMyAwIDAgMSAuMzYgMS43NiA3LjYxIDcuNjEgMCAwIDEgLjItMS4ybC4xNC0uNTQgMS4wNi0zLjgyeiIgc3R5bGU9ImZpbGw6Y3VycmVudENvbG9yO3N0cm9rZS13aWR0aDouMDE1OTg4NCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMS40IC42Nikgc2NhbGUoLjk2MDUwMTM0KSIvPjxwYXRoIGQ9Ik0xMS45IDI0LjIxYzAgLjQtLjA3LjcyLS4yLjk5LS4xNS4yNi0uMzYuNDYtLjYzLjYtLjI4LjEzLS42Mi4yLTEuMDMuMkg5LjJ2LTMuNWguOTdjLjM4IDAgLjcuMDYuOTYuMTkuMjUuMTMuNDUuMzIuNTguNTguMTQuMjUuMi41Ny4yLjk0em0tLjI2LjAxYzAtLjMzLS4wNS0uNjEtLjE2LS44M2ExLjEgMS4xIDAgMCAwLS41MS0uNTEgMS45NSAxLjk1IDAgMCAwLS44Ny0uMTdoLS42NnYzLjA3aC42Yy41MyAwIC45My0uMTMgMS4yLS4zOS4yNy0uMjYuNC0uNjUuNC0xLjE3ek0xNC4yNyAyNmgtMS45NXYtMy41aDEuOTV2LjIyaC0xLjd2MS4zMmgxLjZ2LjIzaC0xLjZ2MS41aDEuN3ptMS4yOC0zLjVjLjI4IDAgLjUyLjAyLjcuMDhhLjguOCAwIDAgMSAuNDQuM2MuMS4xNC4xNC4zMy4xNC41N2EuOS45IDAgMCAxLS4xLjQ1Ljg3Ljg3IDAgMCAxLS4yNy4zMmMtLjEyLjA4LS4yNS4xNC0uNC4xOGwuOTggMS42aC0uM2wtLjkyLTEuNTNoLS44OVYyNmgtLjI1di0zLjV6bS0uMDMuMjFoLS41OXYxLjU1aC43MWMuMyAwIC41Mi0uMDcuNjktLjIxLjE2LS4xNC4yNC0uMzQuMjQtLjYgMC0uMjgtLjA5LS40Ny0uMjYtLjU4LS4xNy0uMS0uNDMtLjE2LS43OS0uMTZ6bTUuNTctLjIyTDIwLjEyIDI2aC0uMjVsLS43Ni0yLjY1LS4wNS0uMTYtLjA0LS4xNGExOC44IDE4LjggMCAwIDEtLjA2LS4yNCAyMC42IDIwLjYgMCAwIDEtLjExLjQ4TDE4LjA5IDI2aC0uMjVsLS45Ni0zLjVoLjI2bC42NyAyLjQ3YTI3LjM2IDI3LjM2IDAgMCAxIC4wOS4zNWwuMDQuMTcuMDMuMTUuMDMtLjE2YTQuODMgNC44MyAwIDAgMSAuMTQtLjUzbC43LTIuNDZoLjI1bC43MyAyLjQ4YTExLjk4IDExLjk4IDAgMCAxIC4xMy41M2wuMDQuMTVhMTEuMDIgMTEuMDIgMCAwIDEgLjE1LS42OGwuNjktMi40OHpNMjMuMjYgMjZoLTEuOTV2LTMuNWgxLjk1di4yMmgtMS43djEuMzJoMS42di4yM2gtMS42djEuNWgxLjd6bTEuMjgtMy41Yy4yOCAwIC41Mi4wMi43MS4wOGEuOC44IDAgMCAxIC40My4zYy4xLjE0LjE0LjMzLjE0LjU3YS45LjkgMCAwIDEtLjEuNDUuODcuODcgMCAwIDEtLjI3LjMyYy0uMTEuMDgtLjI1LjE0LS40LjE4bC45OCAxLjZoLS4zbC0uOTItMS41M2gtLjg4VjI2aC0uMjZ2LTMuNXptLS4wMi4yMWgtLjZ2MS41NWguNzJjLjI5IDAgLjUxLS4wNy42OC0uMjEuMTYtLjE0LjI0LS4zNC4yNC0uNiAwLS4yOC0uMDgtLjQ3LS4yNi0uNTgtLjE3LS4xLS40My0uMTYtLjc4LS4xNnpNMjYuNSAyNmgtLjI1di0zLjVoMS45NXYuMjJoLTEuN3YxLjQ5aDEuNnYuMjJoLTEuNnoiIHN0eWxlPSJmaWxsOmN1cnJlbnRDb2xvcjtzdHJva2Utd2lkdGg6LjAxMDEwNjgiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDEuNCAuNjYpIHNjYWxlKC45NjA1MDEzNCkiLz48L3N2Zz4=');}.icon-user-square{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMDgsMzRINDhBMTQsMTQsMCwwLDAsMzQsNDhWMjA4YTE0LDE0LDAsMCwwLDE0LDE0SDIwOGExNCwxNCwwLDAsMCwxNC0xNFY0OEExNCwxNCwwLDAsMCwyMDgsMzRaTTk0LDEyMGEzNCwzNCwwLDEsMSwzNCwzNEEzNCwzNCwwLDAsMSw5NCwxMjBaTTY1Ljc3LDIxMGE2Ni40Myw2Ni40MywwLDAsMSwyMC43Ny0yOS4zNiw2Niw2NiwwLDAsMSw4Mi45MiwwQTY2LjQzLDY2LjQzLDAsMCwxLDE5MC4yMywyMTBaTTIxMCwyMDhhMiwyLDAsMCwxLTIsMmgtNS4xN2E3Ny44NSw3Ny44NSwwLDAsMC00OS4zOC01MS43MSw0Niw0NiwwLDEsMC01MC45LDBBNzcuODUsNzcuODUsMCwwLDAsNTMuMTcsMjEwSDQ4YTIsMiwwLDAsMS0yLTJWNDhhMiwyLDAsMCwxLDItMkgyMDhhMiwyLDAsMCwxLDIsMloiLz48L3N2Zz4=');}.icon-chat-teardrop{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xMzIsMjZhOTguMTEsOTguMTEsMCwwLDAtOTgsOTh2ODRhMTQsMTQsMCwwLDAsMTQsMTRoODRhOTgsOTgsMCwwLDAsMC0xOTZabTAsMTg0SDQ4YTIsMiwwLDAsMS0yLTJWMTI0YTg2LDg2LDAsMSwxLDg2LDg2WiIvPjwvc3ZnPg==');}.icon-caret-left{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xNjQuMjQsMjAzLjc2YTYsNiwwLDEsMS04LjQ4LDguNDhsLTgwLTgwYTYsNiwwLDAsMSwwLTguNDhsODAtODBhNiw2LDAsMCwxLDguNDgsOC40OEw4OC40OSwxMjhaIi8+PC9zdmc+');}.icon-chat{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMTYsNTBINDBBMTQsMTQsMCwwLDAsMjYsNjRWMjI0YTEzLjg4LDEzLjg4LDAsMCwwLDguMDksMTIuNjlBMTQuMTEsMTQuMTEsMCwwLDAsNDAsMjM4YTEzLjg3LDEzLjg3LDAsMCwwLDktMy4zMWwuMDYtLjA1TDgyLjIzLDIwNkgyMTZhMTQsMTQsMCwwLDAsMTQtMTRWNjRBMTQsMTQsMCwwLDAsMjE2LDUwWm0yLDE0MmEyLDIsMCwwLDEtMiwySDgwYTYsNiwwLDAsMC0zLjkyLDEuNDZMNDEuMjYsMjI1LjUzQTIsMiwwLDAsMSwzOCwyMjRWNjRhMiwyLDAsMCwxLDItMkgyMTZhMiwyLDAsMCwxLDIsMloiLz48L3N2Zz4=');}.icon-envelope{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjQsNTBIMzJhNiw2LDAsMCwwLTYsNlYxOTJhMTQsMTQsMCwwLDAsMTQsMTRIMjE2YTE0LDE0LDAsMCwwLDE0LTE0VjU2QTYsNiwwLDAsMCwyMjQsNTBabS05Niw4NS44Nkw0Ny40Miw2MkgyMDguNThaTTEwMS42NywxMjgsMzgsMTg2LjM2VjY5LjY0Wm04Ljg4LDguMTRMMTI0LDE0OC40MmE2LDYsMCwwLDAsOC4xLDBsMTMuNC0xMi4yOEwyMDguNTgsMTk0SDQ3LjQzWk0xNTQuMzMsMTI4LDIxOCw2OS42NFYxODYuMzZaIi8+PC9zdmc+');}.icon-sun-dim{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xMjIsNDBWMzJhNiw2LDAsMCwxLDEyLDB2OGE2LDYsMCwwLDEtMTIsMFptNjgsODhhNjIsNjIsMCwxLDEtNjItNjJBNjIuMDcsNjIuMDcsMCwwLDEsMTkwLDEyOFptLTEyLDBhNTAsNTAsMCwxLDAtNTAsNTBBNTAuMDYsNTAuMDYsMCwwLDAsMTc4LDEyOFpNNTkuNzYsNjguMjRhNiw2LDAsMSwwLDguNDgtOC40OGwtOC04YTYsNiwwLDAsMC04LjQ4LDguNDhabTAsMTE5LjUyLTgsOGE2LDYsMCwxLDAsOC40OCw4LjQ4bDgtOGE2LDYsMCwxLDAtOC40OC04LjQ4Wm0xMzYtMTM2LTgsOGE2LDYsMCwxLDAsOC40OCw4LjQ4bDgtOGE2LDYsMCwwLDAtOC40OC04LjQ4Wm0uNDgsMTM2YTYsNiwwLDAsMC04LjQ4LDguNDhsOCw4YTYsNiwwLDAsMCw4LjQ4LTguNDhaTTQwLDEyMkgzMmE2LDYsMCwwLDAsMCwxMmg4YTYsNiwwLDAsMCwwLTEyWm04OCw4OGE2LDYsMCwwLDAtNiw2djhhNiw2LDAsMCwwLDEyLDB2LThBNiw2LDAsMCwwLDEyOCwyMTBabTk2LTg4aC04YTYsNiwwLDAsMCwwLDEyaDhhNiw2LDAsMCwwLDAtMTJaIi8+PC9zdmc+');}.icon-moon{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMzIuMTMsMTQzLjY0YTYsNiwwLDAsMC02LTEuNDlBOTAuMDcsOTAuMDcsMCwwLDEsMTEzLjg2LDI5Ljg1YTYsNiwwLDAsMC03LjQ5LTcuNDhBMTAyLjg4LDEwMi44OCwwLDAsMCw1NC40OCw1OC42OCwxMDIsMTAyLDAsMCwwLDE5Ny4zMiwyMDEuNTJhMTAyLjg4LDEwMi44OCwwLDAsMCwzNi4zMS01MS44OUE2LDYsMCwwLDAsMjMyLjEzLDE0My42NFptLTQyLDQ4LjI5YTkwLDkwLDAsMCwxLTEyNi0xMjZBOTAuOSw5MC45LDAsMCwxLDk5LjY1LDM3LjY2LDEwMi4wNiwxMDIuMDYsMCwwLDAsMjE4LjM0LDE1Ni4zNSw5MC45LDkwLjksMCwwLDEsMTkwLjEsMTkxLjkzWiIvPjwvc3ZnPg==');}.icon-logo-jakevan{--icon:url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+PHN2ZyAgICB3aWR0aD0iMzIiICAgIGhlaWdodD0iMzIiICAgIHZpZXdCb3g9IjAgMCAzMiAzMiIgICAgdmVyc2lvbj0iMS4xIiAgICB4bWw6c3BhY2U9InByZXNlcnZlIiAgICBzdHlsZT0iY2xpcC1ydWxlOmV2ZW5vZGQ7ZmlsbC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlLWxpbmVjYXA6c3F1YXJlO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoxLjUiICAgIGlkPSJzdmcxNCIgICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiAgICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZGVmcyAgICBpZD0iZGVmczE0IiAvPjxwYXRoICAgIGQ9Ik0gMTYuNTgwMDc4LDIuMTMyODEyNSBDIDguODY0ODQ1LDIuMTMyODEyNSAxLjQ0MDYwMjEsOC4xNDY2MjE2IDIuMDMzMjAzMSwxNS44Mzk4NDQgMi41OTQ0NTg4LDIzLjEyNjY2NiA4Ljk3MTIwMTMsMjkuMDI1MTU1IDE1Ljk3MDcwMywyOS43ODcxMDkgMjQuMjIxMjI0LDMwLjY4NTI0IDMwLjQ0MDkxMSwyMy44MzQyNzcgMjkuOTc0NjA5LDE1Ljc5ODgyOCAyOS41Mjc4MTMsOC4wOTk4NjU2IDI0LjI5MTU2LDIuMTMyODEyNSAxNi41ODAwNzgsMi4xMzI4MTI1IFogbSAwLDAuNjA3NDIxOSBoIDAuMDMxMjUgViAyOS4yMzI0MjIgYyAtMC4xOTAzMjMsLTAuMDEyMTggLTAuMzgxNTIxLC0wLjAyNzgzIC0wLjU3NDIxOSwtMC4wNDg4MyBDIDkuMzEzMDQ1NywyOC40NTE2MTcgMy4xNzc4Nzc1LDIyLjc5MzA0NCAyLjYzODY3MTksMTUuNzkyOTY5IDIuMDcyNDU2LDguNDQyMDE1IDkuMjA4MTAwOSwyLjc0MDIzNDQgMTYuNTgwMDc4LDIuNzQwMjM0NCBaIG0gLTAuNDkyMTg3LDEuMTQ0NTMxMiAtMC4wOTE4LDAuMDAzOTEgQyAxMi43MjE1Niw0LjAxODM0MTMgOS41NDY0NzQzLDUuMzY0Njg3NyA3LjI1LDcuNSA0Ljk1MzUyNTcsOS42MzUzMTIzIDMuNTM2MzY0NywxMi41NjQ5OCAzLjc4OTA2MjUsMTUuODQ1NzAzIDQuMjgwODY2OCwyMi4yMzAwMzIgOS44NjA1ODc5LDI3LjM4MzYwOCAxNS45OTAyMzQsMjguMDUyNzM0IGwgMC4wODk4NCwwLjAwOTggMC4wMTk1MywtMC4xODE2NDEgLTAuMDg5ODQsLTAuMDA5OCBDIDkuOTY4NzM4OSwyNy4yMTE2NDMgNC40NTQ5NjM4LDIyLjExNjQ4MSAzLjk3MDcwMzEsMTUuODMwMDc4IDMuNzIyNjQ5MSwxMi42MDk2NDQgNS4xMTE4OTc5LDkuNzM5MDQ3NyA3LjM3NSw3LjYzNDc2NTYgOS42MzgxMDIxLDUuNTMwNDgzNiAxMi43NzQ2MjUsNC4xOTgxOTcgMTYuMDAzOTA2LDQuMDcwMzEyNSBsIDAuMDg5ODQsLTAuMDAzOTEgeiBtIDAsMS41MDE5NTMxIC0wLjA5MTgsMC4wMDM5MSBDIDEzLjEyODA1NCw1LjUwNDE5NzEgMTAuMzQ3NDM0LDYuNjg0MzQ1NCA4LjMzNTkzNzUsOC41NTQ2ODc1IDYuMzI0NDQxMSwxMC40MjUwMyA1LjA4MTMzNDEsMTIuOTkwODI3IDUuMzAyNzM0NCwxNS44NjUyMzQgNS43MzM1MzM2LDIxLjQ1NzY1NiAxMC42MjEyNjUsMjUuOTcwNTQ4IDE1Ljk5MDIzNCwyNi41NTY2NDEgbCAwLjA4OTg0LDAuMDA5OCAwLjAxOTUzLC0wLjE3OTY4NyAtMC4wODk4NCwtMC4wMDk4IEMgMTAuNzI5NDE5LDI1LjgwMDUzMiA1LjkwNzYzMDcsMjEuMzQ2MDU2IDUuNDg0Mzc1LDE1Ljg1MTU2MyA1LjI2NzYxODQsMTMuMDM3NDQyIDYuNDgyODEzMywxMC41MjY4MTIgOC40NjA5Mzc1LDguNjg3NSAxMC40MzkwNjIsNi44NDgxODgzIDEzLjE4MTEyLDUuNjg0MDUyOCAxNi4wMDM5MDYsNS41NzIyNjU2IGwgMC4wODk4NCwtMC4wMDM5MSB6IG0gMCwxLjUwMzkwNjMgLTAuMDkxOCwwLjAwMTk1IEMgMTMuNTM0NTQsNi45OTAwNjYzIDExLjE0ODQsOC4wMDQwMDcyIDkuNDIxODc1LDkuNjA5Mzc1IDcuNjk1MzQ5NywxMS4yMTQ3NDMgNi42MjgyNTU4LDEzLjQxNjY4IDYuODE4MzU5NCwxNS44ODQ3NjYgNy4xODgxNTUxLDIwLjY4NTI2OSAxMS4zODE5NiwyNC41NTk0NDQgMTUuOTkwMjM0LDI1LjA2MjUgbCAwLjA4OTg0LDAuMDA5OCAwLjAxOTUzLC0wLjE4MTY0MSAtMC4wODk4NCwtMC4wMDk4IEMgMTEuNDkwMTE0LDI0LjM4NzQ3NyA3LjM2MjI1MjIsMjAuNTczNjcxIDcsMTUuODcxMDk0IDYuODE0NTQwMSwxMy40NjMyOTcgNy44NTM3MjE5LDExLjMxNjUyNSA5LjU0Njg3NSw5Ljc0MjE4NzUgYyAxLjY5MzE1MywtMS41NzQzMzc0IDQuMDQwNzMsLTIuNTcwMzI2IDYuNDU3MDMxLC0yLjY2NjAxNTYgbCAwLjA4OTg0LC0wLjAwMzkxIHogbSAwLDEuNTAxOTUzMSAtMC4wOTE4LDAuMDAzOTEgYyAtMi4wNTUwNzMsMC4wODEzODQgLTQuMDQ2NzI4LDAuOTI1MjMzMSAtNS40ODgyODEsMi4yNjU2MjQ5IC0xLjQ0MTU1NDcsMS4zNDAzOTMgLTIuMzMyNjM1NCwzLjE4MjM3OSAtMi4xNzM4Mjg2LDUuMjQ0MTQxIDAuMzA2Mzk5NCwzLjk3NzUyIDMuNzU3OTY0Niw3LjE3NDg3NyA3LjU3MDMxMjYsNy42MzQ3NjYgbCAtMC4wMDIsMC4wMTc1OCAwLjA4Nzg5LDAuMDA3OCBoIDAuMDAyIGwgMC4wMDk4LDAuMDAyIDAuMDgwMDgsMC4wMDk4IDAuMDIxNDgsLTAuMTgxNjQxIGggLTAuMDAzOSB2IC0wLjAwMiBsIC0wLjA4OTg0LC0wLjAwOTggaCAtMC4wMDIgQyAxMi4yNDk0NzYsMjIuOTczNTY1IDguODE2ODE1NSwxOS44MDI1NzYgOC41MTU2MTcsMTUuODkyNTcxIDguMzYxNDU0LDEzLjg5MTA5OCA5LjIyNDYyMjUsMTIuMTA2MjMgMTAuNjMyODA1LDEwLjc5Njg2OCAxMi4wNDA5ODUsOS40ODc1MDU0IDEzLjk5NDA4NCw4LjY1NzcwOTcgMTYuMDAzODk4LDguNTc4MTE3NyBsIDAuMDg5ODQsLTAuMDAzOTEgeiBtIDAsMS41MDE5NTMyIC0wLjA5MTgsMC4wMDM5MSBjIC0xLjY0ODU4MiwwLjA2NTI4NyAtMy4yNDU3NjYsMC43NDI5NDI3IC00LjQwMjM0NCwxLjgxODM1OTcgLTEuMTU2NTc4LDEuMDc1NDE3IC0xLjg3MTY1MDMsMi41NTM1NDUgLTEuNzQ0MTQwNiw0LjIwODk4NCAwLjI0NTM5NDYsMy4xODU1OSAzLjAwMzEwMzYsNS43NDQzODEgNi4wNTQ2ODc2LDYuMTIxMDk0IGwgLTAuMDAyLDAuMDE3NTggMC4wODc4OSwwLjAwNzggaCAwLjAwMiBsIDAuMDg3ODksMC4wMDk4IGggMC4wMDIgbCAwLjAyMTQ4LC0wLjE4MTY0IGggLTAuMDAzOSBsIC0wLjA4Nzg5LC0wLjAwOTggaCAtMC4wMDIgLTAuMDAyIGMgLTIuOTk3NjIzLC0wLjMyODE1NiAtNS43MzYzNjgsLTIuODYwNDMzIC01Ljk3NjU2MiwtNS45Nzg1MTYgLTAuMTIyODY1OSwtMS41OTUxNSAwLjU2NDI5NCwtMy4wMTgxMTMgMS42ODc1LC00LjA2MjUgMS4xMjMyMDYsLTEuMDQ0Mzg2IDIuNjgxODMzLC0xLjcwNjAzNyA0LjI4NTE1NiwtMS43Njk1MzEgbCAwLjA4OTg0LC0wLjAwMzkgeiBtIDQuMTIxMDkzLDEuMzIwMzEyNyBoIDEuNDY2Nzk3IGwgMS4zMjgxMjUsMy45NjI4OSB2IC0zLjk0NzI2NSBoIDEuMTAzNTE2IHYgMS43MzYzMjggYyAwLjA0NjM5LC0wLjA4NzQyIDAuMDk5ODksLTAuMTc2MjEyIDAuMTYwMTU2LC0wLjI2MzY3MiAwLjA2MDMxLC0wLjA4NzQ2IDAuMTIwMjk0LC0wLjE2OTY4NyAwLjE3NzczNCwtMC4yNDgwNDcgbCAwLjg1MzUxNiwtMS4yMjQ2MDkgaCAzLjU3ODEyNSB2IDAuODY3MTg3IGggLTEuMzE2NDA2IHYgMC42MjY5NTMgaCAxLjIxODc1IHYgMC44NjcxODggaCAtMS4yMTg3NSB2IDAuNzUzOTA2IGggMS4zMTY0MDYgdiAwLjg4MDg2IGggLTMuNjM4NjcyIGwgLTAuODA0Njg3LC0xLjUyNzM0NCAtMC4zMjYxNzIsMC4xOTE0MDYgdiAxLjMzNTkzOCBoIC0yLjI4OTA2MyBsIC0wLjIwMTE3MiwtMC43NDgwNDcgaCAtMS4zMjAzMTIgbCAtMC4yMDcwMzEsMC43NDgwNDcgaCAtMS4yMDcwMzIgeiBtIC0yLjQxNDA2MiwwLjAxNTYzIGggMS4xMDM1MTUgdiAzLjYwNTQ2OSBjIDEwZS03LDAuNDAwOTA1IC0wLjA2MTE3LDAuNzIyMzU1IC0wLjE4MzU5MywwLjk2Mjg5IC0wLjEyMjQyMiwwLjI0MDUzNSAtMC4yOTA4MTMsMC40MTQwMTEgLTAuNTA3ODEzLDAuNTIxNDg0IC0wLjIxNjk5OSwwLjEwNzUxMyAtMC40NjgyNTUsMC4xNjAxNTcgLTAuNzUzOTA2LDAuMTYwMTU3IC0wLjEyNDI1NiwwIC0wLjIzNDQ2NSwtMC4wMDU3IC0wLjMyODEyNSwtMC4wMTc1OCAtMC4wOTM2NiwtMC4wMTE3NyAtMC4xNzM1NzgsLTAuMDI0NDYgLTAuMjQyMTg4LC0wLjAzOTA2IFYgMTUuNTgzOTkgYyAwLjA1OTM2LDAuMDEwODYgMC4xMjI2NzQsMC4wMjM0MSAwLjE4OTQ1NCwwLjAzNzExIDAuMDY2NzcsMC4wMTM3IDAuMTM4OTM2LDAuMDIxNDggMC4yMTY3OTYsMC4wMjE0OCAwLjEzMTY3NiwwIDAuMjMzMzYxLC0wLjAyNzIzIDAuMzA2NjQxLC0wLjA4MDA4IDAuMDczMjMsLTAuMDUyODYgMC4xMjQ2MTcsLTAuMTMyNjExIDAuMTU0Mjk3LC0wLjIzODI4MSAwLjAyOTY4LC0wLjEwNTY3IDAuMDQ0OTIsLTAuMjM3OTE5IDAuMDQ0OTIsLTAuMzk2NDg1IHogbSA4LjY2Nzk2OSwwLjA1NDY5IC0xLjI0NDE0MSwxLjczNjMyOCAxLjI0NDE0MSwyLjE3NTc4MiB6IG0gLTEwLjM3NSwwLjExMzI4MiAtMC4wOTE4LDAuMDAyIGMgLTEuMjQyMDk1LDAuMDQ5MTkgLTIuNDQ0OCwwLjU2MDY1MiAtMy4zMTY0MDYsMS4zNzEwOTMgLTAuODcxNjA4LDAuODEwNDQyIC0xLjQxMjYyLDEuOTI0NzEyIC0xLjMxNjQwNywzLjE3MzgyOSAwLjE4NDM5LDIuMzkzNjU0IDIuMjUwMTk1LDQuMzEyMDIyIDQuNTQxMDE2LDQuNjA1NDY4IGwgLTAuMDAyLDAuMDE3NTggMC4wODc4OSwwLjAwNzggaCAwLjAwMiBsIDAuMDg3ODksMC4wMDk4IGggMC4wMDIgbCAwLjAxOTUzLC0wLjE3OTY4NyBoIC0wLjAwMiB2IC0wLjAwMiBsIC0wLjA3ODEzLC0wLjAwNzggLTAuMDA5OCwtMC4wMDIgaCAtMC4wMDIgLTAuMDAyIGMgLTIuMjM2OTYxLC0wLjI0NTEyMSAtNC4yODM3LC0yLjEzNjczMSAtNC40NjI4OSwtNC40NjI4OSAtMC4wOTE1NywtMS4xODg4MjYgMC40MjE1MzEsLTIuMjQ3OTMzIDEuMjU5NzY2LC0zLjAyNzM0NCAwLjgzODIzNCwtMC43Nzk0MTEgMi4wMDIzODIsLTEuMjcyOTE2IDMuMTk5MjE4LC0xLjMyMDMxMyBsIDAuMDg5ODQsLTAuMDAzOSB6IG0gNC44NjEzMjgsMC40NzQ2MDkgYyAtMC4wMTY3MSwwLjA5MTExIC0wLjAzOTcyLDAuMjAzOTI4IC0wLjA3MDMxLDAuMzM3ODkxIC0wLjAzMDYsMC4xMzM5MjIgLTAuMDYxMjgsMC4yNjUyNjkgLTAuMDkzNzUsMC4zOTY0ODQgLTAuMDMyNDIsMC4xMzExNzUgLTAuMDYxODUsMC4yNDA2NjUgLTAuMDg1OTQsMC4zMjgxMjUgbCAtMC4xNzU3ODIsMC42NTYyNSBoIDAuODY1MjM1IGwgLTAuMTczODI4LC0wLjY1NjI1IGMgLTAuMDE4NTEsLTAuMDcxMDYgLTAuMDQ2ODEsLTAuMTcyNTI5IC0wLjA4MjAzLC0wLjMwNDY4OCAtMC4wMzUyNiwtMC4xMzIxMTggLTAuMDY5MjEsLTAuMjY4OTM1IC0wLjEwMzUxNSwtMC40MTAxNTYgLTAuMDM0MywtMC4xNDEyMjEgLTAuMDYxNTMsLTAuMjU2NTQ2IC0wLjA4MDA4LC0wLjM0NzY1NiB6IG0gLTQuODYxMzI4LDEuMDI3MzQ0IC0wLjA5MTgsMC4wMDM5IGMgLTAuODM1NjA4LDAuMDMzMDkgLTEuNjQzODM2LDAuMzc0NDU2IC0yLjIzMDQ2OSwwLjkxOTkyMiAtMC41ODY2MzMsMC41NDU0NjYgLTAuOTUxNjM1LDEuMjk5Nzg2IC0wLjg4NjcxOSwyLjE0MjU3OCAwLjEyMzIwNSwxLjU5OTM3OCAxLjQ5ODEyOCwyLjg1OTQ2NyAzLjAyNTM5MSwzLjA3MjI2NSBsIC0wLjAwMzksMC4wMzMyIDAuMDg5ODQsMC4wMDk4IDAuMDgyMDMsMC4wMDc4IDAuMDA3OCwwLjAwMiBoIDAuMDAyIGwgMC4wMTk1MywtMC4xODE2NDEgaCAtMC4wMDIgbCAtMC4wODc4OSwtMC4wMDk4IGggLTAuMDAyIC0wLjAwMiBjIC0xLjQ3NjMzNywtMC4xNjIwNDEgLTIuODI5MDc2LC0xLjQxMjk5NiAtMi45NDcyNjUsLTIuOTQ3MjY2IC0wLjA2MDI3LC0wLjc4MjUwMyAwLjI3NjgxNywtMS40Nzk3MDUgMC44MzAwNzgsLTEuOTk0MTQxIDAuNTUzMjYxLC0wLjUxNDQzNSAxLjMyMjkzMywtMC44NDE3NDggMi4xMTMyODEsLTAuODczMDQ2IGwgMC4wODk4NCwtMC4wMDM5IHogbSAwLDEuNTAxOTUzIC0wLjA5MTgsMC4wMDM5IGMgLTAuNDI5MTE4LDAuMDE2OTkgLTAuODQyODczLDAuMTkyMTY2IC0xLjE0NDUzMSwwLjQ3MjY1NiAtMC4zMDE2NiwwLjI4MDQ5MSAtMC40OTA2NDgsMC42NzA5NTUgLTAuNDU3MDMyLDEuMTA3NDIyIDAuMDYyMTksMC44MDczNiAwLjc0MzgwMSwxLjQzMDI1NyAxLjUwOTc2NiwxLjU1ODU5NCBsIC0wLjAwMzksMC4wMzMyIDAuMDg5ODQsMC4wMDc4IGggMC4wMDIgbCAwLjA4Nzg5LDAuMDA5OCAwLjAyMTQ4LC0wLjE3OTY4NyBoIC0wLjAwMiB2IC0wLjAwMiBsIC0wLjA3ODEzLC0wLjAwNzggLTAuMDA5OCwtMC4wMDIgaCAtMC4wMDIgLTAuMDAyIGMgLTAuNzE1Njc1LC0wLjA3OTAxIC0xLjM3NDQ1NSwtMC42ODkyOSAtMS40MzE2NCwtMS40MzE2NDEgLTAuMDI4OTcsLTAuMzc2MTc5IDAuMTMyMTA0LC0wLjcxMTQ3NyAwLjQwMDM5MSwtMC45NjA5MzcgMC4yNjgyODYsLTAuMjQ5NDYxIDAuNjQzNDg1LC0wLjQwODYyNyAxLjAyNzM0MywtMC40MjM4MjggbCAwLjA4OTg0LC0wLjAwMzkgeiBtIDcuMDQxMDE1LDAuODQ5NjA5IGggMS4yNjE3MTkgbCAxLjE0MjU3OCwzLjQxNDA2MyB2IC0zLjQwMDM5MSBoIDEuMjM2MzI4IGwgMS4yNzUzOTEsMi4zOTI1NzggaCAwLjAxMzY3IGMgLTAuMDA0NywtMC4wNzUzNyAtMC4wMDg5LC0wLjE2NDAzMiAtMC4wMTM2NywtMC4yNjM2NzIgLTAuMDA0OCwtMC4wOTk2NCAtMC4wMDk3LC0wLjIwMDcxMyAtMC4wMTM2NywtMC4zMDI3MzQgLTAuMDA0LC0wLjEwMjAyIC0wLjAwNTksLTAuMTkxMDUxIC0wLjAwNTksLTAuMjY5NTMxIHYgLTEuNTU2NjQxIGggMC44NDM3NSB2IDMuNDQxNDA2IGggLTEuMjQyMTg4IGwgLTEuMjc5Mjk3LC0yLjQyMzgyOCBoIC0wLjAyMTQ4IGMgMC4wMDgsMC4wNzM3NyAwLjAxNTA4LDAuMTYyMDg4IDAuMDIxNDgsMC4yNjU2MjUgMC4wMDY0LDAuMTAzNTc5IDAuMDEyODgsMC4yMDg4OTEgMC4wMTc1OCwwLjMxNjQwNiAwLjAwNDgsMC4xMDc0NzQgMC4wMDc4LDAuMjA0NzA2IDAuMDA3OCwwLjI5MTAxNiB2IDEuNTUwNzgxIEggMjQuNTEzNjcyIEwgMjQuMzM5ODQ0LDE4LjA2MjUgaCAtMS4xMzY3MTkgbCAtMC4xNzc3MzQsMC42NDQ1MzEgaCAtMS4wMzkwNjMgeiBtIC00LjE1NDI5NywwLjAxMzY3IGggMS4wNjI1IGwgMC40NzY1NjMsMS43NDQxNDEgYyAwLjAxNzU0LDAuMDY1ODkgMC4wMzkzNywwLjE1MTEwNyAwLjA2MjUsMC4yNTM5MDYgMC4wMjMxNywwLjEwMjc1OCAwLjA0NDQ4LDAuMjA0NjYxIDAuMDY0NDUsMC4zMDY2NCAwLjAxOTk3LDAuMTAyMDIgMC4wMzIzMSwwLjE4NTYyIDAuMDM3MTEsMC4yNSAwLjAwNjQsLTAuMDY0MzggMC4wMTc2MSwtMC4xNDc2MjUgMC4wMzUxNiwtMC4yNDgwNDYgMC4wMTc1OSwtMC4xMDA0MjEgMC4wMzcwNCwtMC4yMDE1MzUgMC4wNTg1OSwtMC4zMDI3MzUgMC4wMjE2LC0wLjEwMTI0MSAwLjA0MTM4LC0wLjE4NDA2IDAuMDYwNTUsLTAuMjUgbCAwLjQ4NjMyOCwtMS43NTM5MDYgaCAxLjA2MDU0NyBsIC0xLjE0ODQzNywzLjQ0MTQwNiBoIC0xLjExMzI4MiB6IG0gNC43OTEwMTYsMC41NTI3MzQgYyAtMC4wMTQyOSwwLjA3ODQ0IC0wLjAzNDIxLDAuMTc1NjY5IC0wLjA2MDU1LDAuMjkxMDE2IC0wLjAyNjM0LDAuMTE1MzQ3IC0wLjA1NDA2LDAuMjMwNzgyIC0wLjA4MjAzLDAuMzQzNzUgLTAuMDI3OTMsMC4xMTI5NjkgLTAuMDUxNDcsMC4yMDU5MiAtMC4wNzIyNywwLjI4MTI1IGwgLTAuMTUyMzQ0LDAuNTY0NDUzIGggMC43NDYwOTQgbCAtMC4xNTAzOSwtMC41NjQ0NTMgYyAtMC4wMTU5MiwtMC4wNjEyMiAtMC4wMzk4OCwtMC4xNDc5NzEgLTAuMDcwMzEsLTAuMjYxNzE5IC0wLjAzMDI3LC0wLjExMzc4OSAtMC4wNjAyOSwtMC4yMzE5MzUgLTAuMDg5ODQsLTAuMzUzNTE1IC0wLjAyOTU2LC0wLjEyMTYyIC0wLjA1MjM1LC0wLjIyMjM0MiAtMC4wNjgzNiwtMC4zMDA3ODIgeiBtIC0zLjY0NjQ4NCwyLjk5MjE4OCBIIDIwLjU2MjUgYyAwLjE3NDc3NSwwIDAuMzIwNjU4LDAuMDI5NjQgMC40Mzk0NTMsMC4wODk4NCAwLjExODc1NCwwLjA2MDIgMC4yMTAyNTUsMC4xNDg1NTYgMC4yNzE0ODQsMC4yNjU2MjUgMC4wNjEyNywwLjExNzA2OSAwLjA5MTgsMC4yNjE4NjQgMC4wOTE4LDAuNDMzNTkzIDAsMC4xNzk4MDcgLTAuMDMzMDEsMC4zMzEzIC0wLjA5NzY2LDAuNDUzMTI1IC0wLjA2NDYxLDAuMTIxODI2IC0wLjE2MDQ3NywwLjIxMzc2MSAtMC4yODcxMDksMC4yNzUzOTEgLTAuMTI2NTksMC4wNjE2NyAtMC4yODM5NjUsMC4wOTE4IC0wLjQ3MDcwMywwLjA5MTggSCAyMC4xMTkxNCBaIG0gMS40MzU1NDYsMCBoIDAuODk2NDg1IHYgMC4xMDE1NjIgaCAtMC43ODEyNSB2IDAuNjExMzI4IEggMjIuNDA2MjUgViAxOS42MjUgaCAtMC43MzYzMjggdiAwLjY5MzM1OSBoIDAuNzgxMjUgdiAwLjEwMTU2MyBoIC0wLjg5NjQ4NSB6IG0gMS4wODM5ODUsMCBoIDAuMzk4NDM3IGMgMC4xMzAwMDgsMCAwLjIzNzIyOSwwLjAxMzA5IDAuMzI0MjE5LDAuMDQxMDIgMC4wODcwMywwLjAyNzg4IDAuMTUzMTY2LDAuMDc0NzggMC4xOTcyNjYsMC4xMzg2NzIgMC4wNDQwMiwwLjA2Mzg0IDAuMDY2NDEsMC4xNDkxOTEgMC4wNjY0MSwwLjI1NzgxMyAwLDAuMDgxNDggLTAuMDE0NjIsMC4xNTI1ODcgLTAuMDQ0OTIsMC4yMTA5MzcgLTAuMDMwMywwLjA1ODM1IC0wLjA3MjAyLDAuMTA1MTc4IC0wLjEyNSwwLjE0MjU3OCAtMC4wNTMxLDAuMDM3NDggLTAuMTE0MTU0LDAuMDY2MTMgLTAuMTgzNTk0LDAuMDg1OTQgbCAwLjQ0OTIxOSwwLjczMjQyMiBIIDIzLjU4Mzk5IGwgLTAuNDIzODI4LC0wLjY5OTIxOSBoIC0wLjQwNjI1IHYgMC42OTkyMTkgaCAtMC4xMTUyMzQgeiBtIDEuMDA3ODEyLDAgaCAwLjExOTE0MSBsIDAuMzA4NTk0LDEuMTM0NzY1IGMgMC4wMDgxLDAuMDMwMDkgMC4wMTQzOCwwLjA1ODc5IDAuMDIxNDgsMC4wODU5NCAwLjAwNywwLjAyNzE1IDAuMDEzNTMsMC4wNTI4MSAwLjAxOTUzLDAuMDc4MTMgMC4wMDYsMC4wMjUzNCAwLjAxMTk4LDAuMDUwMzUgMC4wMTc1OCwwLjA3NDIyIDAuMDA1NiwwLjAyMzgzIDAuMDExMTIsMC4wNDgwNCAwLjAxNTYzLDAuMDcyMjcgMC4wMDUyLC0wLjAyNDI0IDAuMDEwNDMsLTAuMDQ5MjUgMC4wMTU2MywtMC4wNzQyMiAwLjAwNTIsLTAuMDI0OTMgMC4wMTEyOCwtMC4wNDg1NSAwLjAxNzU4LC0wLjA3NDIyIDAuMDA2NCwtMC4wMjU3MSAwLjAxMjMzLC0wLjA1MjU2IDAuMDE5NTMsLTAuMDgwMDggMC4wMDcsLTAuMDI3NTIgMC4wMTYzOSwtMC4wNTc3OSAwLjAyNTM5LC0wLjA4Nzg5IGwgMC4zMjAzMTMsLTEuMTI4OTA2IGggMC4xMTUyMzQgbCAwLjMzMzk4NSwxLjEzNjcxOSBjIDAuMDA5LDAuMDMxNTMgMC4wMTc5OSwwLjA1OTk3IDAuMDI1MzksMC4wODc4OSAwLjAwNzUsMC4wMjc4OSAwLjAxMzUzLDAuMDU0NzQgMC4wMTk1MywwLjA4MDA4IDAuMDA1OSwwLjAyNTMgMC4wMTIyOCwwLjA1MDM1IDAuMDE3NTgsMC4wNzQyMiAwLjAwNTIsMC4wMjM4MyAwLjAxMDQzLDAuMDQ4MDggMC4wMTU2MywwLjA3MjI3IDAuMDA2LC0wLjAzMzAxIDAuMDExMTgsLTAuMDY1NzIgMC4wMTc1OCwtMC4wOTc2NiAwLjAwNjMsLTAuMDMxOSAwLjAxNDQ0LC0wLjA2NDM5IDAuMDIzNDQsLTAuMDk5NjEgMC4wMDksLTAuMDM1MjYgMC4wMiwtMC4wNzUzNyAwLjAzMTI1LC0wLjExNzE4NyBsIDAuMzE0NDUzLC0xLjEzNjcxOSBoIDAuMTE5MTQxIGwgLTAuNDQ3MjY2LDEuNjA5Mzc1IGggLTAuMTExMzI4IGwgLTAuMzUxNTYyLC0xLjIxNDg0NCBjIC0wLjAwODMsLTAuMDI1NjcgLTAuMDE1MDgsLTAuMDUxMDkgLTAuMDIxNDgsLTAuMDc0MjIgLTAuMDA2MywtMC4wMjMxMyAtMC4wMTE4OCwtMC4wNDM1NCAtMC4wMTc1OCwtMC4wNjQ0NSAtMC4wMDU2LC0wLjAyMDkyIC0wLjAxMDczLC0wLjA0MTg1IC0wLjAxNTYzLC0wLjA2MDU1IC0wLjAwNDgsLTAuMDE4NjkgLTAuMDA4NywtMC4wMzU0IC0wLjAxMTcyLC0wLjA1MDc4IC0wLjAwMjksMC4wMTUzOCAtMC4wMDY0LDAuMDMxNjEgLTAuMDA5OCwwLjA0ODgzIC0wLjAwMzQsMC4wMTcyNiAtMC4wMDcyLDAuMDM0MzggLTAuMDExNzIsMC4wNTI3MyAtMC4wMDQ1LDAuMDE4MzMgLTAuMDA4NCwwLjAzODc0IC0wLjAxMzY3LDAuMDU4NTkgLTAuMDA1MiwwLjAxOTgxIC0wLjAxMTU4LDAuMDM5MjcgLTAuMDE3NTgsMC4wNjA1NSBsIC0wLjM0OTYwOSwxLjI0NDE0MSBoIC0wLjExMzI4MSB6IG0gMi4wMzUxNTcsMCBoIDAuODk2NDg0IHYgMC4xMDE1NjIgaCAtMC43NzkyOTcgdiAwLjYxMTMyOCBoIDAuNzM2MzI4IFYgMTkuNjI1IGggLTAuNzM2MzI4IHYgMC42OTMzNTkgaCAwLjc3OTI5NyB2IDAuMTAxNTYzIGggLTAuODk2NDg0IHogbSAxLjA4Mzk4NCwwIGggMC4zOTg0MzcgYyAwLjEyOTkyNiwwIDAuMjM5MTQyLDAuMDEzMDkgMC4zMjYxNzIsMC4wNDEwMiAwLjA4Njk5LDAuMDI3ODggMC4xNTEyMTMsMC4wNzQ3OCAwLjE5NTMxMywwLjEzODY3MiAwLjA0NDAyLDAuMDYzODQgMC4wNjY0MSwwLjE0OTE5MSAwLjA2NjQxLDAuMjU3ODEzIDAsMC4wODE0OCAtMC4wMTQ2MiwwLjE1MjU4NyAtMC4wNDQ5MiwwLjIxMDkzNyAtMC4wMzAyNiwwLjA1ODM1IC0wLjA3MjAyLDAuMTA1MTc4IC0wLjEyNSwwLjE0MjU3OCAtMC4wNTMwNiwwLjAzNzQ4IC0wLjExNDE1MywwLjA2NjEzIC0wLjE4MzU5NCwwLjA4NTk0IGwgMC40NDkyMTksMC43MzI0MjIgaCAtMC4xMzY3MTkgbCAtMC40MjE4NzUsLTAuNjk5MjE5IGggLTAuNDA4MjAzIHYgMC42OTkyMTkgaCAtMC4xMTUyMzQgeiBtIDEuMTgxNjQxLDAgaCAwLjg5NjQ4NCB2IDAuMTAxNTYyIEggMjguMDYyNSB2IDAuNjg3NSBoIDAuNzM4MjgxIHYgMC4wOTk2MSBIIDI4LjA2MjUgdiAwLjcyMDcwMyBoIC0wLjExNTIzNCB6IG0gLTcuNzEyODkxLDAuMDk5NjEgdiAxLjQxMDE1NiBoIDAuMjcxNDg0IGMgMC4yNDcyNjEsMCAwLjQzMjE4MywtMC4wNjA0IDAuNTU0Njg4LC0wLjE3OTY4NyAwLjEyMjU0OSwtMC4xMTkyNDIgMC4xODM1OTQsLTAuMjk4NTQyIDAuMTgzNTk0LC0wLjUzNzEwOSAwLC0wLjE1MzM1OCAtMC4wMjUzNiwtMC4yODAwNTQgLTAuMDc2MTcsLTAuMzgyODEzIC0wLjA1MDc3LC0wLjEwMjc1OCAtMC4xMjk3OTMsLTAuMTgwNjcyIC0wLjIzNDM3NSwtMC4yMzI0MjIgLTAuMTA0NTgyLC0wLjA1MTcxIC0wLjIzNTg4MiwtMC4wNzgxMyAtMC4zOTY0ODUsLTAuMDc4MTMgeiBtIDIuNTE5NTMxLDAgdiAwLjcwODk4NSBoIDAuMzI2MTcyIGMgMC4xMzM3NiwwIDAuMjM3NDcsLTAuMDMwNjQgMC4zMTI1LC0wLjA5Mzc1IDAuMDc1MTYsLTAuMDYzMTUgMC4xMTMyODEsLTAuMTUzMzUgMC4xMTMyODEsLTAuMjcxNDg1IDAsLTAuMTI5OTQ0IC0wLjAzOTk4LC0wLjIyMDMyMSAtMC4xMTkxNCwtMC4yNjk1MzEgLTAuMDc5MjQsLTAuMDQ5MTcgLTAuMTk5MjI0LC0wLjA3NDIyIC0wLjM2MTMyOCwtMC4wNzQyMiB6IG0gNC4xMjY5NTMsMCB2IDAuNzA4OTg1IGggMC4zMjYxNzIgYyAwLjEzMzc1OSwwIDAuMjM5NDIzLC0wLjAzMDY0IDAuMzE0NDUzLC0wLjA5Mzc1IDAuMDc1MTIsLTAuMDYzMTUgMC4xMTEzMjgsLTAuMTUzMzUgMC4xMTEzMjgsLTAuMjcxNDg1IDAsLTAuMTI5OTQ0IC0wLjAzODA4LC0wLjIyMDMyMSAtMC4xMTcxODcsLTAuMjY5NTMxIC0wLjA3OTI0LC0wLjA0OTE3IC0wLjIwMTE3OCwtMC4wNzQyMiAtMC4zNjMyODEsLTAuMDc0MjIgeiIgICAgc3R5bGU9ImJhc2VsaW5lLXNoaWZ0OmJhc2VsaW5lO2NsaXAtcnVsZTpub256ZXJvO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7dmVjdG9yLWVmZmVjdDpub25lO2ZpbGw6IzIyMjIyMjtmaWxsLXJ1bGU6bm9uemVybztzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlO3N0b3AtY29sb3I6IzAwMDAwMDtzdG9wLW9wYWNpdHk6MSIgICAgaWQ9InBhdGgxNyIgLz48L3N2Zz4=');}.icon-x{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMDQuMjQsMTk1Ljc2YTYsNiwwLDEsMS04LjQ4LDguNDhMMTI4LDEzNi40OSw2MC4yNCwyMDQuMjRhNiw2LDAsMCwxLTguNDgtOC40OEwxMTkuNTEsMTI4LDUxLjc2LDYwLjI0YTYsNiwwLDAsMSw4LjQ4LTguNDhMMTI4LDExOS41MWw2Ny43Ni02Ny43NWE2LDYsMCwwLDEsOC40OCw4LjQ4TDEzNi40OSwxMjhaIi8+PC9zdmc+');}.icon-loading{--icon:url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+PHN2ZyAgICB3aWR0aD0iMzIiICAgIGhlaWdodD0iMzIiICAgIHZpZXdCb3g9IjAgMCAzMiAzMiIgICAgdmVyc2lvbj0iMS4xIiAgICB4bWw6c3BhY2U9InByZXNlcnZlIiAgICBzdHlsZT0iY2xpcC1ydWxlOmV2ZW5vZGQ7ZmlsbC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjEuNSIgICAgaWQ9InN2ZzEwIiAgICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciICAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxkZWZzICAgIGlkPSJkZWZzMTAiIC8+PHBhdGggICAgaWQ9InBhdGgxMSIgICAgc3R5bGU9ImJhc2VsaW5lLXNoaWZ0OmJhc2VsaW5lO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7dmVjdG9yLWVmZmVjdDpub25lO2ZpbGw6IzIyMjIyMjtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlO3N0b3AtY29sb3I6IzAwMDAwMDtzdG9wLW9wYWNpdHk6MSIgICAgZD0ibSAxNi42MjEwOTQsMS4xNDI1NzgxIGMgLTguMjY2MzIzMiwwIC0xNi4yMjA4NjczOCw2LjQ0MjgwOTUgLTE1LjU4NTkzNzgsMTQuNjg1NTQ2OSAwLjYwMTM0NTUsNy44MDczMDggNy40MzQxMjY0LDE0LjEyNjk4IDE0LjkzMzU5MzgsMTQuOTQzMzU5IDguODM5ODQ1LDAuOTYyMjgzIDE1LjUwNTQ2OSwtNi4zNzY5MTkgMTUuMDA1ODU5LC0xNC45ODYzMjggQyAzMC40OTU5LDcuNTM2MjY4NCAyNC44ODMzOTcsMS4xNDI1NzgxIDE2LjYyMTA5NCwxLjE0MjU3ODEgWiBtIDAsMC42NTAzOTA3IEMgMjYuNDg4Nzg2LDEuODAzODY0NSAyOS43MTQ1MTgsOS41OTM1ODMzIDMwLjMwMjczNCwxNS44MDQ2ODggMzEuMTQxOTgyLDI0LjY2NjM2NSAyMi4xNjA0NTksMzEuMTY4MDc3IDE2LjAzOTA2MiwzMC4xMjUgOC44OTUxMzI3LDI4LjkwNzY4MSAyLjI2MTMxNDIsMjMuMjc5Mzc2IDEuNjgzNTkzOCwxNS43NzkyOTcgMS4wNzY5MzM4LDcuOTAzMjc1NCA4LjcyMjU0NTEsMS43ODQyNjk5IDE2LjYyMTA5NCwxLjc5Mjk2ODggWiBtIC0wLjA2NDQ1LDEuMjE4NzUgYyAtMy42MTAwODMsMCAtNy4xNTQ3OTk1LDEuNDAxMDY4NyAtOS43MzA0NjkxLDMuNzAzMTI1IEMgNC4yNTA1MDIzLDkuMDE2OTAwMiAyLjY0MjAzNzIsMTIuMjI2Mjk1IDIuOTE5OTIxOSwxNS44MzM5ODQgMy40NDY5MzUsMjIuNjc1NzEyIDkuNDI4OTY0OSwyOC4xOTg5ODUgMTUuOTk4MDQ3LDI4LjkxNDA2MiAyMy43MTQyNTYsMjkuNzU0MDIzIDI5LjUzMTYwMywyMy4zMzE3IDI5LjA5NTcwMywxNS44MjAzMTIgMjguNjc3OTQ4LDguNjIxMzk1MyAyMy43NzY2ODYsMy4wMTE3MTg4IDE2LjU1NjY0MSwzLjAxMTcxODggWiBtIDAsMC4xOTUzMTI0IGMgNy4xMTkxMzQsMCAxMS45MzI3MSw1LjUwODEzNzMgMTIuMzQ1NzAzLDEyLjYyNDk5OTggQyAyOS4zMzIwNjIsMjMuMjM2ODk2IDIzLjYxODk1OCwyOS41NDU5OTggMTYuMDE5NTMxLDI4LjcxODc1IDkuNTQ1NDMyMSwyOC4wMTQwMTIgMy42MzQxNjM3LDIyLjU1NTE0MyAzLjExNTIzNDQsMTUuODE4MzU5IDIuODQyNDU2MywxMi4yNzY5NjcgNC40MTg0MTA5LDkuMTI4MzE2OSA2Ljk1NzAzMTIsNi44NTkzNzUgOS40OTU2NTE2LDQuNTkwNDMzMSAxMi45OTcwOTMsMy4yMDcwMzEyIDE2LjU1NjY0MSwzLjIwNzAzMTIgWiBtIC0wLjA3MDMxLDEuNDE2MDE1NyBjIC0zLjE2MTk3MywwIC02LjI2MzUwOSwxLjIyNTgxMzkgLTguNTE5NTMxMSwzLjI0MjE4NzUgQyA1LjcxMDc2OTEsOS44ODE2MDggNC4zMDE0NTQyLDEyLjY5NDU4OSA0LjU0NDkyMTksMTUuODU1NDY5IDUuMDA2NTYyNCwyMS44NDg1NTQgMTAuMjQ0MTc4LDI2LjY4NjE1OSAxNS45OTgwNDcsMjcuMzEyNSAyMi43NTcwMTMsMjguMDQ4MjYxIDI3Ljg1NDQ1MSwyMi40MjA5MzYgMjcuNDcyNjU2LDE1Ljg0MTc5NyAyNy4xMDY4MjQsOS41Mzc2MDI1IDIyLjgxMDE2LDQuNjIzMDQ2OSAxNi40ODYzMjgsNC42MjMwNDY5IFogbSAwLDAuMTk1MzEyNSBjIDYuMjIyOTIsMCAxMC40Mjk5NDYsNC44MTMwMTM4IDEwLjc5MTAxNiwxMS4wMzUxNTY2IDAuMzc1NjEzLDYuNDcyNjE1IC00LjYxNzU4NCwxMS45ODY3MiAtMTEuMjU5NzY2LDExLjI2MzY3MiBDIDEwLjM1ODY4NSwyNi41MDExODYgNS4xOTE4MzgxLDIxLjcyNzk4NSA0LjczODI4MTIsMTUuODM5ODQ0IDQuNDk5OTIwMSwxMi43NDUyNjIgNS44NzY3MzE1LDkuOTk0OTc3OCA4LjA5NTcwMzEsOC4wMTE3MTg4IDEwLjMxNDY3NSw2LjAyODQ1OTUgMTMuMzc0ODksNC44MTgzNTk0IDE2LjQ4NjMyOCw0LjgxODM1OTQgWiBtIC0wLjA2ODM2LDEuNDE2MDE1NiBjIC0yLjcxMzg3NywwIC01LjM3NjExOCwxLjA1MjUxNjQgLTcuMzEyNTAwMiwyLjc4MzIwMzEgLTEuOTM2MzgyOCwxLjczMDY4NjkgLTMuMTQ2NTUxNyw0LjE0NTMxMTkgLTIuOTM3NSw2Ljg1OTM3NDkgMC4zOTYyNjk5LDUuMTQ0NDMgNC44ODk0NDQyLDkuMjk0NDI5IDkuODI4MTI1Miw5LjgzMjAzMSA1LjgwMTc0OSwwLjYzMTU2MiAxMC4xNzkyNTcsLTQuMTk4ODI4IDkuODUxNTYyLC05Ljg0NTcwMyBDIDI1LjUzMzc1LDEwLjQ1MzgyMiAyMS44NDU2MTYsNi4yMzQzNzUgMTYuNDE3OTc0LDYuMjM0Mzc1IFogbSAwLDAuMTk1MzEyNSBjIDUuMzI2NzMsMCA4LjkyNTIyNiw0LjExNzkwNTUgOS4yMzQzNzUsOS40NDUzMTI1IDAuMzIxNTEzLDUuNTQwMzUxIC0zLjk0OTgwMSwxMC4yNTk0NzQgLTkuNjM0NzY2LDkuNjQwNjI1IEMgMTEuMTczODc1LDI0Ljk4ODM2MiA2Ljc0OTUxNDMsMjAuOTAwODE0IDYuMzYxMzI4MSwxNS44NjEzMjggNi4xNTczODMxLDEzLjIxMzU2MyA3LjMzNTA0MzEsMTAuODU5NjgyIDkuMjM0Mzc1LDkuMTYyMTA5NCAxMS4xMzM3MDcsNy40NjQ1MzcyIDEzLjc1NDYyOCw2LjQyOTY4NzUgMTYuNDE3OTY5LDYuNDI5Njg3NSBaIG0gLTAuMDY4MzYsMS40MTYwMTU2IGMgLTIuMjY1Nzc1LDAgLTQuNDg4NzI5LDAuODc5MjE5NiAtNi4xMDU0NjgsMi4zMjQyMTg5IC0xLjYxNjc0MDgsMS40NDQ5OTkgLTIuNjI3NzYwNywzLjQ2MTI2OSAtMi40NTMxMjU0LDUuNzI4NTE2IDAuMzMwODk4Niw0LjI5NTc2OCA0LjA4MTU5NjQsNy43NjAxMiA4LjIwNTA3ODQsOC4yMDg5ODQgNC44NDQ1MjUsMC41MjczNiA4LjUwMDE1NiwtMy41MDYwOTcgOC4yMjY1NjIsLTguMjIwNzAzIEMgMjMuOTYwNjcyLDExLjM3MTk5NiAyMC44ODEwNiw3Ljg0NTcwMzEgMTYuMzQ5NjE0LDcuODQ1NzAzMSBaIG0gMCwwLjE5NTMxMjUgYyA0LjQzMDUzNCwwIDcuNDIyNDYxLDMuNDIyNzk5NCA3LjY3OTY4OCw3Ljg1NTQ2ODQgMC4yNjc0MTIsNC42MDgwODIgLTMuMjgzOTc4LDguNTMyMjI2IC04LjAxMTcxOSw4LjAxNzU3OCBDIDExLjk4OTA3NSwyMy40NzU1MzggOC4zMDcxODk5LDIwLjA3NTU5MyA3Ljk4NDM3NSwxNS44ODQ3NjYgNy44MTQ4NDYzLDEzLjY4MzgxOSA4Ljc5NTMxMDUsMTEuNzI2MzM4IDEwLjM3NSwxMC4zMTQ0NTMgMTEuOTU0Njg5LDguOTAyNTY4OSAxNC4xMzQzNyw4LjA0MTAxNTYgMTYuMzQ5NjA5LDguMDQxMDE1NiBaIG0gLTAuMDY4MzYsMS40MTYwMTU2IGMgLTEuODE3NjcyLDAgLTMuNjAxMzQyLDAuNzAzOTY4OCAtNC44OTg0MzgsMS44NjMyODA4IC0xLjI5NzA5NSwxLjE1OTMxIC0yLjEwODk2ODMsMi43NzkxODUgLTEuOTY4NzQ5NSw0LjU5OTYxIDAuMjY1NTI2OSwzLjQ0NzExMSAzLjI3Mzc1MDUsNi4yMjU4MTMgNi41ODIwMzE1LDYuNTg1OTM3IDMuODg3Mjk1LDAuNDIzMTYgNi44MjMwMDgsLTIuODE1MzE4IDYuNjAzNTE1LC02LjU5NzY1NiBDIDIyLjM4OTU0MSwxMi4yODgyMjIgMTkuOTE2NDk1LDkuNDU3MDMxMSAxNi4yODEyNSw5LjQ1NzAzMTIgWiBtIDAsMC4xOTUzMTI2IGMgMy41MzQzMzMsMCA1LjkxNzc0MiwyLjcyNzY5NjIgNi4xMjMwNDcsNi4yNjU2MjUyIDAuMjEzMzExLDMuNjc1ODE0IC0yLjYxNjIwOCw2LjgwMzAyNSAtNi4zODY3MTksNi4zOTI1NzggLTMuMjEzMjk4LC0wLjM0OTc4NSAtNi4xNTA3NTk3LC0zLjA2MjEzIC02LjQwODIwMywtNi40MDQyOTcgLTAuMTM1MTEyMiwtMS43NTQxMjcgMC42NDQyNTIsLTMuMzEzMjU3IDEuOTA0Mjk3LC00LjQzOTQ1MyAxLjI2MDA0NSwtMS4xMjYxOTYgMy4wMDA0NDEsLTEuODE0NDUzMyA0Ljc2NzU3OCwtMS44MTQ0NTMyIHogbSAtMC4wNzAzMSwxLjQxNjAxNTIgYyAtMS4zNjk1NzIsMCAtMi43MTIsMC41MzA2NzUgLTMuNjg5NDU0LDEuNDA0Mjk3IC0wLjk3NzQ1MywwLjg3MzYyMiAtMS41OTAxNzcsMi4wOTUxNDUgLTEuNDg0Mzc1LDMuNDY4NzUgMC4yMDAxNTYsMi41OTg0NTIgMi40NjU5LDQuNjg5NTUxIDQuOTU4OTg1LDQuOTYwOTM4IDIuOTMwMDcsMC4zMTg5NTggNS4xNDM5MDgsLTIuMTIyNTg3IDQuOTc4NTE1LC00Ljk3MjY1NiAtMC4xNTgxNDUsLTIuNzI1MjQ0IC0yLjAyNDYyMiwtNC44NjEzMjkgLTQuNzYzNjcxLC00Ljg2MTMyOSB6IG0gMCwwLjE5NTMxMyBjIDIuNjM4MTM1LDAgNC40MTQ5NzUsMi4wMzQ1NDQgNC41NjgzNTksNC42Nzc3MzQgMC4xNTkyMTEsMi43NDM1NDYgLTEuOTUwMzg2LDUuMDczODI0IC00Ljc2MzY3Miw0Ljc2NzU3OCAtMi4zOTgxMDIsLTAuMjYxMDQ3IC00LjU5MTEzMSwtMi4yODc3NDEgLTQuNzgzMjAzLC00Ljc4MTI1IC0wLjEwMDY5NiwtMS4zMDczMDggMC40Nzk1MTksLTIuNDcwMDM5IDEuNDE5OTIyLC0zLjMxMDU0NiAwLjk0MDQwMywtMC44NDA1MDggMi4yMzk1NTcsLTEuMzUzNTE2IDMuNTU4NTk0LC0xLjM1MzUxNiB6IG0gLTAuMDY4MzYsMS40MTYwMTYgYyAtMC45MjE0NzIsMCAtMS44MjI2NTcsMC4zNTU0MjUgLTIuNDgwNDY5LDAuOTQzMzU5IC0wLjY1NzgxMSwwLjU4NzkzNCAtMS4wNzMzMzksMS40MTUwMSAtMS4wMDE5NTMsMi4zNDE3OTcgMC4xMzQ3ODUsMS43NDk3OTIgMS42NTYwOTUsMy4xNTMyOTEgMy4zMzM5ODUsMy4zMzU5MzcgMS45NzI4NDYsMC4yMTQ3NTkgMy40NjY3NiwtMS40MzE4MDkgMy4zNTU0NjgsLTMuMzQ5NjA5IC0wLjEwNjIyNCwtMS44MzA1MDMgLTEuMzY0MTc3LC0zLjI3MTQ4NyAtMy4yMDcwMzEsLTMuMjcxNDg0IHogbSAwLDAuMTk1MzEyIGMgMS43NDE5NDIsMCAyLjkxMjIwOSwxLjMzOTQ0IDMuMDEzNjcyLDMuMDg3ODkxIDAuMTA1MTEsMS44MTEyNzYgLTEuMjg0NTYyLDMuMzQ2NTc3IC0zLjE0MDYyNSwzLjE0NDUzMSAtMS41ODI5MDcsLTAuMTcyMzA3IC0zLjAzMzQ1NSwtMS41MTMzNTUgLTMuMTYwMTU2LC0zLjE1ODIwMyAtMC4wNjYyOCwtMC44NjA0OSAwLjMxNDc4NSwtMS42MjQ4NjggMC45MzU1NDcsLTIuMTc5Njg4IDAuNjIwNzQ5LC0wLjU1NDgxOSAxLjQ4MDYyLC0wLjg5NDUzMSAyLjM1MTU1NiwtMC44OTQ1MzEgeiBtIC0wLjA2ODM2LDEuNDE2MDE2IGMgLTAuNDczMzY5LDAgLTAuOTM1MjcxLDAuMTgyMTI5IC0xLjI3MzQzOCwwLjQ4NDM3NSAtMC4zMzgxNjcsMC4zMDIyNDYgLTAuNTU0NTQ2LDAuNzMwOTY5IC0wLjUxNzU3OCwxLjIxMDkzNyAwLjA2OTQxLDAuOTAxMTMzIDAuODQ4MjQ5LDEuNjE4OTgxIDEuNzEwOTM4LDEuNzEyODkxIDEuMDE1NjE2LDAuMTEwNTU3IDEuNzg5NjE0LC0wLjc0MTAzMSAxLjczMjQyMSwtMS43MjY1NjMgLTAuMDU0MywtMC45MzU3NjYgLTAuNzA1NjkxLC0xLjY4MTY0IC0xLjY1MjM0MywtMS42ODE2NCB6IG0gMCwwLjE5NTMxMiBjIDAuODQ1NzQsMCAxLjQwNzQ5LDAuNjQ0MzMzIDEuNDU3MDMxLDEuNDk4MDQ3IDAuMDUxMDEsMC44NzkwMDggLTAuNjE2NzkzLDEuNjE5MzI5IC0xLjUxNTYyNSwxLjUyMTQ4NCAtMC43Njc3MDYsLTAuMDgzNTcgLTEuNDc1NzgsLTAuNzM4OTY3IC0xLjUzNzEwOSwtMS41MzUxNTYgLTAuMDMxODYsLTAuNDEzNjcxIDAuMTUwMDU1LC0wLjc3OTY5NyAwLjQ1MTE3MiwtMS4wNDg4MjggMC4zMDExMTYsLTAuMjY5MTMxIDAuNzIxNjk4LC0wLjQzNTU0NyAxLjE0NDUzMSwtMC40MzU1NDcgeiIgLz48L3N2Zz4=');}.icon-house-simple{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMTcuOSwxMTAuMWwtODAtODBhMTQsMTQsMCwwLDAtMTkuOCwwbC04MCw4MEExMy45MiwxMy45MiwwLDAsMCwzNCwxMjB2OTZhNiw2LDAsMCwwLDYsNkgyMTZhNiw2LDAsMCwwLDYtNlYxMjBBMTMuOTIsMTMuOTIsMCwwLDAsMjE3LjksMTEwLjFaTTIxMCwyMTBINDZWMTIwYTIsMiwwLDAsMSwuNTgtMS40Mmw4MC04MGEyLDIsMCwwLDEsMi44NCwwbDgwLDgwQTIsMiwwLDAsMSwyMTAsMTIwWiIvPjwvc3ZnPg==');}.icon-house{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMTcuOSwxMTAuMWwtODAtODBhMTQsMTQsMCwwLDAtMTkuOCwwbC04MCw4MEExMy45MiwxMy45MiwwLDAsMCwzNCwxMjB2OTZhNiw2LDAsMCwwLDYsNmg2NGE2LDYsMCwwLDAsNi02VjE1OGgzNnY1OGE2LDYsMCwwLDAsNiw2aDY0YTYsNiwwLDAsMCw2LTZWMTIwQTEzLjkyLDEzLjkyLDAsMCwwLDIxNy45LDExMC4xWk0yMTAsMjEwSDE1OFYxNTJhNiw2LDAsMCwwLTYtNkgxMDRhNiw2LDAsMCwwLTYsNnY1OEg0NlYxMjBhMiwyLDAsMCwxLC41OC0xLjQybDgwLTgwYTIsMiwwLDAsMSwyLjg0LDBsODAsODBBMiwyLDAsMCwxLDIxMCwxMjBaIi8+PC9zdmc+');}.icon-x-circle{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xNjQuMjQsMTAwLjI0LDEzNi40OCwxMjhsMjcuNzYsMjcuNzZhNiw2LDAsMSwxLTguNDgsOC40OEwxMjgsMTM2LjQ4bC0yNy43NiwyNy43NmE2LDYsMCwwLDEtOC40OC04LjQ4TDExOS41MiwxMjgsOTEuNzYsMTAwLjI0YTYsNiwwLDAsMSw4LjQ4LTguNDhMMTI4LDExOS41MmwyNy43Ni0yNy43NmE2LDYsMCwwLDEsOC40OCw4LjQ4Wk0yMzAsMTI4QTEwMiwxMDIsMCwxLDEsMTI4LDI2LDEwMi4xMiwxMDIuMTIsMCwwLDEsMjMwLDEyOFptLTEyLDBhOTAsOTAsMCwxLDAtOTAsOTBBOTAuMSw5MC4xLDAsMCwwLDIxOCwxMjhaIi8+PC9zdmc+');}.icon-plus-square{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMDgsMzRINDhBMTQsMTQsMCwwLDAsMzQsNDhWMjA4YTE0LDE0LDAsMCwwLDE0LDE0SDIwOGExNCwxNCwwLDAsMCwxNC0xNFY0OEExNCwxNCwwLDAsMCwyMDgsMzRabTIsMTc0YTIsMiwwLDAsMS0yLDJINDhhMiwyLDAsMCwxLTItMlY0OGEyLDIsMCwwLDEsMi0ySDIwOGEyLDIsMCwwLDEsMiwyWm0tMzYtODBhNiw2LDAsMCwxLTYsNkgxMzR2MzRhNiw2LDAsMCwxLTEyLDBWMTM0SDg4YTYsNiwwLDAsMSwwLTEyaDM0Vjg4YTYsNiwwLDAsMSwxMiwwdjM0aDM0QTYsNiwwLDAsMSwxNzQsMTI4WiIvPjwvc3ZnPg==');}.icon-infinity{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yNDYsMTI4YTU0LDU0LDAsMCwxLTkyLjE4LDM4LjE4LDMuMDcsMy4wNywwLDAsMS0uMjUtLjI2bC02MC02Ny43NGE0Miw0MiwwLDEsMCwwLDU5LjY0bDguNTctOS42N2E2LDYsMCwxLDEsOSw4bC04LjY5LDkuODFhMy4wNywzLjA3LDAsMCwxLS4yNS4yNiw1NCw1NCwwLDEsMSwwLTc2LjM2LDMuMDcsMy4wNywwLDAsMSwuMjUuMjZsNjAsNjcuNzRhNDIsNDIsMCwxLDAsMC01OS42NGwtOC41Nyw5LjY3YTYsNiwwLDEsMS05LThsOC42OS05LjgxYTMuMDcsMy4wNywwLDAsMSwuMjUtLjI2QTU0LDU0LDAsMCwxLDI0NiwxMjhaIi8+PC9zdmc+');}.icon-arrow-counter-clockwise{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjIsMTI4YTk0LDk0LDAsMCwxLTkyLjc0LDk0SDEyOGE5My40Myw5My40MywwLDAsMS02NC41LTI1LjY1LDYsNiwwLDEsMSw4LjI0LTguNzJBODIsODIsMCwxLDAsNzAsNzBsLS4xOS4xOUwzOS40NCw5OEg3MmE2LDYsMCwwLDEsMCwxMkgyNGE2LDYsMCwwLDEtNi02VjU2YTYsNiwwLDAsMSwxMiwwVjkwLjM0TDYxLjYzLDYxLjRBOTQsOTQsMCwwLDEsMjIyLDEyOFoiLz48L3N2Zz4=');}.icon-magnifying-glass{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjguMjQsMjE5Ljc2bC01MS4zOC01MS4zOGE4Ni4xNSw4Ni4xNSwwLDEsMC04LjQ4LDguNDhsNTEuMzgsNTEuMzhhNiw2LDAsMCwwLDguNDgtOC40OFpNMzgsMTEyYTc0LDc0LDAsMSwxLDc0LDc0QTc0LjA5LDc0LjA5LDAsMCwxLDM4LDExMloiLz48L3N2Zz4=');}.icon-floppy-disk{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMTcuOSw3My40MiwxODIuNTgsMzguMWExMy45LDEzLjksMCwwLDAtOS44OS00LjFINDhBMTQsMTQsMCwwLDAsMzQsNDhWMjA4YTE0LDE0LDAsMCwwLDE0LDE0SDIwOGExNCwxNCwwLDAsMCwxNC0xNFY4My4zMUExMy45LDEzLjksMCwwLDAsMjE3LjksNzMuNDJaTTE3MCwyMTBIODZWMTUyYTIsMiwwLDAsMSwyLTJoODBhMiwyLDAsMCwxLDIsMlptNDAtMmEyLDIsMCwwLDEtMiwySDE4MlYxNTJhMTQsMTQsMCwwLDAtMTQtMTRIODhhMTQsMTQsMCwwLDAtMTQsMTR2NThINDhhMiwyLDAsMCwxLTItMlY0OGEyLDIsMCwwLDEsMi0ySDE3Mi42OWEyLDIsMCwwLDEsMS40MS41OEwyMDkuNDIsODEuOWEyLDIsMCwwLDEsLjU4LDEuNDFaTTE1OCw3MmE2LDYsMCwwLDEtNiw2SDk2YTYsNiwwLDAsMSwwLTEyaDU2QTYsNiwwLDAsMSwxNTgsNzJaIi8+PC9zdmc+');}.icon-calendar{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMDgsMzRIMTgyVjI0YTYsNiwwLDAsMC0xMiwwVjM0SDg2VjI0YTYsNiwwLDAsMC0xMiwwVjM0SDQ4QTE0LDE0LDAsMCwwLDM0LDQ4VjIwOGExNCwxNCwwLDAsMCwxNCwxNEgyMDhhMTQsMTQsMCwwLDAsMTQtMTRWNDhBMTQsMTQsMCwwLDAsMjA4LDM0Wk00OCw0Nkg3NFY1NmE2LDYsMCwwLDAsMTIsMFY0Nmg4NFY1NmE2LDYsMCwwLDAsMTIsMFY0NmgyNmEyLDIsMCwwLDEsMiwyVjgySDQ2VjQ4QTIsMiwwLDAsMSw0OCw0NlpNMjA4LDIxMEg0OGEyLDIsMCwwLDEtMi0yVjk0SDIxMFYyMDhBMiwyLDAsMCwxLDIwOCwyMTBabS05OC05MHY2NGE2LDYsMCwwLDEtMTIsMFYxMjkuNzFsLTcuMzIsMy42NmE2LDYsMCwxLDEtNS4zNi0xMC43NGwxNi04QTYsNiwwLDAsMSwxMTAsMTIwWm01OS41NywyOS4yNUwxNDgsMTc4aDIwYTYsNiwwLDAsMSwwLDEySDEzNmE2LDYsMCwwLDEtNC44LTkuNkwxNjAsMTQyYTEwLDEwLDAsMSwwLTE2LjY1LTExQTYsNiwwLDEsMSwxMzMsMTI1YTIyLDIyLDAsMSwxLDM2LjYyLDI0LjI2WiIvPjwvc3ZnPg==');}.icon-clock-clockwise{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xMzQsODB2NDQuNmwzNy4wOSwyMi4yNWE2LDYsMCwwLDEtNi4xOCwxMC4zbC00MC0yNEE2LDYsMCwwLDEsMTIyLDEyOFY4MGE2LDYsMCwwLDEsMTIsMFptOTAtMjJhNiw2LDAsMCwwLTYsNlY4Ny4zNmMtNy40OC04LjgzLTE0Ljk0LTE3LjEzLTIzLjUzLTI1LjgzYTk0LDk0LDAsMSwwLTEuOTUsMTM0LjgzLDYsNiwwLDAsMC04LjI0LTguNzJBODIsODIsMCwxLDEsMTg2LDcwYzkuMjQsOS4zNiwxNy4xOCwxOC4zLDI1LjMxLDI4SDE4NGE2LDYsMCwwLDAsMCwxMmg0MGE2LDYsMCwwLDAsNi02VjY0QTYsNiwwLDAsMCwyMjQsNThaIi8+PC9zdmc+');}.icon-shuffle{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMzYuMjQsMTc5Ljc2YTYsNiwwLDAsMSwwLDguNDhsLTI0LDI0YTYsNiwwLDAsMS04LjQ4LTguNDhMMjE3LjUyLDE5MEgyMDAuOTRhNzAuMTYsNzAuMTYsMCwwLDEtNTctMjkuMzFsLTQxLjcxLTU4LjRBNTguMTEsNTguMTEsMCwwLDAsNTUuMDYsNzhIMzJhNiw2LDAsMCwxLDAtMTJINTUuMDZhNzAuMTYsNzAuMTYsMCwwLDEsNTcsMjkuMzFsNDEuNzEsNTguNEE1OC4xMSw1OC4xMSwwLDAsMCwyMDAuOTQsMTc4aDE2LjU4bC0xMy43Ni0xMy43NmE2LDYsMCwwLDEsOC40OC04LjQ4Wm0tOTIuMDYtNzQuNDFhNS45MSw1LjkxLDAsMCwwLDMuNDgsMS4xMiw2LDYsMCwwLDAsNC44OS0yLjUxbDEuMTktMS42N0E1OC4xMSw1OC4xMSwwLDAsMSwyMDAuOTQsNzhoMTYuNThMMjAzLjc2LDkxLjc2YTYsNiwwLDEsMCw4LjQ4LDguNDhsMjQtMjRhNiw2LDAsMCwwLDAtOC40OGwtMjQtMjRhNiw2LDAsMCwwLTguNDgsOC40OEwyMTcuNTIsNjZIMjAwLjk0YTcwLjE2LDcwLjE2LDAsMCwwLTU3LDI5LjMxTDE0Mi43OCw5N0E2LDYsMCwwLDAsMTQ0LjE4LDEwNS4zNVptLTMyLjM2LDQ1LjNhNiw2LDAsMCwwLTguMzcsMS4zOWwtMS4xOSwxLjY3QTU4LjExLDU4LjExLDAsMCwxLDU1LjA2LDE3OEgzMmE2LDYsMCwwLDAsMCwxMkg1NS4wNmE3MC4xNiw3MC4xNiwwLDAsMCw1Ny0yOS4zMWwxLjE5LTEuNjdBNiw2LDAsMCwwLDExMS44MiwxNTAuNjVaIi8+PC9zdmc+');}.icon-sort-descending{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik00MiwxMjhhNiw2LDAsMCwxLDYtNmg3MmE2LDYsMCwwLDEsMCwxMkg0OEE2LDYsMCwwLDEsNDIsMTI4Wm02LTU4aDU2YTYsNiwwLDAsMCwwLTEySDQ4YTYsNiwwLDAsMCwwLDEyWk0xODQsMTg2SDQ4YTYsNiwwLDAsMCwwLDEySDE4NGE2LDYsMCwwLDAsMC0xMlpNMjI4LjI0LDgzLjc2bC00MC00MGE2LDYsMCwwLDAtOC40OCwwbC00MCw0MGE2LDYsMCwwLDAsOC40OCw4LjQ4TDE3OCw2Mi40OVYxNDRhNiw2LDAsMCwwLDEyLDBWNjIuNDlsMjkuNzYsMjkuNzVhNiw2LDAsMCwwLDguNDgtOC40OFoiLz48L3N2Zz4=');}.icon-sort-ascending{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xMjYsMTI4YTYsNiwwLDAsMS02LDZINDhhNiw2LDAsMCwxLDAtMTJoNzJBNiw2LDAsMCwxLDEyNiwxMjhaTTQ4LDcwSDE4NGE2LDYsMCwwLDAsMC0xMkg0OGE2LDYsMCwwLDAsMCwxMlptNTYsMTE2SDQ4YTYsNiwwLDAsMCwwLDEyaDU2YTYsNiwwLDAsMCwwLTEyWm0xMjQuMjQtMjIuMjRhNiw2LDAsMCwwLTguNDgsMEwxOTAsMTkzLjUxVjExMmE2LDYsMCwwLDAtMTIsMHY4MS41MWwtMjkuNzYtMjkuNzVhNiw2LDAsMCwwLTguNDgsOC40OGw0MCw0MGE2LDYsMCwwLDAsOC40OCwwbDQwLTQwQTYsNiwwLDAsMCwyMjguMjQsMTYzLjc2WiIvPjwvc3ZnPg==');}.icon-arrow-elbow-left-down{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMzgsNzJhNiw2LDAsMCwxLTYsNkg5NFYyMDEuNTFsMzcuNzYtMzcuNzVhNiw2LDAsMCwxLDguNDgsOC40OGwtNDgsNDhhNiw2LDAsMCwxLTguNDgsMGwtNDgtNDhhNiw2LDAsMCwxLDguNDgtOC40OEw4MiwyMDEuNTFWNzJhNiw2LDAsMCwxLDYtNkgyMzJBNiw2LDAsMCwxLDIzOCw3MloiLz48L3N2Zz4=');}.icon-arrow-elbow-right-down{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjguMjQsMTY0LjI0bC00OCw0OGE2LDYsMCwwLDEtOC40OCwwbC00OC00OGE2LDYsMCwxLDEsOC40OC04LjQ4TDE3MCwxOTMuNTFWNzBIMzJhNiw2LDAsMCwxLDAtMTJIMTc2YTYsNiwwLDAsMSw2LDZWMTkzLjUxbDM3Ljc2LTM3Ljc1YTYsNiwwLDAsMSw4LjQ4LDguNDhaIi8+PC9zdmc+');}.icon-caret-right{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xODAuMjQsMTMyLjI0bC04MCw4MGE2LDYsMCwwLDEtOC40OC04LjQ4TDE2Ny41MSwxMjgsOTEuNzYsNTIuMjRhNiw2LDAsMCwxLDguNDgtOC40OGw4MCw4MEE2LDYsMCwwLDEsMTgwLjI0LDEzMi4yNFoiLz48L3N2Zz4=');}.icon-heart{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xNzgsNDJjLTIxLDAtMzkuMjYsOS40Ny01MCwyNS4zNEMxMTcuMjYsNTEuNDcsOTksNDIsNzgsNDJhNjAuMDcsNjAuMDcsMCwwLDAtNjAsNjBjMCwyOS4yLDE4LjIsNTkuNTksNTQuMSw5MC4zMWEzMzQuNjgsMzM0LjY4LDAsMCwwLDUzLjA2LDM3LDYsNiwwLDAsMCw1LjY4LDAsMzM0LjY4LDMzNC42OCwwLDAsMCw1My4wNi0zN0MyMTkuOCwxNjEuNTksMjM4LDEzMS4yLDIzOCwxMDJBNjAuMDcsNjAuMDcsMCwwLDAsMTc4LDQyWk0xMjgsMjE3LjExQzExMS41OSwyMDcuNjQsMzAsMTU3LjcyLDMwLDEwMkE0OC4wNSw0OC4wNSwwLDAsMSw3OCw1NGMyMC4yOCwwLDM3LjMxLDEwLjgzLDQ0LjQ1LDI4LjI3YTYsNiwwLDAsMCwxMS4xLDBDMTQwLjY5LDY0LjgzLDE1Ny43Miw1NCwxNzgsNTRhNDguMDUsNDguMDUsMCwwLDEsNDgsNDhDMjI2LDE1Ny43MiwxNDQuNDEsMjA3LjY0LDEyOCwyMTcuMTFaIi8+PC9zdmc+');}.icon-dots-three{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xMzgsMTI4YTEwLDEwLDAsMSwxLTEwLTEwQTEwLDEwLDAsMCwxLDEzOCwxMjhaTTYwLDExOGExMCwxMCwwLDEsMCwxMCwxMEExMCwxMCwwLDAsMCw2MCwxMThabTEzNiwwYTEwLDEwLDAsMSwwLDEwLDEwQTEwLDEwLDAsMCwwLDE5NiwxMThaIi8+PC9zdmc+');}.icon-star-half-fi{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMzkuMTgsOTcuMjZBMTYuMzgsMTYuMzgsMCwwLDAsMjI0LjkyLDg2bC01OS00Ljc2TDE0My4xNCwyNi4xNWExNi4zNiwxNi4zNiwwLDAsMC0zMC4yNywwTDkwLjExLDgxLjIzLDMxLjA4LDg2YTE2LjQ2LDE2LjQ2LDAsMCwwLTkuMzcsMjguODZsNDUsMzguODNMNTMsMjExLjc1YTE2LjQsMTYuNCwwLDAsMCwyNC41LDE3LjgyTDEyOCwxOTguNDlsNTAuNTMsMzEuMDhBMTYuNCwxNi40LDAsMCwwLDIwMywyMTEuNzVsLTEzLjc2LTU4LjA3LDQ1LTM4LjgzQTE2LjQzLDE2LjQzLDAsMCwwLDIzOS4xOCw5Ny4yNlptLTE1LjM0LDUuNDctNDguNyw0MmE4LDgsMCwwLDAtMi41Niw3LjkxbDE0Ljg4LDYyLjhhLjM3LjM3LDAsMCwxLS4xNy40OGMtLjE4LjE0LS4yMy4xMS0uMzgsMGwtNTQuNzItMzMuNjVBOCw4LDAsMCwwLDEyOCwxODEuMVYzMmMuMjQsMCwuMjcuMDguMzUuMjZMMTUzLDkxLjg2YTgsOCwwLDAsMCw2Ljc1LDQuOTJsNjMuOTEsNS4xNmMuMTYsMCwuMjUsMCwuMzQuMjlTMjI0LDEwMi42MywyMjMuODQsMTAyLjczWiIvPjwvc3ZnPg==');}.icon-star-fi{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMzQuMjksMTE0Ljg1bC00NSwzOC44M0wyMDMsMjExLjc1YTE2LjQsMTYuNCwwLDAsMS0yNC41LDE3LjgyTDEyOCwxOTguNDksNzcuNDcsMjI5LjU3QTE2LjQsMTYuNCwwLDAsMSw1MywyMTEuNzVsMTMuNzYtNTguMDctNDUtMzguODNBMTYuNDYsMTYuNDYsMCwwLDEsMzEuMDgsODZsNTktNC43NiwyMi43Ni01NS4wOGExNi4zNiwxNi4zNiwwLDAsMSwzMC4yNywwbDIyLjc1LDU1LjA4LDU5LDQuNzZhMTYuNDYsMTYuNDYsMCwwLDEsOS4zNywyOC44NloiLz48L3N2Zz4=');}.icon-heart-fi{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yNDAsMTAyYzAsNzAtMTAzLjc5LDEyNi42Ni0xMDguMjEsMTI5YTgsOCwwLDAsMS03LjU4LDBDMTE5Ljc5LDIyOC42NiwxNiwxNzIsMTYsMTAyQTYyLjA3LDYyLjA3LDAsMCwxLDc4LDQwYzIwLjY1LDAsMzguNzMsOC44OCw1MCwyMy44OUMxMzkuMjcsNDguODgsMTU3LjM1LDQwLDE3OCw0MEE2Mi4wNyw2Mi4wNywwLDAsMSwyNDAsMTAyWiIvPjwvc3ZnPg==');}
.icon-google-logo{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjIsMTI4YTk0LDk0LDAsMSwxLTIxLjQ5LTU5LjgyLDYsNiwwLDEsMS05LjI1LDcuNjRBODIsODIsMCwxLDAsMjA5Ljc4LDEzNEgxMjhhNiw2LDAsMCwxLDAtMTJoODhBNiw2LDAsMCwxLDIyMiwxMjhaIi8+PC9zdmc+');}.icon-apple-logo{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMTkuNCwxNjcuODRDMjAxLjcxLDE1NS42OSwxOTgsMTM1LjEyLDE5OCwxMjBjMC0xOC40MiwxMy44Ni0zNC4yOSwyMi4xMi00Mi4xMmE2LDYsMCwwLDAsMC04LjcxQzIwOCw1Ny43LDE4Ny4wNyw1MCwxNjgsNTBhNzAuMjMsNzAuMjMsMCwwLDAtNDAsMTIuNTUsNjkuNiw2OS42LDAsMCwwLTg5LjMxLDguMDhBNzIuNjMsNzIuNjMsMCwwLDAsMTgsMTIzLjM1YTEyNS4xMSwxMjUuMTEsMCwwLDAsMzkuNTMsODguMzNBMzcuODUsMzcuODUsMCwwLDAsODMuNiwyMjJoODcuN0EzNy44MywzNy44MywwLDAsMCwxOTksMjEwLjA3YTEyMi42LDEyMi42LDAsMCwwLDE3LjU0LTI0LjJjNi41NS0xMiw1Ljc3LTEzLjc1LDUtMTUuNDhBNi4wNyw2LjA3LDAsMCwwLDIxOS40LDE2Ny44NFptLTI5LjIzLDM0QTI1LjgyLDI1LjgyLDAsMCwxLDE3MS4zLDIxMEg4My42QTI1Ljg1LDI1Ljg1LDAsMCwxLDY1Ljc4LDIwMywxMTMuMjEsMTEzLjIxLDAsMCwxLDMwLDEyM2E2MC41NSw2MC41NSwwLDAsMSwxNy4yMS00NEE1Ni44Miw1Ni44MiwwLDAsMSw4OCw2MmguODFhNTcuMzUsNTcuMzUsMCwwLDEsMzUuNDQsMTIuNzEsNiw2LDAsMCwwLDcuNSwwQTU3LjM5LDU3LjM5LDAsMCwxLDE2OCw2MmMxMy44OSwwLDI4LjgxLDQuNjgsMzkuMTEsMTItOS40NCwxMC4xNC0yMS4xLDI2LjU5LTIxLjEsNDYsMCwyMy43OCw3LjgxLDQyLjYsMjIuNjYsNTQuNzdBMTA3LjMzLDEwNy4zMywwLDAsMSwxOTAuMTcsMjAxLjg5Wm0tNjAtMTcxLjM5QTM4LDM4LDAsMCwxLDE2NywyaDFhNiw2LDAsMCwxLDAsMTJoLTFhMjYsMjYsMCwwLDAtMjUuMTgsMTkuNSw2LDYsMCwxLDEtMTEuNjItM1oiLz48L3N2Zz4=');}.icon-check-circle{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xNzIuMjQsOTkuNzZhNiw2LDAsMCwxLDAsOC40OGwtNTYsNTZhNiw2LDAsMCwxLTguNDgsMGwtMjQtMjRhNiw2LDAsMCwxLDguNDgtOC40OEwxMTIsMTUxLjUxbDUxLjc2LTUxLjc1QTYsNiwwLDAsMSwxNzIuMjQsOTkuNzZaTTIzMCwxMjhBMTAyLDEwMiwwLDEsMSwxMjgsMjYsMTAyLjEyLDEwMi4xMiwwLDAsMSwyMzAsMTI4Wm0tMTIsMGE5MCw5MCwwLDEsMC05MCw5MEE5MC4xLDkwLjEsMCwwLDAsMjE4LDEyOFoiLz48L3N2Zz4=');}.icon-cloud-slash{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik01Mi40NCwzNkE2LDYsMCwwLDAsNDMuNTYsNDRsNDAuMTgsNDQuMmMtLjQ1Ljg3LS45LDEuNzUtMS4zMiwyLjY0QTYyLDYyLDAsMSwwLDcyLDIxNGg4OGE4NS4yMyw4NS4yMywwLDAsMCwzMi4zNS02LjNMMjAzLjU2LDIyMGE2LDYsMCwwLDAsOC44OC04LjA4Wk0xNjAsMjAySDcyYTUwLDUwLDAsMSwxLDUuOS05OS42NEE4Ni4yNSw4Ni4yNSwwLDAsMCw3NCwxMjhhNiw2LDAsMCwwLDEyLDAsNzMuOTIsNzMuOTIsMCwwLDEsNi40NC0zMC4ybDkxLjIyLDEwMC4zNEE3My42NSw3My42NSwwLDAsMSwxNjAsMjAyWm04Ni03NGE4NS44NSw4NS44NSwwLDAsMS0yMS44NSw1Ny4yNyw2LDYsMCwwLDEtNC40NywyLDYsNiwwLDAsMS00LjQ3LTEwLDc0LDc0LDAsMCwwLTk5LTEwOC45Miw2LDYsMCwxLDEtNy4xMS05LjY3QTg2LDg2LDAsMCwxLDI0NiwxMjhaIi8+PC9zdmc+');}.icon-exclamation-mark{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xNDIsMjAwYTE0LDE0LDAsMSwxLTE0LTE0QTE0LDE0LDAsMCwxLDE0MiwyMDBabS0xNC00MmE2LDYsMCwwLDAsNi02VjQ4YTYsNiwwLDAsMC0xMiwwVjE1MkE2LDYsMCwwLDAsMTI4LDE1OFoiLz48L3N2Zz4=');}.icon-cloud-arrow-down{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yNDYsMTI4YTg1LjI3LDg1LjI3LDAsMCwxLTE3LjIsNTEuNiw2LDYsMCwxLDEtOS42LTcuMkE3NCw3NCwwLDEsMCw4NiwxMjhhNiw2LDAsMCwxLTEyLDAsODUuNTQsODUuNTQsMCwwLDEsMy45MS0yNS42NEE1MC42OCw1MC42OCwwLDAsMCw3MiwxMDJhNTAsNTAsMCwwLDAsMCwxMDBIOTZhNiw2LDAsMCwxLDAsMTJINzJBNjIsNjIsMCwxLDEsODIuNDMsOTAuODgsODYsODYsMCwwLDEsMjQ2LDEyOFptLTY2LjI0LDQzLjc2TDE1OCwxOTMuNTFWMTI4YTYsNiwwLDAsMC0xMiwwdjY1LjUxbC0yMS43Ni0yMS43NWE2LDYsMCwwLDAtOC40OCw4LjQ4bDMyLDMyYTYsNiwwLDAsMCw4LjQ4LDBsMzItMzJhNiw2LDAsMCwwLTguNDgtOC40OFoiLz48L3N2Zz4=');}.icon-caret-down{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMTIuMjQsMTAwLjI0bC04MCw4MGE2LDYsMCwwLDEtOC40OCwwbC04MC04MGE2LDYsMCwwLDEsOC40OC04LjQ4TDEyOCwxNjcuNTFsNzUuNzYtNzUuNzVhNiw2LDAsMCwxLDguNDgsOC40OFoiLz48L3N2Zz4=');}.icon-cloud-arrow-up{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xODguMjQsMTY0LjI0YTYsNiwwLDAsMS04LjQ4LDBMMTU4LDE0Mi40OVYyMDhhNiw2LDAsMCwxLTEyLDBWMTQyLjQ5bC0yMS43NiwyMS43NWE2LDYsMCwwLDEtOC40OC04LjQ4bDMyLTMyYTYsNiwwLDAsMSw4LjQ4LDBsMzIsMzJBNiw2LDAsMCwxLDE4OC4yNCwxNjQuMjRaTTE2MCw0MkE4Ni4xLDg2LjEsMCwwLDAsODIuNDMsOTAuODgsNjIsNjIsMCwxLDAsNzIsMjE0aDQwYTYsNiwwLDAsMCwwLTEySDcyYTUwLDUwLDAsMCwxLDAtMTAwLDUwLjY4LDUwLjY4LDAsMCwxLDUuOTEuMzZBODUuNTQsODUuNTQsMCwwLDAsNzQsMTI4YTYsNiwwLDAsMCwxMiwwLDc0LDc0LDAsMSwxLDEwMy42LDY3Ljg1LDYsNiwwLDAsMCw0LjgsMTFBODYsODYsMCwwLDAsMTYwLDQyWiIvPjwvc3ZnPg==');}.icon-cloud-check{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xNjAsNDJBODYuMTEsODYuMTEsMCwwLDAsODIuNDMsOTAuODgsNjIsNjIsMCwxLDAsNzIsMjE0aDg4YTg2LDg2LDAsMCwwLDAtMTcyWm0wLDE2MEg3MmE1MCw1MCwwLDAsMSwwLTEwMCw1MC42Nyw1MC42NywwLDAsMSw1LjkxLjM1QTg1LjYxLDg1LjYxLDAsMCwwLDc0LDEyOGE2LDYsMCwwLDAsMTIsMCw3NCw3NCwwLDEsMSw3NCw3NFptMzYuMjQtOTQuMjRhNiw2LDAsMCwxLDAsOC40OGwtNDgsNDhhNiw2LDAsMCwxLTguNDgsMGwtMjQtMjRhNiw2LDAsMCwxLDguNDgtOC40OEwxNDQsMTUxLjUxbDQzLjc2LTQzLjc1QTYsNiwwLDAsMSwxOTYuMjQsMTA3Ljc2WiIvPjwvc3ZnPg==');}.icon-cloud-warning{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xNjAsNDJBODYuMTEsODYuMTEsMCwwLDAsODIuNDMsOTAuODgsNjIsNjIsMCwxLDAsNzIsMjE0aDg4YTg2LDg2LDAsMCwwLDAtMTcyWm0wLDE2MEg3MmE1MCw1MCwwLDAsMSwwLTEwMCw1MC42Nyw1MC42NywwLDAsMSw1LjkxLjM1QTg1LjYxLDg1LjYxLDAsMCwwLDc0LDEyOGE2LDYsMCwwLDAsMTIsMCw3NCw3NCwwLDEsMSw3NCw3NFptLTYtNzRWODhhNiw2LDAsMCwxLDEyLDB2NDBhNiw2LDAsMCwxLTEyLDBabTE2LDM2YTEwLDEwLDAsMSwxLTEwLTEwQTEwLDEwLDAsMCwxLDE3MCwxNjRaIi8+PC9zdmc+');}.icon-syncing{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbDpzcGFjZT0icHJlc2VydmUiIHdpZHRoPSIzMiIgaGVpZ2h0PSIzMiIgZmlsbD0iY3VycmVudENvbG9yIiB2aWV3Qm94PSIwIDAgMjU2IDI1NiI+PHBhdGggaWQ9InJlZnJlc2giIGQ9Ik0xNjAuMDQ3IDEyMi44NzVhMzAuNzg0IDMwLjc4NCAwIDAgMC0yMS43NSA4Ljc5N2MtMi44NDIgMy4wMDMtLjQ2NyA0Ljk3MSAxLjMxMiAzLjE1NiAxMS4wNDMtMTAuNzg2IDI4LjcxLTEwLjY4IDM5LjYyNS4yMzRsNy4yMDMgNy4yMDRoLTEyLjg3NWMtMy4zNDcuMDA4LTMuMTY1IDMuODc1IDAgMy44NzVoMTYuMTFjMi4wNjIgMCAyLjU0LTEuNDE4IDIuNTYyLTQuOTdsLjA5NC0xNC45MjFjLjAyLTMuMjktMy40MzctMy4xNjUtMy40MzcgMHYxMi44NmwtNy4yMDMtNy4xODhhMzAuNzY4IDMwLjc2OCAwIDAgMC0yMS42NDEtOS4wNDd6bS0yOS41OTQgMzkuNzk3Yy0yLjA2MiAwLTIuNTI0IDEuNDAyLTIuNTQ3IDQuOTUzbC0uMDk0IDE0LjkyMmMtLjAyIDMuMjkgMy40MjIgMy4xNjQgMy40MjIgMHYtMTIuODZsNy4yMDMgNy4yMDRjMTEuOTU2IDExLjk1NSAzMS4zMTIgMTIuMDY0IDQzLjQwNy4yNSAyLjg0Mi0zLjAwMy40NTEtNC45ODgtMS4zMjgtMy4xNzItMTEuMDQzIDEwLjc4Ni0yOC43MSAxMC42OC0zOS42MjUtLjIzNWwtNy4xODgtNy4yMDNoMTIuODZjMy4zNDctLjAwOCAzLjE2NS0zLjg2IDAtMy44NmgtMTYuMTF6Ii8+PHBhdGggZD0iTTE2MCA0NGE4NC4xMSA4NC4xMSAwIDAgMC03Ni40MSA0OS4xMkE2MC43MSA2MC43MSAwIDAgMCA3MiA5MmE2MCA2MCAwIDAgMCAwIDEyMGg4OGE4NCA4NCAwIDAgMCAwLTE2OFptMCAxNjBINzJhNTIgNTIgMCAxIDEgOC41NS0xMDMuM0E4My42NiA4My42NiAwIDAgMCA3NiAxMjhhNCA0IDAgMCAwIDggMCA3NiA3NiAwIDEgMSA3NiA3NloiLz48L3N2Zz4=');}.icon-cloud-x{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xNjAsNDJBODYuMTEsODYuMTEsMCwwLDAsODIuNDMsOTAuODgsNjIsNjIsMCwxLDAsNzIsMjE0aDg4YTg2LDg2LDAsMCwwLDAtMTcyWm0wLDE2MEg3MmE1MCw1MCwwLDAsMSwwLTEwMCw1MC42Nyw1MC42NywwLDAsMSw1LjkxLjM1QTg1LjYxLDg1LjYxLDAsMCwwLDc0LDEyOGE2LDYsMCwwLDAsMTIsMCw3NCw3NCwwLDEsMSw3NCw3NFptMjguMjQtODUuNzZMMTY4LjQ4LDEzNmwxOS43NiwxOS43NmE2LDYsMCwxLDEtOC40OCw4LjQ4TDE2MCwxNDQuNDhsLTE5Ljc2LDE5Ljc2YTYsNiwwLDAsMS04LjQ4LTguNDhMMTUxLjUyLDEzNmwtMTkuNzYtMTkuNzZhNiw2LDAsMCwxLDguNDgtOC40OEwxNjAsMTI3LjUybDE5Ljc2LTE5Ljc2YTYsNiwwLDAsMSw4LjQ4LDguNDhaIi8+PC9zdmc+');}.icon-arrows-clockwise{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjIsNDhWOTZhNiw2LDAsMCwxLTYsNkgxNjhhNiw2LDAsMCwxLDAtMTJoMzMuNTJMMTgzLjQ3LDcyYTgxLjUxLDgxLjUxLDAsMCwwLTU3LjUzLTI0aC0uNDZBODEuNSw4MS41LDAsMCwwLDY4LjE5LDcxLjI4YTYsNiwwLDEsMS04LjM4LTguNTgsOTMuMzgsOTMuMzgsMCwwLDEsNjUuNjctMjYuNzZIMTI2YTkzLjQ1LDkzLjQ1LDAsMCwxLDY2LDI3LjUzbDE4LDE4VjQ4YTYsNiwwLDAsMSwxMiwwWk0xODcuODEsMTg0LjcyYTgxLjUsODEuNSwwLDAsMS01Ny4yOSwyMy4zNGgtLjQ2YTgxLjUxLDgxLjUxLDAsMCwxLTU3LjUzLTI0TDU0LjQ4LDE2Nkg4OGE2LDYsMCwwLDAsMC0xMkg0MGE2LDYsMCwwLDAtNiw2djQ4YTYsNiwwLDAsMCwxMiwwVjE3NC40OGwxOCwxOC4wNWE5My40NSw5My40NSwwLDAsMCw2NiwyNy41M2guNTJhOTMuMzgsOTMuMzgsMCwwLDAsNjUuNjctMjYuNzYsNiw2LDAsMSwwLTguMzgtOC41OFoiLz48L3N2Zz4=');}.icon-share-fat{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMzYuMjQsMTA3Ljc2bC04MC04MEE2LDYsMCwwLDAsMTQ2LDMyVjc0LjJjLTU0LjQ4LDMuNTktMTIwLjM5LDU1LTEyNy45MywxMjAuNjZhMTAsMTAsMCwwLDAsMTcuMjMsOGgwQzQ2LjU2LDE5MC44NSw4NywxNTIuNiwxNDYsMTUwLjEzVjE5MmE2LDYsMCwwLDAsMTAuMjQsNC4yNGw4MC04MEE2LDYsMCwwLDAsMjM2LjI0LDEwNy43NlpNMTU4LDE3Ny41MlYxNDRhNiw2LDAsMCwwLTYtNmMtMjcuNzMsMC01NC43Niw3LjI1LTgwLjMyLDIxLjU1YTE5My4zOCwxOTMuMzgsMCwwLDAtNDAuODEsMzAuNjVjNC43LTI2LjU2LDIwLjE2LTUyLDQ0LTcyLjI3Qzk4LjQ3LDk3Ljk0LDEyNy4yOSw4NiwxNTIsODZhNiw2LDAsMCwwLDYtNlY0Ni40OUwyMjMuNTEsMTEyWiIvPjwvc3ZnPg==');}.icon-trash{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMTYsNTBIMTc0VjQwYTIyLDIyLDAsMCwwLTIyLTIySDEwNEEyMiwyMiwwLDAsMCw4Miw0MFY1MEg0MGE2LDYsMCwwLDAsMCwxMkg1MFYyMDhhMTQsMTQsMCwwLDAsMTQsMTRIMTkyYTE0LDE0LDAsMCwwLDE0LTE0VjYyaDEwYTYsNiwwLDAsMCwwLTEyWk05NCw0MGExMCwxMCwwLDAsMSwxMC0xMGg0OGExMCwxMCwwLDAsMSwxMCwxMFY1MEg5NFpNMTk0LDIwOGEyLDIsMCwwLDEtMiwySDY0YTIsMiwwLDAsMS0yLTJWNjJIMTk0Wk0xMTAsMTA0djY0YTYsNiwwLDAsMS0xMiwwVjEwNGE2LDYsMCwwLDEsMTIsMFptNDgsMHY2NGE2LDYsMCwwLDEtMTIsMFYxMDRhNiw2LDAsMCwxLDEyLDBaIi8+PC9zdmc+');}.icon-star{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMzcuMjgsOTcuODdBMTQuMTgsMTQuMTgsMCwwLDAsMjI0Ljc2LDg4bC02MC4yNS00Ljg3LTIzLjIyLTU2LjJhMTQuMzcsMTQuMzcsMCwwLDAtMjYuNTgsMEw5MS40OSw4My4xMSwzMS4yNCw4OGExNC4xOCwxNC4xOCwwLDAsMC0xMi41Miw5Ljg5QTE0LjQzLDE0LjQzLDAsMCwwLDIzLDExMy4zMkw2OSwxNTIuOTNsLTE0LDU5LjI1YTE0LjQsMTQuNCwwLDAsMCw1LjU5LDE1LDE0LjEsMTQuMSwwLDAsMCwxNS45MS42TDEyOCwxOTYuMTJsNTEuNTgsMzEuNzFhMTQuMSwxNC4xLDAsMCwwLDE1LjkxLS42LDE0LjQsMTQuNCwwLDAsMCw1LjU5LTE1bC0xNC01OS4yNUwyMzMsMTEzLjMyQTE0LjQzLDE0LjQzLDAsMCwwLDIzNy4yOCw5Ny44N1ptLTEyLjE0LDYuMzctNDguNjksNDJhNiw2LDAsMCwwLTEuOTIsNS45MmwxNC44OCw2Mi43OWEyLjM1LDIuMzUsMCwwLDEtLjk1LDIuNTcsMi4yNCwyLjI0LDAsMCwxLTIuNi4xTDEzMS4xNCwxODRhNiw2LDAsMCwwLTYuMjgsMEw3MC4xNCwyMTcuNjFhMi4yNCwyLjI0LDAsMCwxLTIuNi0uMSwyLjM1LDIuMzUsMCwwLDEtMS0yLjU3bDE0Ljg4LTYyLjc5YTYsNiwwLDAsMC0xLjkyLTUuOTJsLTQ4LjY5LTQyYTIuMzcsMi4zNywwLDAsMS0uNzMtMi42NSwyLjI4LDIuMjgsMCwwLDEsMi4wNy0xLjY1bDYzLjkyLTUuMTZhNiw2LDAsMCwwLDUuMDYtMy42OWwyNC42My01OS42YTIuMzUsMi4zNSwwLDAsMSw0LjM4LDBsMjQuNjMsNTkuNmE2LDYsMCwwLDAsNS4wNiwzLjY5bDYzLjkyLDUuMTZhMi4yOCwyLjI4LDAsMCwxLDIuMDcsMS42NUEyLjM3LDIuMzcsMCwwLDEsMjI1LjE0LDEwNC4yNFoiLz48L3N2Zz4=');}.icon-alphabetical{--icon:url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIGZpbGw9ImN1cnJlbnRDb2xvciIgdmVyc2lvbj0iMS4xIiB2aWV3Qm94PSIwIDAgMTgzLjc4IDE4NC4wNiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJtNTkuNTg2IDY5Ljc0MmMtMC44NTEzIDAtMS40NjEgMC4xOTY1Ni0xLjgzNjYgMC41OTcxOC0wLjM1MDU0IDAuMzc1NTgtMC41Mjk1OCAxLjAyMjktMC41Mjk1OCAxLjk0OTNzMC4xNzkwMyAxLjU5MzcgMC41Mjk1OCAxLjk5NDRjMC4zNzU1OCAwLjM3NTU4IDAuOTg1MjkgMC41NjMzOCAxLjgzNjYgMC41NjMzOGg3LjAxOTdsLTEyLjQyOCAzNC4zNjZoLTIuMTA3Yy0wLjg1MTMgMC0xLjQ2MSAwLjE5NjU2LTEuODM2NiAwLjU5NzE4LTAuMzUwNTQgMC4zNzU1OC0wLjUyOTU3IDEuMDM0MS0wLjUyOTU3IDEuOTYwNiAwIDAuOTI2NDQgMC4xNzkwMyAxLjU4MjUgMC41Mjk1NyAxLjk4MyAwLjM3NTU4IDAuMzc1NTkgMC45ODUyOSAwLjU2MzM4IDEuODM2NiAwLjU2MzM4aDEyLjU1MmMwLjg1MTMgMCAxLjQ1MjItMC4xODc3OSAxLjgwMjgtMC41NjMzOCAwLjM3NTU4LTAuNDAwNjIgMC41NjMzNy0xLjA1NjYgMC41NjMzNy0xLjk4MyAwLTAuOTI2NDUtMC4xODc3OS0xLjU4NS0wLjU2MzM3LTEuOTYwNi0wLjM1MDU0LTAuNDAwNjItMC45NTE0Ny0wLjU5NzE4LTEuODAyOC0wLjU5NzE4aC00LjU1MjFsMy4xMjExLTguOTM0OWgxOC4yMmwzLjA3NiA4LjkzNDloLTUuMDcwNGMtMC44NTEzIDAtMS40NjEgMC4xOTY1Ni0xLjgzNjYgMC41OTcxOC0wLjM1MDU0IDAuMzc1NTgtMC41Mjk1OCAxLjAzNDEtMC41Mjk1OCAxLjk2MDYgMCAwLjkyNjQ0IDAuMTc5MDMgMS41ODI1IDAuNTI5NTggMS45ODMgMC4zNzU1OCAwLjM3NTU5IDAuOTg1MjkgMC41NjMzOCAxLjgzNjYgMC41NjMzOGgxMy4yOTZjMC44NTEzIDAgMS40NTIyLTAuMTg3NzkgMS44MDI4LTAuNTYzMzggMC4zNzU1OC0wLjQwMDYyIDAuNTYzMzctMS4wNTY2IDAuNTYzMzctMS45ODMgMC0wLjkyNjQ1LTAuMTg3NzktMS41ODUtMC41NjMzNy0xLjk2MDYtMC4zNTA1NC0wLjQwMDYyLTAuOTUxNDctMC41OTcxOC0xLjgwMjgtMC41OTcxOGgtMi4yODczbC0xMy4yNjItMzcuMDM2Yy0wLjMwMDQ3LTAuODUxMy0wLjc1OTk0LTEuNDYxLTEuMzg1OS0xLjgzNjYtMC42MDA5My0wLjQwMDYyLTEuNDA5Ny0wLjU5NzE4LTIuNDExMy0wLjU5NzE4em00NC4xNDYgMGMtMC44NTEzIDAtMS40NzIzIDAuMTk2NTYtMS44NDc4IDAuNTk3MTgtMC4zNTA1NSAwLjM3NTU4LTAuNTE4MyAxLjAyMjktMC41MTgzIDEuOTQ5M3YxMS45MWMwIDAuODc2MzMgMC4yMDUzMiAxLjUwNjEgMC42MzA5OCAxLjg4MTcgMC40MjU2NiAwLjM3NTU4IDEuMTU5MyAwLjU2MzM3IDIuMTg1OSAwLjU2MzM3czEuNzQ5LTAuMTg3NzkgMi4xNzQ3LTAuNTYzMzdjMC40MjU2OS0wLjM3NTU4IDAuNjQyMjYtMS4wMDUzIDAuNjQyMjYtMS44ODE3di05LjM1MTdoMTguODUxbC0yNC43NTQgMzUuMzAxYy0wLjM1MDU0IDAuNTI1ODItMC41MTgzMSAxLjA3MTctMC41MTgzMSAxLjYyMjYgMCAwLjkyNjQ1IDAuMTY3NzcgMS41ODI1IDAuNTE4MzEgMS45ODMxIDAuMzc1NTggMC4zNzU1OCAwLjk5NjU0IDAuNTYzMzggMS44NDc4IDAuNTYzMzhoMjguNzY2YzAuODUxMyAwIDEuNDUyMi0wLjE4NzggMS44MDI4LTAuNTYzMzggMC4zNzU1OC0wLjQwMDYyIDAuNTYzMzgtMS4wNTY2IDAuNTYzMzgtMS45ODMxdi0xMi42NjVjMC0wLjg3NjMzLTAuMjE2NTgtMS40OTQ4LTAuNjQyMjUtMS44NzA0LTAuNDI1NjYtMC4zNzU1OC0xLjE0OC0wLjU2MzM4LTIuMTc0Ny0wLjU2MzM4LTEuMDI2NiAwLTEuNzQ5IDAuMTg3NzktMi4xNzQ3IDAuNTYzMzgtMC40MjU2NiAwLjM3NTU4LTAuNjQyMjQgMC45OTQwMi0wLjY0MjI0IDEuODcwNHYxMC4xMDdoLTE5Ljk3OGwyNC45MDEtMzUuNDU5YzAuMjUwMzktMC4zNTA1NCAwLjM3MTgzLTAuODM4ODMgMC4zNzE4My0xLjQ2NDggMC0wLjkyNjQ1LTAuMTg3OC0xLjU3MzctMC41NjMzOC0xLjk0OTMtMC4zNTA1NS0wLjQwMDYyLTAuOTUxNDctMC41OTcxOC0xLjgwMjgtMC41OTcxOHptLTMxLjc1MiA1LjEwNDJoMC43MDk4NWw2Ljk4NTkgMjAuMzE1aC0xNC43MTZ6bS0zNy43MjMtNDkuMTgzYy00LjczNDIgMC04LjYzMTMgMy44OTctOC42MzEzIDguNjMxM3YxMTUuNDdjMCA0LjczNDIgMy44OTcgOC42MzEzIDguNjMxMyA4LjYzMTNoMTE1LjI2YzQuNzM0MiAwIDguNjQyMS0zLjg5NyA4LjY0MjEtOC42MzEzdi0xMTUuNDdjMC00LjczNDItMy45MDgyLTguNjMxMy04LjY0MjEtOC42MzEzem0wIDUuNzI0aDExNS4yNmMxLjY1OCAwIDIuOTA3IDEuMjQ5MSAyLjkwNyAyLjkwNzF2MTE1LjQ3YzAgMS42NTgtMS4yNDkxIDIuOTA3LTIuOTA3IDIuOTA3aC0xMTUuMjZjLTEuNjU4IDAtMi44OTU4LTEuMjQ5MS0yLjg5NTgtMi45MDd2LTExNS40N2MwLTEuNjU4IDEuMjM3OC0yLjkwNzEgMi44OTU4LTIuOTA3MXoiIGZpbGw9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIuNzIxMTQiLz48L3N2Zz4=');}.icon-scribble{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMDQuMjUsMTg4LjI0YTE2LjYzLDE2LjYzLDAsMCwwLDAsMjMuNTIsNiw2LDAsMSwxLTguNDgsOC40OCwyOC42MSwyOC42MSwwLDAsMSwwLTQwLjQ4bDkuMzctOS4zOGExNi42MywxNi42MywwLDAsMC0yMy41Mi0yMy41MWwtNjYuNzUsNjYuNzVhMjguNjMsMjguNjMsMCwwLDEtNDAuNDktNDAuNDlsOTguNzYtOTguNzVhMTYuNjMsMTYuNjMsMCwwLDAtMjMuNTItMjMuNTFMODIuODYsMTE3LjYyQTI4LjYzLDI4LjYzLDAsMCwxLDQyLjM3LDc3LjEzTDgzLjc1LDM1Ljc2YTYsNiwwLDEsMSw4LjQ5LDguNDhMNTAuODYsODUuNjJhMTYuNjMsMTYuNjMsMCwwLDAsMjMuNTIsMjMuNTFsNjYuNzUtNjYuNzVhMjguNjMsMjguNjMsMCwwLDEsNDAuNDksNDAuNDlMODIuODYsMTgxLjYyYTE2LjYzLDE2LjYzLDAsMCwwLDIzLjUyLDIzLjUxbDY2Ljc2LTY2Ljc1YTI4LjYzLDI4LjYzLDAsMCwxLDQwLjQ5LDQwLjQ5WiIvPjwvc3ZnPg==');}.icon-brackets-angle{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik04NS4wNiw0My4yMiwzMS4xMSwxMjhsNTQsODQuNzhhNiw2LDAsMCwxLTEuODQsOC4yOCw2LDYsMCwwLDEtOC4yOC0xLjg0bC01Ni04OGE2LDYsMCwwLDEsMC02LjQ0bDU2LTg4YTYsNiwwLDAsMSwxMC4xMiw2LjQ0Wm0xNTIsODEuNTYtNTYtODhhNiw2LDAsMSwwLTEwLjEyLDYuNDRMMjI0Ljg5LDEyOGwtNTMuOTUsODQuNzhhNiw2LDAsMCwwLDEuODQsOC4yOCw2LDYsMCwwLDAsOC4yOC0xLjg0bDU2LTg4QTYsNiwwLDAsMCwyMzcuMDYsMTI0Ljc4WiIvPjwvc3ZnPg==');}.icon-brain{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yNDYsMTI0YTU0LjEzLDU0LjEzLDAsMCwwLTMyLTQ5LjMzVjcyYTQ2LDQ2LDAsMCwwLTg2LTIyLjY3QTQ2LDQ2LDAsMCwwLDQyLDcydjIuNjdhNTQsNTQsMCwwLDAsMCw5OC42M1YxNzZhNDYsNDYsMCwwLDAsODYsMjIuNjdBNDYsNDYsMCwwLDAsMjE0LDE3NnYtMi43QTU0LjA3LDU0LjA3LDAsMCwwLDI0NiwxMjRaTTg4LDIxMGEzNCwzNCwwLDAsMS0zNC0zMi45NEE1My42Nyw1My42NywwLDAsMCw2NCwxNzhoOGE2LDYsMCwwLDAsMC0xMkg2NEE0Miw0MiwwLDAsMSw1MCw4NC4zOWE2LDYsMCwwLDAsNC01LjY2VjcyYTM0LDM0LDAsMCwxLDY4LDB2NzMuMDVBNDUuODksNDUuODksMCwwLDAsODgsMTMwYTYsNiwwLDAsMCwwLDEyLDM0LDM0LDAsMCwxLDAsNjhabTEwNC00NGgtOGE2LDYsMCwwLDAsMCwxMmg4YTUzLjY3LDUzLjY3LDAsMCwwLDEwLS45NEEzNCwzNCwwLDEsMSwxNjgsMTQyYTYsNiwwLDAsMCwwLTEyLDQ1Ljg5LDQ1Ljg5LDAsMCwwLTM0LDE1LjA1VjcyYTM0LDM0LDAsMCwxLDY4LDB2Ni43M2E2LDYsMCwwLDAsNCw1LjY2QTQyLDQyLDAsMCwxLDE5MiwxNjZabTE0LTU0YTYsNiwwLDAsMS02LDZoLTRhMzQsMzQsMCwwLDEtMzQtMzRWODBhNiw2LDAsMCwxLDEyLDB2NGEyMiwyMiwwLDAsMCwyMiwyMmg0QTYsNiwwLDAsMSwyMDYsMTEyWk02MCwxMThINTZhNiw2LDAsMCwxLDAtMTJoNEEyMiwyMiwwLDAsMCw4Miw4NFY4MGE2LDYsMCwwLDEsMTIsMHY0QTM0LDM0LDAsMCwxLDYwLDExOFoiLz48L3N2Zz4=');}.icon-palette{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xOTkuMzcsNTUuMzFBMTAxLjMyLDEwMS4zMiwwLDAsMCwxMjgsMjZoLTFBMTAyLDEwMiwwLDAsMCwyNiwxMjhjMCw0Mi4wOSwyNi4wNyw3Ny40NCw2OCw5Mi4yNkEzMC4yMSwzMC4yMSwwLDAsMCwxMDQuMTEsMjIyLDMwLjA2LDMwLjA2LDAsMCwwLDEzNCwxOTJhMTgsMTgsMCwwLDEsMTgtMThoNDYuMjFhMjkuODIsMjkuODIsMCwwLDAsMjkuMjUtMjMuMzFBMTAyLjcxLDEwMi43MSwwLDAsMCwyMzAsMTI3LjExLDEwMS4yNSwxMDEuMjUsMCwwLDAsMTk5LjM3LDU1LjMxWk0yMTUuNzYsMTQ4YTE3Ljg5LDE3Ljg5LDAsMCwxLTE3LjU1LDE0SDE1MmEzMCwzMCwwLDAsMC0zMCwzMCwxOCwxOCwwLDAsMS0yNCwxN0M2MSwxOTUuODYsMzgsMTY0Ljg1LDM4LDEyOGE5MCw5MCwwLDAsMSw4OS4wNy05MEgxMjhhOTAuMzQsOTAuMzQsMCwwLDEsOTAsODkuMjJBOTAuNDYsOTAuNDYsMCwwLDEsMjE1Ljc2LDE0OFpNMTM4LDc2YTEwLDEwLDAsMSwxLTEwLTEwQTEwLDEwLDAsMCwxLDEzOCw3NlpNOTQsMTAwQTEwLDEwLDAsMSwxLDg0LDkwLDEwLDEwLDAsMCwxLDk0LDEwMFptMCw1NmExMCwxMCwwLDEsMS0xMC0xMEExMCwxMCwwLDAsMSw5NCwxNTZabTg4LTU2YTEwLDEwLDAsMSwxLTEwLTEwQTEwLDEwLDAsMCwxLDE4MiwxMDBaIi8+PC9zdmc+');}.icon-pen-nib{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yNDYsOTIuNjhhMTMuOTQsMTMuOTQsMCwwLDAtNC4xLTkuOUwxNzMuMjEsMTQuMWExNCwxNCwwLDAsMC0xOS44LDBMMTI0LjY4LDQyLjgzLDY2LjIyLDY0Ljc2YTE0LDE0LDAsMCwwLTguOSwxMC44TDM0LjA4LDIxNUE2LDYsMCwwLDAsNDAsMjIyYTYuNjEsNi42MSwwLDAsMCwxLS4wOGwxMzkuNDQtMjMuMjRhMTQsMTQsMCwwLDAsMTAuODEtOC45bDIxLjkyLTU4LjQ2LDI4Ljc0LTI4Ljc0QTEzLjkyLDEzLjkyLDAsMCwwLDI0Niw5Mi42OFptLTY2LDkyLjg5YTIsMiwwLDAsMS0xLjU0LDEuMjdMNTcuNDksMjA3bDUyLjg3LTUyLjg4YTI2LDI2LDAsMSwwLTguNDgtOC40OEw0OSwxOTguNTNsMjAuMTctMTIxQTIsMiwwLDAsMSw3MC40Myw3Nmw1Ni4wNi0yMUwyMDEsMTI5LjUxWk0xMTAsMTMyYTE0LDE0LDAsMSwxLDE0LDE0QTE0LDE0LDAsMCwxLDExMCwxMzJaTTIzMy40MSw5NC4xLDIwOCwxMTkuNTEsMTM2LjQ4LDQ4LDE2MS45LDIyLjU4YTIsMiwwLDAsMSwyLjgzLDBsNjguNjgsNjguNjlhMiwyLDAsMCwxLDAsMi44M1oiLz48L3N2Zz4=');}.icon-question{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xMzgsMTgwYTEwLDEwLDAsMSwxLTEwLTEwQTEwLDEwLDAsMCwxLDEzOCwxODBaTTEyOCw3NGMtMjEsMC0zOCwxNS4yNS0zOCwzNHY0YTYsNiwwLDAsMCwxMiwwdi00YzAtMTIuMTMsMTEuNjYtMjIsMjYtMjJzMjYsOS44NywyNiwyMi0xMS42NiwyMi0yNiwyMmE2LDYsMCwwLDAtNiw2djhhNiw2LDAsMCwwLDEyLDB2LTIuNDJjMTguMTEtMi41OCwzMi0xNi42NiwzMi0zMy41OEMxNjYsODkuMjUsMTQ5LDc0LDEyOCw3NFptMTAyLDU0QTEwMiwxMDIsMCwxLDEsMTI4LDI2LDEwMi4xMiwxMDIuMTIsMCwwLDEsMjMwLDEyOFptLTEyLDBhOTAsOTAsMCwxLDAtOTAsOTBBOTAuMSw5MC4xLDAsMCwwLDIxOCwxMjhaIi8+PC9zdmc+');}.icon-city{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yNDAsMjEwSDIzMFY4OGE2LDYsMCwwLDAtNi02SDE2MGE2LDYsMCwwLDAtNiw2djQySDEwMlY0MGE2LDYsMCwwLDAtNi02SDMyYTYsNiwwLDAsMC02LDZWMjEwSDE2YTYsNiwwLDAsMCwwLDEySDI0MGE2LDYsMCwwLDAsMC0xMlpNMTY2LDk0aDUyVjIxMEgxNjZabS0xMiw0OHY2OEgxMDJWMTQyWk0zOCw0Nkg5MFYyMTBIMzhaTTcwLDcyVjg4YTYsNiwwLDAsMS0xMiwwVjcyYTYsNiwwLDAsMSwxMiwwWm0wLDQ4djE2YTYsNiwwLDAsMS0xMiwwVjEyMGE2LDYsMCwwLDEsMTIsMFptMCw0OHYxNmE2LDYsMCwwLDEtMTIsMFYxNjhhNiw2LDAsMCwxLDEyLDBabTUyLDE2VjE2OGE2LDYsMCwwLDEsMTIsMHYxNmE2LDYsMCwwLDEtMTIsMFptNjQsMFYxNjhhNiw2LDAsMCwxLDEyLDB2MTZhNiw2LDAsMCwxLTEyLDBabTAtNDhWMTIwYTYsNiwwLDAsMSwxMiwwdjE2YTYsNiwwLDAsMS0xMiwwWiIvPjwvc3ZnPg==');}.icon-folder{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMTYsNzRIMTMwLjQ5bC0yNy45LTI3LjlhMTMuOTQsMTMuOTQsMCwwLDAtOS45LTQuMUg0MEExNCwxNCwwLDAsMCwyNiw1NlYyMDAuNjJBMTMuMzksMTMuMzksMCwwLDAsMzkuMzgsMjE0SDIxNi44OUExMy4xMiwxMy4xMiwwLDAsMCwyMzAsMjAwLjg5Vjg4QTE0LDE0LDAsMCwwLDIxNiw3NFpNNDAsNTRIOTIuNjlhMiwyLDAsMCwxLDEuNDEuNTlMMTEzLjUxLDc0SDM4VjU2QTIsMiwwLDAsMSw0MCw1NFpNMjE4LDIwMC44OWExLjExLDEuMTEsMCwwLDEtMS4xMSwxLjExSDM5LjM4QTEuNCwxLjQsMCwwLDEsMzgsMjAwLjYyVjg2SDIxNmEyLDIsMCwwLDEsMiwyWiIvPjwvc3ZnPg==');}.icon-hash{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjQsOTBIMTczbDguODktNDguOTNhNiw2LDAsMSwwLTExLjgtMi4xNEwxNjAuODEsOTBIMTA5bDguODktNDguOTNhNiw2LDAsMCwwLTExLjgtMi4xNEw5Ni44MSw5MEg0OGE2LDYsMCwwLDAsMCwxMkg5NC42M2wtOS40Niw1MkgzMmE2LDYsMCwwLDAsMCwxMkg4M0w3NC4xLDIxNC45M2E2LDYsMCwwLDAsNC44Myw3QTUuNjQsNS42NCwwLDAsMCw4MCwyMjJhNiw2LDAsMCwwLDUuODktNC45M0w5NS4xOSwxNjZIMTQ3bC04Ljg5LDQ4LjkzYTYsNiwwLDAsMCw0LjgzLDcsNS42NCw1LjY0LDAsMCwwLDEuMDguMSw2LDYsMCwwLDAsNS44OS00LjkzTDE1OS4xOSwxNjZIMjA4YTYsNiwwLDAsMCwwLTEySDE2MS4zN2w5LjQ2LTUySDIyNGE2LDYsMCwwLDAsMC0xMlptLTc0LjgzLDY0SDk3LjM3bDkuNDYtNTJoNTEuOFoiLz48L3N2Zz4=');}.icon-shapes{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik02OS42OSw2Mi4xYTYsNiwwLDAsMC0xMS4zOCwwbC00MCwxMjBBNiw2LDAsMCwwLDI0LDE5MGg4MGE2LDYsMCwwLDAsNS42OS03LjlaTTMyLjMyLDE3OCw2NCw4M2wzMS42OCw5NVpNMjA2LDc2YTUwLDUwLDAsMSwwLTUwLDUwQTUwLjA2LDUwLjA2LDAsMCwwLDIwNiw3NlptLTg4LDBhMzgsMzgsMCwxLDEsMzgsMzhBMzgsMzgsMCwwLDEsMTE4LDc2Wm0xMDYsNzBIMTM2YTYsNiwwLDAsMC02LDZ2NTZhNiw2LDAsMCwwLDYsNmg4OGE2LDYsMCwwLDAsNi02VjE1MkE2LDYsMCwwLDAsMjI0LDE0NlptLTYsNTZIMTQyVjE1OGg3NloiLz48L3N2Zz4=');}.icon-diamonds-four{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xMjMuNzYsMTA4LjI0YTYsNiwwLDAsMCw4LjQ4LDBsNDAtNDBhNiw2LDAsMCwwLDAtOC40OGwtNDAtNDBhNiw2LDAsMCwwLTguNDgsMGwtNDAsNDBhNiw2LDAsMCwwLDAsOC40OFpNMTI4LDMyLjQ5LDE1OS41MSw2NCwxMjgsOTUuNTEsOTYuNDksNjRabTQuMjQsMTE1LjI3YTYsNiwwLDAsMC04LjQ4LDBsLTQwLDQwYTYsNiwwLDAsMCwwLDguNDhsNDAsNDBhNiw2LDAsMCwwLDguNDgsMGw0MC00MGE2LDYsMCwwLDAsMC04LjQ4Wk0xMjgsMjIzLjUxLDk2LjQ5LDE5MiwxMjgsMTYwLjQ5LDE1OS41MSwxOTJabTEwOC4yNC05OS43NS00MC00MGE2LDYsMCwwLDAtOC40OCwwbC00MCw0MGE2LDYsMCwwLDAsMCw4LjQ4bDQwLDQwYTYsNiwwLDAsMCw4LjQ4LDBsNDAtNDBBNiw2LDAsMCwwLDIzNi4yNCwxMjMuNzZaTTE5MiwxNTkuNTEsMTYwLjQ5LDEyOCwxOTIsOTYuNDksMjIzLjUxLDEyOFptLTgzLjc2LTM1Ljc1LTQwLTQwYTYsNiwwLDAsMC04LjQ4LDBsLTQwLDQwYTYsNiwwLDAsMCwwLDguNDhsNDAsNDBhNiw2LDAsMCwwLDguNDgsMGw0MC00MEE2LDYsMCwwLDAsMTA4LjI0LDEyMy43NlpNNjQsMTU5LjUxLDMyLjQ5LDEyOCw2NCw5Ni40OSw5NS41MSwxMjhaIi8+PC9zdmc+');}.icon-crosshair-simple{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xMjgsMjZBMTAyLDEwMiwwLDEsMCwyMzAsMTI4LDEwMi4xMiwxMDIuMTIsMCwwLDAsMTI4LDI2Wm02LDE5MS44VjE4NGE2LDYsMCwwLDAtMTIsMHYzMy44QTkwLjE1LDkwLjE1LDAsMCwxLDM4LjIsMTM0SDcyYTYsNiwwLDAsMCwwLTEySDM4LjJBOTAuMTUsOTAuMTUsMCwwLDEsMTIyLDM4LjJWNzJhNiw2LDAsMCwwLDEyLDBWMzguMkE5MC4xNSw5MC4xNSwwLDAsMSwyMTcuOCwxMjJIMTg0YTYsNiwwLDAsMCwwLDEyaDMzLjhBOTAuMTUsOTAuMTUsMCwwLDEsMTM0LDIxNy44WiIvPjwvc3ZnPg==');}.icon-circle-notch{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMzAsMTI4YTEwMiwxMDIsMCwwLDEtMjA0LDBjMC00MC4xOCwyMy4zNS03Ni44Niw1OS41LTkzLjQ1YTYsNiwwLDAsMSw1LDEwLjlDNTguNjEsNjAuMDksMzgsOTIuNDksMzgsMTI4YTkwLDkwLDAsMCwwLDE4MCwwYzAtMzUuNTEtMjAuNjEtNjcuOTEtNTIuNS04Mi41NWE2LDYsMCwwLDEsNS0xMC45QzIwNi42NSw1MS4xNCwyMzAsODcuODIsMjMwLDEyOFoiLz48L3N2Zz4=');}.icon-cards-three{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMDgsOTBINDhhMTQsMTQsMCwwLDAtMTQsMTR2OTZhMTQsMTQsMCwwLDAsMTQsMTRIMjA4YTE0LDE0LDAsMCwwLDE0LTE0VjEwNEExNCwxNCwwLDAsMCwyMDgsOTBabTIsMTEwYTIsMiwwLDAsMS0yLDJINDhhMiwyLDAsMCwxLTItMlYxMDRhMiwyLDAsMCwxLDItMkgyMDhhMiwyLDAsMCwxLDIsMlpNNTAsNjRhNiw2LDAsMCwxLDYtNkgyMDBhNiw2LDAsMCwxLDAsMTJINTZBNiw2LDAsMCwxLDUwLDY0Wk02NiwzMmE2LDYsMCwwLDEsNi02SDE4NGE2LDYsMCwwLDEsMCwxMkg3MkE2LDYsMCwwLDEsNjYsMzJaIi8+PC9zdmc+');}.icon-house{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMTcuOSwxMTAuMWwtODAtODBhMTQsMTQsMCwwLDAtMTkuOCwwbC04MCw4MEExMy45MiwxMy45MiwwLDAsMCwzNCwxMjB2OTZhNiw2LDAsMCwwLDYsNmg2NGE2LDYsMCwwLDAsNi02VjE1OGgzNnY1OGE2LDYsMCwwLDAsNiw2aDY0YTYsNiwwLDAsMCw2LTZWMTIwQTEzLjkyLDEzLjkyLDAsMCwwLDIxNy45LDExMC4xWk0yMTAsMjEwSDE1OFYxNTJhNiw2LDAsMCwwLTYtNkgxMDRhNiw2LDAsMCwwLTYsNnY1OEg0NlYxMjBhMiwyLDAsMCwxLC41OC0xLjQybDgwLTgwYTIsMiwwLDAsMSwyLjg0LDBsODAsODBBMiwyLDAsMCwxLDIxMCwxMjBaIi8+PC9zdmc+');}.icon-sun-dim{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xMjIsNDBWMzJhNiw2LDAsMCwxLDEyLDB2OGE2LDYsMCwwLDEtMTIsMFptNjgsODhhNjIsNjIsMCwxLDEtNjItNjJBNjIuMDcsNjIuMDcsMCwwLDEsMTkwLDEyOFptLTEyLDBhNTAsNTAsMCwxLDAtNTAsNTBBNTAuMDYsNTAuMDYsMCwwLDAsMTc4LDEyOFpNNTkuNzYsNjguMjRhNiw2LDAsMSwwLDguNDgtOC40OGwtOC04YTYsNiwwLDAsMC04LjQ4LDguNDhabTAsMTE5LjUyLTgsOGE2LDYsMCwxLDAsOC40OCw4LjQ4bDgtOGE2LDYsMCwxLDAtOC40OC04LjQ4Wm0xMzYtMTM2LTgsOGE2LDYsMCwxLDAsOC40OCw4LjQ4bDgtOGE2LDYsMCwwLDAtOC40OC04LjQ4Wm0uNDgsMTM2YTYsNiwwLDAsMC04LjQ4LDguNDhsOCw4YTYsNiwwLDAsMCw4LjQ4LTguNDhaTTQwLDEyMkgzMmE2LDYsMCwwLDAsMCwxMmg4YTYsNiwwLDAsMCwwLTEyWm04OCw4OGE2LDYsMCwwLDAtNiw2djhhNiw2LDAsMCwwLDEyLDB2LThBNiw2LDAsMCwwLDEyOCwyMTBabTk2LTg4aC04YTYsNiwwLDAsMCwwLDEyaDhhNiw2LDAsMCwwLDAtMTJaIi8+PC9zdmc+');}.icon-moon{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMzIuMTMsMTQzLjY0YTYsNiwwLDAsMC02LTEuNDlBOTAuMDcsOTAuMDcsMCwwLDEsMTEzLjg2LDI5Ljg1YTYsNiwwLDAsMC03LjQ5LTcuNDhBMTAyLjg4LDEwMi44OCwwLDAsMCw1NC40OCw1OC42OCwxMDIsMTAyLDAsMCwwLDE5Ny4zMiwyMDEuNTJhMTAyLjg4LDEwMi44OCwwLDAsMCwzNi4zMS01MS44OUE2LDYsMCwwLDAsMjMyLjEzLDE0My42NFptLTQyLDQ4LjI5YTkwLDkwLDAsMCwxLTEyNi0xMjZBOTAuOSw5MC45LDAsMCwxLDk5LjY1LDM3LjY2LDEwMi4wNiwxMDIuMDYsMCwwLDAsMjE4LjM0LDE1Ni4zNSw5MC45LDkwLjksMCwwLDEsMTkwLjEsMTkxLjkzWiIvPjwvc3ZnPg==');}.icon-sign-out{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xMTgsMjE2YTYsNiwwLDAsMS02LDZINDhhNiw2LDAsMCwxLTYtNlY0MGE2LDYsMCwwLDEsNi02aDY0YTYsNiwwLDAsMSwwLDEySDU0VjIxMGg1OEE2LDYsMCwwLDEsMTE4LDIxNlptMTEwLjI0LTkyLjI0LTQwLTQwYTYsNiwwLDAsMC04LjQ4LDguNDhMMjA5LjUxLDEyMkgxMTJhNiw2LDAsMCwwLDAsMTJoOTcuNTFsLTI5Ljc1LDI5Ljc2YTYsNiwwLDEsMCw4LjQ4LDguNDhsNDAtNDBBNiw2LDAsMCwwLDIyOC4yNCwxMjMuNzZaIi8+PC9zdmc+');}.icon-plus-square{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMDgsMzRINDhBMTQsMTQsMCwwLDAsMzQsNDhWMjA4YTE0LDE0LDAsMCwwLDE0LDE0SDIwOGExNCwxNCwwLDAsMCwxNC0xNFY0OEExNCwxNCwwLDAsMCwyMDgsMzRabTIsMTc0YTIsMiwwLDAsMS0yLDJINDhhMiwyLDAsMCwxLTItMlY0OGEyLDIsMCwwLDEsMi0ySDIwOGEyLDIsMCwwLDEsMiwyWm0tMzYtODBhNiw2LDAsMCwxLTYsNkgxMzR2MzRhNiw2LDAsMCwxLTEyLDBWMTM0SDg4YTYsNiwwLDAsMSwwLTEyaDM0Vjg4YTYsNiwwLDAsMSwxMiwwdjM0aDM0QTYsNiwwLDAsMSwxNzQsMTI4WiIvPjwvc3ZnPg==');}.icon-arrow-elbow-left-up{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMzgsMTkyYTYsNiwwLDAsMS02LDZIODhhNiw2LDAsMCwxLTYtNlY2Mi40OUw0NC4yNCwxMDAuMjRhNiw2LDAsMCwxLTguNDgtOC40OGw0OC00OGE2LDYsMCwwLDEsOC40OCwwbDQ4LDQ4YTYsNiwwLDEsMS04LjQ4LDguNDhMOTQsNjIuNDlWMTg2SDIzMkE2LDYsMCwwLDEsMjM4LDE5MloiLz48L3N2Zz4=');}.icon-arrow-elbow-right-up{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjAuMjQsMTAwLjI0YTYsNiwwLDAsMS04LjQ4LDBMMTc0LDYyLjQ5VjE5MmE2LDYsMCwwLDEtNiw2SDI0YTYsNiwwLDAsMSwwLTEySDE2MlY2Mi40OWwtMzcuNzYsMzcuNzVhNiw2LDAsMCwxLTguNDgtOC40OGw0OC00OGE2LDYsMCwwLDEsOC40OCwwbDQ4LDQ4QTYsNiwwLDAsMSwyMjAuMjQsMTAwLjI0WiIvPjwvc3ZnPg==');}.icon-x-circle{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xNjQuMjQsMTAwLjI0LDEzNi40OCwxMjhsMjcuNzYsMjcuNzZhNiw2LDAsMSwxLTguNDgsOC40OEwxMjgsMTM2LjQ4bC0yNy43NiwyNy43NmE2LDYsMCwwLDEtOC40OC04LjQ4TDExOS41MiwxMjgsOTEuNzYsMTAwLjI0YTYsNiwwLDAsMSw4LjQ4LTguNDhMMTI4LDExOS41MmwyNy43Ni0yNy43NmE2LDYsMCwwLDEsOC40OCw4LjQ4Wk0yMzAsMTI4QTEwMiwxMDIsMCwxLDEsMTI4LDI2LDEwMi4xMiwxMDIuMTIsMCwwLDEsMjMwLDEyOFptLTEyLDBhOTAsOTAsMCwxLDAtOTAsOTBBOTAuMSw5MC4xLDAsMCwwLDIxOCwxMjhaIi8+PC9zdmc+');}.icon-x{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMDQuMjQsMTk1Ljc2YTYsNiwwLDEsMS04LjQ4LDguNDhMMTI4LDEzNi40OSw2MC4yNCwyMDQuMjRhNiw2LDAsMCwxLTguNDgtOC40OEwxMTkuNTEsMTI4LDUxLjc2LDYwLjI0YTYsNiwwLDAsMSw4LjQ4LTguNDhMMTI4LDExOS41MWw2Ny43Ni02Ny43NWE2LDYsMCwwLDEsOC40OCw4LjQ4TDEzNi40OSwxMjhaIi8+PC9zdmc+');}.icon-magnifying-glass{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjguMjQsMjE5Ljc2bC01MS4zOC01MS4zOGE4Ni4xNSw4Ni4xNSwwLDEsMC04LjQ4LDguNDhsNTEuMzgsNTEuMzhhNiw2LDAsMCwwLDguNDgtOC40OFpNMzgsMTEyYTc0LDc0LDAsMSwxLDc0LDc0QTc0LjA5LDc0LjA5LDAsMCwxLDM4LDExMloiLz48L3N2Zz4=');}.icon-floppy-disk{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMTcuOSw3My40MiwxODIuNTgsMzguMWExMy45LDEzLjksMCwwLDAtOS44OS00LjFINDhBMTQsMTQsMCwwLDAsMzQsNDhWMjA4YTE0LDE0LDAsMCwwLDE0LDE0SDIwOGExNCwxNCwwLDAsMCwxNC0xNFY4My4zMUExMy45LDEzLjksMCwwLDAsMjE3LjksNzMuNDJaTTE3MCwyMTBIODZWMTUyYTIsMiwwLDAsMSwyLTJoODBhMiwyLDAsMCwxLDIsMlptNDAtMmEyLDIsMCwwLDEtMiwySDE4MlYxNTJhMTQsMTQsMCwwLDAtMTQtMTRIODhhMTQsMTQsMCwwLDAtMTQsMTR2NThINDhhMiwyLDAsMCwxLTItMlY0OGEyLDIsMCwwLDEsMi0ySDE3Mi42OWEyLDIsMCwwLDEsMS40MS41OEwyMDkuNDIsODEuOWEyLDIsMCwwLDEsLjU4LDEuNDFaTTE1OCw3MmE2LDYsMCwwLDEtNiw2SDk2YTYsNiwwLDAsMSwwLTEyaDU2QTYsNiwwLDAsMSwxNTgsNzJaIi8+PC9zdmc+');}.icon-dots-six-vertical{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xMDIsNjBBMTAsMTAsMCwxLDEsOTIsNTAsMTAsMTAsMCwwLDEsMTAyLDYwWm02MiwxMGExMCwxMCwwLDEsMC0xMC0xMEExMCwxMCwwLDAsMCwxNjQsNzBaTTkyLDExOGExMCwxMCwwLDEsMCwxMCwxMEExMCwxMCwwLDAsMCw5MiwxMThabTcyLDBhMTAsMTAsMCwxLDAsMTAsMTBBMTAsMTAsMCwwLDAsMTY0LDExOFpNOTIsMTg2YTEwLDEwLDAsMSwwLDEwLDEwQTEwLDEwLDAsMCwwLDkyLDE4NlptNzIsMGExMCwxMCwwLDEsMCwxMCwxMEExMCwxMCwwLDAsMCwxNjQsMTg2WiIvPjwvc3ZnPg==');}.icon-plus{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjIsMTI4YTYsNiwwLDAsMS02LDZIMTM0djgyYTYsNiwwLDAsMS0xMiwwVjEzNEg0MGE2LDYsMCwwLDEsMC0xMmg4MlY0MGE2LDYsMCwwLDEsMTIsMHY4Mmg4MkE2LDYsMCwwLDEsMjIyLDEyOFoiLz48L3N2Zz4=');}.icon-pencil-simple{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjUuOSw3NC43OCwxODEuMjEsMzAuMDlhMTQsMTQsMCwwLDAtMTkuOCwwTDM4LjEsMTUzLjQxYTEzLjk0LDEzLjk0LDAsMCwwLTQuMSw5LjlWMjA4YTE0LDE0LDAsMCwwLDE0LDE0SDkyLjY5YTEzLjk0LDEzLjk0LDAsMCwwLDkuOS00LjFMMjI1LjksOTQuNThhMTQsMTQsMCwwLDAsMC0xOS44Wk05NC4xLDIwOS40MWEyLDIsMCwwLDEtMS40MS41OUg0OGEyLDIsMCwwLDEtMi0yVjE2My4zMWEyLDIsMCwwLDEsLjU5LTEuNDFMMTM2LDcyLjQ4LDE4My41MSwxMjBaTTIxNy40MSw4Ni4xLDE5MiwxMTEuNTEsMTQ0LjQ5LDY0LDE2OS45LDM4LjU4YTIsMiwwLDAsMSwyLjgzLDBsNDQuNjgsNDQuNjlhMiwyLDAsMCwxLDAsMi44M1oiLz48L3N2Zz4=');}.icon-list{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjIsMTI4YTYsNiwwLDAsMS02LDZINDBhNiw2LDAsMCwxLDAtMTJIMjE2QTYsNiwwLDAsMSwyMjIsMTI4Wk00MCw3MEgyMTZhNiw2LDAsMCwwLDAtMTJINDBhNiw2LDAsMCwwLDAsMTJaTTIxNiwxODZINDBhNiw2LDAsMCwwLDAsMTJIMjE2YTYsNiwwLDAsMCwwLTEyWiIvPjwvc3ZnPg==');}.icon-loading{--icon:url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+PHN2ZyAgICB3aWR0aD0iMzIiICAgIGhlaWdodD0iMzIiICAgIHZpZXdCb3g9IjAgMCAzMiAzMiIgICAgdmVyc2lvbj0iMS4xIiAgICB4bWw6c3BhY2U9InByZXNlcnZlIiAgICBzdHlsZT0iY2xpcC1ydWxlOmV2ZW5vZGQ7ZmlsbC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjEuNSIgICAgaWQ9InN2ZzEwIiAgICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciICAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxkZWZzICAgIGlkPSJkZWZzMTAiIC8+PHBhdGggICAgaWQ9InBhdGgxMSIgICAgc3R5bGU9ImJhc2VsaW5lLXNoaWZ0OmJhc2VsaW5lO2Rpc3BsYXk6aW5saW5lO292ZXJmbG93OnZpc2libGU7dmVjdG9yLWVmZmVjdDpub25lO2ZpbGw6IzIyMjIyMjtlbmFibGUtYmFja2dyb3VuZDphY2N1bXVsYXRlO3N0b3AtY29sb3I6IzAwMDAwMDtzdG9wLW9wYWNpdHk6MSIgICAgZD0ibSAxNi42MjEwOTQsMS4xNDI1NzgxIGMgLTguMjY2MzIzMiwwIC0xNi4yMjA4NjczOCw2LjQ0MjgwOTUgLTE1LjU4NTkzNzgsMTQuNjg1NTQ2OSAwLjYwMTM0NTUsNy44MDczMDggNy40MzQxMjY0LDE0LjEyNjk4IDE0LjkzMzU5MzgsMTQuOTQzMzU5IDguODM5ODQ1LDAuOTYyMjgzIDE1LjUwNTQ2OSwtNi4zNzY5MTkgMTUuMDA1ODU5LC0xNC45ODYzMjggQyAzMC40OTU5LDcuNTM2MjY4NCAyNC44ODMzOTcsMS4xNDI1NzgxIDE2LjYyMTA5NCwxLjE0MjU3ODEgWiBtIDAsMC42NTAzOTA3IEMgMjYuNDg4Nzg2LDEuODAzODY0NSAyOS43MTQ1MTgsOS41OTM1ODMzIDMwLjMwMjczNCwxNS44MDQ2ODggMzEuMTQxOTgyLDI0LjY2NjM2NSAyMi4xNjA0NTksMzEuMTY4MDc3IDE2LjAzOTA2MiwzMC4xMjUgOC44OTUxMzI3LDI4LjkwNzY4MSAyLjI2MTMxNDIsMjMuMjc5Mzc2IDEuNjgzNTkzOCwxNS43NzkyOTcgMS4wNzY5MzM4LDcuOTAzMjc1NCA4LjcyMjU0NTEsMS43ODQyNjk5IDE2LjYyMTA5NCwxLjc5Mjk2ODggWiBtIC0wLjA2NDQ1LDEuMjE4NzUgYyAtMy42MTAwODMsMCAtNy4xNTQ3OTk1LDEuNDAxMDY4NyAtOS43MzA0NjkxLDMuNzAzMTI1IEMgNC4yNTA1MDIzLDkuMDE2OTAwMiAyLjY0MjAzNzIsMTIuMjI2Mjk1IDIuOTE5OTIxOSwxNS44MzM5ODQgMy40NDY5MzUsMjIuNjc1NzEyIDkuNDI4OTY0OSwyOC4xOTg5ODUgMTUuOTk4MDQ3LDI4LjkxNDA2MiAyMy43MTQyNTYsMjkuNzU0MDIzIDI5LjUzMTYwMywyMy4zMzE3IDI5LjA5NTcwMywxNS44MjAzMTIgMjguNjc3OTQ4LDguNjIxMzk1MyAyMy43NzY2ODYsMy4wMTE3MTg4IDE2LjU1NjY0MSwzLjAxMTcxODggWiBtIDAsMC4xOTUzMTI0IGMgNy4xMTkxMzQsMCAxMS45MzI3MSw1LjUwODEzNzMgMTIuMzQ1NzAzLDEyLjYyNDk5OTggQyAyOS4zMzIwNjIsMjMuMjM2ODk2IDIzLjYxODk1OCwyOS41NDU5OTggMTYuMDE5NTMxLDI4LjcxODc1IDkuNTQ1NDMyMSwyOC4wMTQwMTIgMy42MzQxNjM3LDIyLjU1NTE0MyAzLjExNTIzNDQsMTUuODE4MzU5IDIuODQyNDU2MywxMi4yNzY5NjcgNC40MTg0MTA5LDkuMTI4MzE2OSA2Ljk1NzAzMTIsNi44NTkzNzUgOS40OTU2NTE2LDQuNTkwNDMzMSAxMi45OTcwOTMsMy4yMDcwMzEyIDE2LjU1NjY0MSwzLjIwNzAzMTIgWiBtIC0wLjA3MDMxLDEuNDE2MDE1NyBjIC0zLjE2MTk3MywwIC02LjI2MzUwOSwxLjIyNTgxMzkgLTguNTE5NTMxMSwzLjI0MjE4NzUgQyA1LjcxMDc2OTEsOS44ODE2MDggNC4zMDE0NTQyLDEyLjY5NDU4OSA0LjU0NDkyMTksMTUuODU1NDY5IDUuMDA2NTYyNCwyMS44NDg1NTQgMTAuMjQ0MTc4LDI2LjY4NjE1OSAxNS45OTgwNDcsMjcuMzEyNSAyMi43NTcwMTMsMjguMDQ4MjYxIDI3Ljg1NDQ1MSwyMi40MjA5MzYgMjcuNDcyNjU2LDE1Ljg0MTc5NyAyNy4xMDY4MjQsOS41Mzc2MDI1IDIyLjgxMDE2LDQuNjIzMDQ2OSAxNi40ODYzMjgsNC42MjMwNDY5IFogbSAwLDAuMTk1MzEyNSBjIDYuMjIyOTIsMCAxMC40Mjk5NDYsNC44MTMwMTM4IDEwLjc5MTAxNiwxMS4wMzUxNTY2IDAuMzc1NjEzLDYuNDcyNjE1IC00LjYxNzU4NCwxMS45ODY3MiAtMTEuMjU5NzY2LDExLjI2MzY3MiBDIDEwLjM1ODY4NSwyNi41MDExODYgNS4xOTE4MzgxLDIxLjcyNzk4NSA0LjczODI4MTIsMTUuODM5ODQ0IDQuNDk5OTIwMSwxMi43NDUyNjIgNS44NzY3MzE1LDkuOTk0OTc3OCA4LjA5NTcwMzEsOC4wMTE3MTg4IDEwLjMxNDY3NSw2LjAyODQ1OTUgMTMuMzc0ODksNC44MTgzNTk0IDE2LjQ4NjMyOCw0LjgxODM1OTQgWiBtIC0wLjA2ODM2LDEuNDE2MDE1NiBjIC0yLjcxMzg3NywwIC01LjM3NjExOCwxLjA1MjUxNjQgLTcuMzEyNTAwMiwyLjc4MzIwMzEgLTEuOTM2MzgyOCwxLjczMDY4NjkgLTMuMTQ2NTUxNyw0LjE0NTMxMTkgLTIuOTM3NSw2Ljg1OTM3NDkgMC4zOTYyNjk5LDUuMTQ0NDMgNC44ODk0NDQyLDkuMjk0NDI5IDkuODI4MTI1Miw5LjgzMjAzMSA1LjgwMTc0OSwwLjYzMTU2MiAxMC4xNzkyNTcsLTQuMTk4ODI4IDkuODUxNTYyLC05Ljg0NTcwMyBDIDI1LjUzMzc1LDEwLjQ1MzgyMiAyMS44NDU2MTYsNi4yMzQzNzUgMTYuNDE3OTc0LDYuMjM0Mzc1IFogbSAwLDAuMTk1MzEyNSBjIDUuMzI2NzMsMCA4LjkyNTIyNiw0LjExNzkwNTUgOS4yMzQzNzUsOS40NDUzMTI1IDAuMzIxNTEzLDUuNTQwMzUxIC0zLjk0OTgwMSwxMC4yNTk0NzQgLTkuNjM0NzY2LDkuNjQwNjI1IEMgMTEuMTczODc1LDI0Ljk4ODM2MiA2Ljc0OTUxNDMsMjAuOTAwODE0IDYuMzYxMzI4MSwxNS44NjEzMjggNi4xNTczODMxLDEzLjIxMzU2MyA3LjMzNTA0MzEsMTAuODU5NjgyIDkuMjM0Mzc1LDkuMTYyMTA5NCAxMS4xMzM3MDcsNy40NjQ1MzcyIDEzLjc1NDYyOCw2LjQyOTY4NzUgMTYuNDE3OTY5LDYuNDI5Njg3NSBaIG0gLTAuMDY4MzYsMS40MTYwMTU2IGMgLTIuMjY1Nzc1LDAgLTQuNDg4NzI5LDAuODc5MjE5NiAtNi4xMDU0NjgsMi4zMjQyMTg5IC0xLjYxNjc0MDgsMS40NDQ5OTkgLTIuNjI3NzYwNywzLjQ2MTI2OSAtMi40NTMxMjU0LDUuNzI4NTE2IDAuMzMwODk4Niw0LjI5NTc2OCA0LjA4MTU5NjQsNy43NjAxMiA4LjIwNTA3ODQsOC4yMDg5ODQgNC44NDQ1MjUsMC41MjczNiA4LjUwMDE1NiwtMy41MDYwOTcgOC4yMjY1NjIsLTguMjIwNzAzIEMgMjMuOTYwNjcyLDExLjM3MTk5NiAyMC44ODEwNiw3Ljg0NTcwMzEgMTYuMzQ5NjE0LDcuODQ1NzAzMSBaIG0gMCwwLjE5NTMxMjUgYyA0LjQzMDUzNCwwIDcuNDIyNDYxLDMuNDIyNzk5NCA3LjY3OTY4OCw3Ljg1NTQ2ODQgMC4yNjc0MTIsNC42MDgwODIgLTMuMjgzOTc4LDguNTMyMjI2IC04LjAxMTcxOSw4LjAxNzU3OCBDIDExLjk4OTA3NSwyMy40NzU1MzggOC4zMDcxODk5LDIwLjA3NTU5MyA3Ljk4NDM3NSwxNS44ODQ3NjYgNy44MTQ4NDYzLDEzLjY4MzgxOSA4Ljc5NTMxMDUsMTEuNzI2MzM4IDEwLjM3NSwxMC4zMTQ0NTMgMTEuOTU0Njg5LDguOTAyNTY4OSAxNC4xMzQzNyw4LjA0MTAxNTYgMTYuMzQ5NjA5LDguMDQxMDE1NiBaIG0gLTAuMDY4MzYsMS40MTYwMTU2IGMgLTEuODE3NjcyLDAgLTMuNjAxMzQyLDAuNzAzOTY4OCAtNC44OTg0MzgsMS44NjMyODA4IC0xLjI5NzA5NSwxLjE1OTMxIC0yLjEwODk2ODMsMi43NzkxODUgLTEuOTY4NzQ5NSw0LjU5OTYxIDAuMjY1NTI2OSwzLjQ0NzExMSAzLjI3Mzc1MDUsNi4yMjU4MTMgNi41ODIwMzE1LDYuNTg1OTM3IDMuODg3Mjk1LDAuNDIzMTYgNi44MjMwMDgsLTIuODE1MzE4IDYuNjAzNTE1LC02LjU5NzY1NiBDIDIyLjM4OTU0MSwxMi4yODgyMjIgMTkuOTE2NDk1LDkuNDU3MDMxMSAxNi4yODEyNSw5LjQ1NzAzMTIgWiBtIDAsMC4xOTUzMTI2IGMgMy41MzQzMzMsMCA1LjkxNzc0MiwyLjcyNzY5NjIgNi4xMjMwNDcsNi4yNjU2MjUyIDAuMjEzMzExLDMuNjc1ODE0IC0yLjYxNjIwOCw2LjgwMzAyNSAtNi4zODY3MTksNi4zOTI1NzggLTMuMjEzMjk4LC0wLjM0OTc4NSAtNi4xNTA3NTk3LC0zLjA2MjEzIC02LjQwODIwMywtNi40MDQyOTcgLTAuMTM1MTEyMiwtMS43NTQxMjcgMC42NDQyNTIsLTMuMzEzMjU3IDEuOTA0Mjk3LC00LjQzOTQ1MyAxLjI2MDA0NSwtMS4xMjYxOTYgMy4wMDA0NDEsLTEuODE0NDUzMyA0Ljc2NzU3OCwtMS44MTQ0NTMyIHogbSAtMC4wNzAzMSwxLjQxNjAxNTIgYyAtMS4zNjk1NzIsMCAtMi43MTIsMC41MzA2NzUgLTMuNjg5NDU0LDEuNDA0Mjk3IC0wLjk3NzQ1MywwLjg3MzYyMiAtMS41OTAxNzcsMi4wOTUxNDUgLTEuNDg0Mzc1LDMuNDY4NzUgMC4yMDAxNTYsMi41OTg0NTIgMi40NjU5LDQuNjg5NTUxIDQuOTU4OTg1LDQuOTYwOTM4IDIuOTMwMDcsMC4zMTg5NTggNS4xNDM5MDgsLTIuMTIyNTg3IDQuOTc4NTE1LC00Ljk3MjY1NiAtMC4xNTgxNDUsLTIuNzI1MjQ0IC0yLjAyNDYyMiwtNC44NjEzMjkgLTQuNzYzNjcxLC00Ljg2MTMyOSB6IG0gMCwwLjE5NTMxMyBjIDIuNjM4MTM1LDAgNC40MTQ5NzUsMi4wMzQ1NDQgNC41NjgzNTksNC42Nzc3MzQgMC4xNTkyMTEsMi43NDM1NDYgLTEuOTUwMzg2LDUuMDczODI0IC00Ljc2MzY3Miw0Ljc2NzU3OCAtMi4zOTgxMDIsLTAuMjYxMDQ3IC00LjU5MTEzMSwtMi4yODc3NDEgLTQuNzgzMjAzLC00Ljc4MTI1IC0wLjEwMDY5NiwtMS4zMDczMDggMC40Nzk1MTksLTIuNDcwMDM5IDEuNDE5OTIyLC0zLjMxMDU0NiAwLjk0MDQwMywtMC44NDA1MDggMi4yMzk1NTcsLTEuMzUzNTE2IDMuNTU4NTk0LC0xLjM1MzUxNiB6IG0gLTAuMDY4MzYsMS40MTYwMTYgYyAtMC45MjE0NzIsMCAtMS44MjI2NTcsMC4zNTU0MjUgLTIuNDgwNDY5LDAuOTQzMzU5IC0wLjY1NzgxMSwwLjU4NzkzNCAtMS4wNzMzMzksMS40MTUwMSAtMS4wMDE5NTMsMi4zNDE3OTcgMC4xMzQ3ODUsMS43NDk3OTIgMS42NTYwOTUsMy4xNTMyOTEgMy4zMzM5ODUsMy4zMzU5MzcgMS45NzI4NDYsMC4yMTQ3NTkgMy40NjY3NiwtMS40MzE4MDkgMy4zNTU0NjgsLTMuMzQ5NjA5IC0wLjEwNjIyNCwtMS44MzA1MDMgLTEuMzY0MTc3LC0zLjI3MTQ4NyAtMy4yMDcwMzEsLTMuMjcxNDg0IHogbSAwLDAuMTk1MzEyIGMgMS43NDE5NDIsMCAyLjkxMjIwOSwxLjMzOTQ0IDMuMDEzNjcyLDMuMDg3ODkxIDAuMTA1MTEsMS44MTEyNzYgLTEuMjg0NTYyLDMuMzQ2NTc3IC0zLjE0MDYyNSwzLjE0NDUzMSAtMS41ODI5MDcsLTAuMTcyMzA3IC0zLjAzMzQ1NSwtMS41MTMzNTUgLTMuMTYwMTU2LC0zLjE1ODIwMyAtMC4wNjYyOCwtMC44NjA0OSAwLjMxNDc4NSwtMS42MjQ4NjggMC45MzU1NDcsLTIuMTc5Njg4IDAuNjIwNzQ5LC0wLjU1NDgxOSAxLjQ4MDYyLC0wLjg5NDUzMSAyLjM1MTU1NiwtMC44OTQ1MzEgeiBtIC0wLjA2ODM2LDEuNDE2MDE2IGMgLTAuNDczMzY5LDAgLTAuOTM1MjcxLDAuMTgyMTI5IC0xLjI3MzQzOCwwLjQ4NDM3NSAtMC4zMzgxNjcsMC4zMDIyNDYgLTAuNTU0NTQ2LDAuNzMwOTY5IC0wLjUxNzU3OCwxLjIxMDkzNyAwLjA2OTQxLDAuOTAxMTMzIDAuODQ4MjQ5LDEuNjE4OTgxIDEuNzEwOTM4LDEuNzEyODkxIDEuMDE1NjE2LDAuMTEwNTU3IDEuNzg5NjE0LC0wLjc0MTAzMSAxLjczMjQyMSwtMS43MjY1NjMgLTAuMDU0MywtMC45MzU3NjYgLTAuNzA1NjkxLC0xLjY4MTY0IC0xLjY1MjM0MywtMS42ODE2NCB6IG0gMCwwLjE5NTMxMiBjIDAuODQ1NzQsMCAxLjQwNzQ5LDAuNjQ0MzMzIDEuNDU3MDMxLDEuNDk4MDQ3IDAuMDUxMDEsMC44NzkwMDggLTAuNjE2NzkzLDEuNjE5MzI5IC0xLjUxNTYyNSwxLjUyMTQ4NCAtMC43Njc3MDYsLTAuMDgzNTcgLTEuNDc1NzgsLTAuNzM4OTY3IC0xLjUzNzEwOSwtMS41MzUxNTYgLTAuMDMxODYsLTAuNDEzNjcxIDAuMTUwMDU1LC0wLjc3OTY5NyAwLjQ1MTE3MiwtMS4wNDg4MjggMC4zMDExMTYsLTAuMjY5MTMxIDAuNzIxNjk4LC0wLjQzNTU0NyAxLjE0NDUzMSwtMC40MzU1NDcgeiIgLz48L3N2Zz4=');}.icon-infinity{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yNDYsMTI4YTU0LDU0LDAsMCwxLTkyLjE4LDM4LjE4LDMuMDcsMy4wNywwLDAsMS0uMjUtLjI2bC02MC02Ny43NGE0Miw0MiwwLDEsMCwwLDU5LjY0bDguNTctOS42N2E2LDYsMCwxLDEsOSw4bC04LjY5LDkuODFhMy4wNywzLjA3LDAsMCwxLS4yNS4yNiw1NCw1NCwwLDEsMSwwLTc2LjM2LDMuMDcsMy4wNywwLDAsMSwuMjUuMjZsNjAsNjcuNzRhNDIsNDIsMCwxLDAsMC01OS42NGwtOC41Nyw5LjY3YTYsNiwwLDEsMS05LThsOC42OS05LjgxYTMuMDcsMy4wNywwLDAsMSwuMjUtLjI2QTU0LDU0LDAsMCwxLDI0NiwxMjhaIi8+PC9zdmc+');}.icon-arrow-counter-clockwise{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjIsMTI4YTk0LDk0LDAsMCwxLTkyLjc0LDk0SDEyOGE5My40Myw5My40MywwLDAsMS02NC41LTI1LjY1LDYsNiwwLDEsMSw4LjI0LTguNzJBODIsODIsMCwxLDAsNzAsNzBsLS4xOS4xOUwzOS40NCw5OEg3MmE2LDYsMCwwLDEsMCwxMkgyNGE2LDYsMCwwLDEtNi02VjU2YTYsNiwwLDAsMSwxMiwwVjkwLjM0TDYxLjYzLDYxLjRBOTQsOTQsMCwwLDEsMjIyLDEyOFoiLz48L3N2Zz4=');}.icon-clock{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xMjgsMjZBMTAyLDEwMiwwLDEsMCwyMzAsMTI4LDEwMi4xMiwxMDIuMTIsMCwwLDAsMTI4LDI2Wm0wLDE5MmE5MCw5MCwwLDEsMSw5MC05MEE5MC4xLDkwLjEsMCwwLDEsMTI4LDIxOFptNjItOTBhNiw2LDAsMCwxLTYsNkgxMjhhNiw2LDAsMCwxLTYtNlY3MmE2LDYsMCwwLDEsMTIsMHY1MGg1MEE2LDYsMCwwLDEsMTkwLDEyOFoiLz48L3N2Zz4=');}.icon-x-square{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMDgsMzRINDhBMTQsMTQsMCwwLDAsMzQsNDhWMjA4YTE0LDE0LDAsMCwwLDE0LDE0SDIwOGExNCwxNCwwLDAsMCwxNC0xNFY0OEExNCwxNCwwLDAsMCwyMDgsMzRabTIsMTc0YTIsMiwwLDAsMS0yLDJINDhhMiwyLDAsMCwxLTItMlY0OGEyLDIsMCwwLDEsMi0ySDIwOGEyLDIsMCwwLDEsMiwyWk0xNjQuMjQsMTAwLjI0LDEzNi40OCwxMjhsMjcuNzYsMjcuNzZhNiw2LDAsMSwxLTguNDgsOC40OEwxMjgsMTM2LjQ4bC0yNy43NiwyNy43NmE2LDYsMCwwLDEtOC40OC04LjQ4TDExOS41MiwxMjgsOTEuNzYsMTAwLjI0YTYsNiwwLDAsMSw4LjQ4LTguNDhMMTI4LDExOS41MmwyNy43Ni0yNy43NmE2LDYsMCwwLDEsOC40OCw4LjQ4WiIvPjwvc3ZnPg==');}.icon-eye-closed{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjkuMjEsMTY1YTYsNiwwLDAsMS0xMC40Miw2bC0yMC0zNS4wOGExMjIsMTIyLDAsMCwxLTM5LDE4LjA5bDYuMTcsMzdhNiw2LDAsMCwxLTQuOTMsNi45MSw2Ljg1LDYuODUsMCwwLDEtMSwuMDgsNiw2LDAsMCwxLTUuOTEtNUwxNDgsMTU2LjQ0YTEyOC44NiwxMjguODYsMCwwLDEtNDAsMEwxMDEuOTIsMTkzQTYsNiwwLDAsMSw5NiwxOThhNi44NSw2Ljg1LDAsMCwxLTEtLjA4QTYsNiwwLDAsMSw5MC4wOCwxOTFsNi4xNy0zN2ExMjIsMTIyLDAsMCwxLTM5LTE4LjA5TDM3LjIxLDE3MWE2LDYsMCwxLDEtMTAuNDItNmwyMC44NS0zNi40OGExNTIsMTUyLDAsMCwxLTIwLjMxLTIwLjc3LDYsNiwwLDAsMSw5LjM0LTcuNTRDNTMuNTQsMTIxLjExLDgzLjA3LDE0NiwxMjgsMTQ2czc0LjQ2LTI0Ljg5LDkxLjMzLTQ1Ljc3YTYsNiwwLDAsMSw5LjM0LDcuNTQsMTUyLDE1MiwwLDAsMS0yMC4zMSwyMC43N1oiLz48L3N2Zz4=');}.icon-star-half-fi{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMzkuMTgsOTcuMjZBMTYuMzgsMTYuMzgsMCwwLDAsMjI0LjkyLDg2bC01OS00Ljc2TDE0My4xNCwyNi4xNWExNi4zNiwxNi4zNiwwLDAsMC0zMC4yNywwTDkwLjExLDgxLjIzLDMxLjA4LDg2YTE2LjQ2LDE2LjQ2LDAsMCwwLTkuMzcsMjguODZsNDUsMzguODNMNTMsMjExLjc1YTE2LjQsMTYuNCwwLDAsMCwyNC41LDE3LjgyTDEyOCwxOTguNDlsNTAuNTMsMzEuMDhBMTYuNCwxNi40LDAsMCwwLDIwMywyMTEuNzVsLTEzLjc2LTU4LjA3LDQ1LTM4LjgzQTE2LjQzLDE2LjQzLDAsMCwwLDIzOS4xOCw5Ny4yNlptLTE1LjM0LDUuNDctNDguNyw0MmE4LDgsMCwwLDAtMi41Niw3LjkxbDE0Ljg4LDYyLjhhLjM3LjM3LDAsMCwxLS4xNy40OGMtLjE4LjE0LS4yMy4xMS0uMzgsMGwtNTQuNzItMzMuNjVBOCw4LDAsMCwwLDEyOCwxODEuMVYzMmMuMjQsMCwuMjcuMDguMzUuMjZMMTUzLDkxLjg2YTgsOCwwLDAsMCw2Ljc1LDQuOTJsNjMuOTEsNS4xNmMuMTYsMCwuMjUsMCwuMzQuMjlTMjI0LDEwMi42MywyMjMuODQsMTAyLjczWiIvPjwvc3ZnPg==');}.icon-star-fi{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMzQuMjksMTE0Ljg1bC00NSwzOC44M0wyMDMsMjExLjc1YTE2LjQsMTYuNCwwLDAsMS0yNC41LDE3LjgyTDEyOCwxOTguNDksNzcuNDcsMjI5LjU3QTE2LjQsMTYuNCwwLDAsMSw1MywyMTEuNzVsMTMuNzYtNTguMDctNDUtMzguODNBMTYuNDYsMTYuNDYsMCwwLDEsMzEuMDgsODZsNTktNC43NiwyMi43Ni01NS4wOGExNi4zNiwxNi4zNiwwLDAsMSwzMC4yNywwbDIyLjc1LDU1LjA4LDU5LDQuNzZhMTYuNDYsMTYuNDYsMCwwLDEsOS4zNywyOC44NloiLz48L3N2Zz4=');}
assets/js/concise/CRUD.js
@@ -402,7 +402,7 @@
                    keyPath: 'id',
                    endpoint: this.endpoint??'content', //for taxonomy stores
                    headers: {
                        'action_nonce': window.auth.getNonce('dash'),
                        'X-Action-Nonce': window.auth.getNonce('dash'),
                    },
                    indexes: [
                        {name: 'id', keyPath: 'id'},
@@ -482,16 +482,29 @@
        //  }
        // });
        if (window.jvbUploads) {
            window.jvbUploads.subscribe((event, data) => {
                if (event === 'groups_uploaded' && data.content === this.content) {
                    this.handleGroupsUploaded(data);
                }
            });
        }
        this.queue.subscribe((event, data) => {
            if (['image_upload', 'video_upload', 'document_upload'].includes(data.type)
                && event === 'operation-status'
                && data.status === 'completed') {
                this.store.clearCache();
            }
            console.log('CRUD.js queue subscription');
            console.log(event, data);
            if (event === 'operation-status'
                && data.status === 'completed'
                && data.endpoint === 'uploads/groups') {
                console.log('Grouped Uploads completed');
                if (data.result && data.result.group_mappings) {
                    this.handleGroupMappings(data.result.group_mappings);
                }
                console.log('Cleared local cache. Refresh to see changes');
                this.store.clearCache();
            }
@@ -508,9 +521,7 @@
                }
                // Get successfully processed post IDs
                const successfulIds = Object.keys(data.result.posts).filter(id => {
                    return data.result.posts[id]?.success === true;
                });
                const successfulIds = Object.keys(data.result.posts);
                if (successfulIds.length === 0) {
                    return;
@@ -752,7 +763,10 @@
        this.changes.get(itemId)[name] = value;
        this.scheduleBackup();
        this.scheduleSave();
        //Only send actual itemIds to server. If this is a recently uploaded item, just store changes for now
        if (typeof itemId === 'number' || !itemId.includes('group')) {
            this.scheduleSave();
        }
    }
    scheduleBackup() {
        window.debouncer.schedule(
@@ -1201,7 +1215,7 @@
        let operation = {
            endpoint: this.endpoint,
            headers: {
                'action_nonce': window.auth.getNonce('dash'),
                'X-Action-Nonce': window.auth.getNonce('dash'),
            },
            data: {
                posts: allChanges,
@@ -1400,6 +1414,102 @@
        });
    }
    /***************************************************************
     UPLOAD GROUP SUPPORT
     Handles:
        - immediate UI feedback once the uploaded groups are sent to server
    ***************************************************************/
    handleGroupsUploaded(data) {
        const { posts, fieldId } = data;
        let uploader = window.jvbUploads;
        let field = uploader.fields.get(fieldId);
        console.log(posts);
        console.log(field);
        let added = [];
        posts.forEach(post => {
            const placeholderPost = {
                id: post.groupId,
                title: post.fields.post_title || `New ${this.singular}`,
                status: 'draft',
                date: new Date().toISOString(),
                modified: new Date().toISOString(),
                thumbnail: null,
                icon: this.content,
                taxonomies: {},
                fields: post.fields,
                images: {},
            };
            post.images.forEach((uploadId, index) => {
                let id = uploadId['upload_id'];
                if (index === 0) {
                    placeholderPost.fields['post_thumbnail'] = uploadId;
                }
                let upload = uploader.stores.uploads.get(id);
                if (upload) {
                    placeholderPost.images[id] = {
                        'image-alt-text': '',
                        'image-caption': '',
                        'image-title': upload.fields.originalName,
                        medium: uploader.createPreviewUrl(uploader.formatFile(upload))
                    };
                }
            });
            //
            // // Add to store (won't persist since it's a fake ID)
            // this.store.data.set(post.groupId, placeholderPost);
            //
            //
            // // Render immediately
            // let element;
            // switch (this.view) {
            //  case 'grid':
            //      element = this.renderGridItem(placeholderPost);
            //      this.ui.grid.prepend(element);
            //      break;
            //  case 'list':
            //      element = this.renderListItem(placeholderPost);
            //      this.ui.grid.prepend(element);
            //      break;
            //  case 'table':
            //      element = this.renderTableItem(placeholderPost);
            //      if (this.ui.table.body) {
            //          this.ui.table.body.prepend(element);
            //      }
            //      break;
            // }
            // element.classList.add('uploading');
            added.push(placeholderPost);
        });
        this.store.saveMany(added).then(() => this.render());
        this.a11y.announce(`${posts.length} ${posts.length === 1 ? this.singular : this.plural} created. Waiting for server confirmation...`);
    }
    handleGroupMappings(mappings) {
        console.log('[CRUD] Applying group mappings:', mappings);
        // mappings = { "group_abc123": 456, "group_def456": 789 }
        for (const [groupId, postId] of Object.entries(mappings)) {
            // Get any pending changes for this temp item
            let changes = {};
            if (this.changes.has(groupId)) {
                changes = this.changes.get(groupId);
                this.changes.delete(groupId);
            }
            let storedChanges = this.changesStore.get(groupId)??{};
            if (changes.size > 0 || storedChanges.size > 0) {
                changes = window.deepMerge(storedChanges, changes);
                this.changes.set(postId, changes);
                this.scheduleBackup();
            }
        }
    }
    /***************************************************************
     UTILITY
    ***************************************************************/
    shouldRemoveItemUI(newStatus) {
assets/js/concise/CRUDOld.js
@@ -25,7 +25,7 @@
                keyPath: 'id',
                endpoint: 'content',
                headers: {
                    'action_nonce': window.auth.getNonce('dash'),
                    'X-Action-Nonce': window.auth.getNonce('dash'),
                },
                indexes: [
                    {name: 'id', keyPath: 'id'},
@@ -194,7 +194,7 @@
        let operation = {
            endpoint: 'content',
            headers: {
                'action_nonce': window.auth.getNonce('dash'),
                'X-Action-Nonce': window.auth.getNonce('dash'),
            },
            data: {
                posts: changes,
assets/js/concise/ContentManager.js
@@ -361,7 +361,7 @@
                    headers: {
                        'Content-Type': 'application/json',
                        'X-WP-Nonce': window.auth.getNonce(),
                        'action_nonce': window.auth.getNonce('dash'),
                        'X-Action-Nonce': window.auth.getNonce('dash'),
                    },
                },
                {
@@ -374,7 +374,7 @@
            //     headers: {
            //         'Content-Type': 'application/json',
            //         'X-WP-Nonce': window.auth.getNonce(),
            //         'action_nonce': window.auth.getNonce('dash'),
            //         'X-Action-Nonce': window.auth.getNonce('dash'),
            //     }
            // });
            // const data = await response.json();
assets/js/concise/FavouritesManager.js
@@ -335,7 +335,7 @@
                    method: 'GET',
                    headers: {
                        'X-WP-Nonce': window.auth.getNonce(),
                        'action_nonce': window.auth.getNonce('favourites'),
                        'X-Action-Nonce': window.auth.getNonce('favourites'),
                    }
                },{
                    context: 'favouritesManager',
@@ -1023,7 +1023,7 @@
                    method: 'GET',
                    headers: {
                        'X-WP-Nonce': window.auth.getNonce(),
                        'action_nonce': window.auth.getNonce('favourites')
                        'X-Action-Nonce': window.auth.getNonce('favourites')
                    }
                },
                {
@@ -1187,7 +1187,7 @@
                    method: 'GET',
                    headers: {
                        'X-WP-Nonce': window.auth.getNonce(),
                        'action_nonce': window.auth.getNonce('favourites')
                        'X-Action-Nonce': window.auth.getNonce('favourites')
                    }
                },
                {
@@ -1601,7 +1601,7 @@
                    method: 'GET',
                    headers: {
                        'X-WP-Nonce': window.auth.getNonce(),
                        'action_nonce': window.auth.getNonce('favourites')
                        'X-Action-Nonce': window.auth.getNonce('favourites')
                    }
                },
                {
assets/js/concise/NewsManager.js
@@ -213,7 +213,7 @@
                    method: 'GET',
                    headers: {
                        'X-WP-Nonce': window.auth.getNonce(),
                        'action_nonce': window.auth.getNonce('dash'),
                        'X-Action-Nonce': window.auth.getNonce('dash'),
                    }
                },{
                    context: 'news',
assets/js/concise/NotificationManager.js
@@ -87,7 +87,7 @@
                    method: 'GET',
                    headers: {
                        'X-WP-Nonce': window.auth.getNonce(),
                        'action_nonce': jvbAdmin.nonce,
                        'X-Action-Nonce': jvbAdmin.nonce,
                    }
                },{
                    context: 'admin',
assets/js/concise/Notifications.js
@@ -93,7 +93,7 @@
                    method: 'GET',
                    headers: {
                    'X-WP-Nonce': window.auth.getNonce(),
                    'action_nonce': window.auth.getNonce('notifications')
                    'X-Action-Nonce': window.auth.getNonce('notifications')
                    }
                }, {
                    context: 'notifications',
@@ -278,7 +278,7 @@
                    method: 'POST',
                    headers: {
                        'X-WP-Nonce': window.auth.getNonce(),
                        'action_nonce': window.auth.getNonce('dash'),
                        'X-Action-Nonce': window.auth.getNonce('dash'),
                    },
                    body: {
                        notification: notificationId,
@@ -339,7 +339,7 @@
            const response = await fetch(`${jvbSettings.api}notifications?${params.toString()}`, {
                headers: {
                    'X-WP-Nonce': window.auth.getNonce(),
                    'action_nonce': window.auth.getNonce('dash'),
                    'X-Action-Nonce': window.auth.getNonce('dash'),
                    'If-Modified-Since': this.lastCheck,
                }
            });
assets/js/concise/Queue.js
@@ -288,7 +288,8 @@
                storeName: 'queue',
                keyPath: 'id',
                endpoint: this.endpoint,
                TTL: Infinity,
                // TTL: Infinity,
                TTL: 5000,
                indexes: [
                    {name: 'status', keyPath: 'status'},
                    {name: 'type', keyPath: 'type'},
@@ -485,7 +486,7 @@
                    },
                    body: JSON.stringify({
                        action,
                        ids: statusOrId,
                        ids: Array.isArray(statusOrId) ? statusOrId : [statusOrId],
                        user: this.user
                    })
                }
@@ -932,20 +933,28 @@
                item.ui.actions.refresh.hidden = op.status !== 'completed';
            }
        }
        getProgress(op) {
            if (op.progress) return op.progress;
            if (!this.statuses.includes(op.status)) return 0;
            let statusProgress = {
                'queued': 10,
                'uploading': 25,
                'pending': 40,
                'processing':70,
                'completed':100,
                'failed':0,
                'failed_permanent':0
            };
            return statusProgress[op.status]??0;
    getProgress(op) {
        // Check server-provided percentage first
        if (op.progress_percentage !== undefined) {
            return op.progress_percentage;
        }
        // Legacy: check old 'progress' field
        if (op.progress !== undefined) {
            return op.progress;
        }
        // Fallback to status-based calculation
        if (!this.statuses.includes(op.status)) return 0;
        const statusProgress = {
            'queued': 10,
            'uploading': 25,
            'pending': 40,
            'processing': 70,
            'completed': 100,
            'failed': 0,
            'failed_permanent': 0
        };
        return statusProgress[op.status] ?? 0;
    }
    removeOperationUI(opId) {
        let op = this.items.get(opId);
        if (!op) return;
@@ -970,7 +979,8 @@
            'processing': 'Processing',
            'completed': 'Completed',
            'failed': 'Failed',
            'failed_permanent': 'Failed permanently'
            'failed_permanent': 'Failed permanently',
            'merged': 'Merged'
        };
        return labels[status];
    }
@@ -986,9 +996,24 @@
            case 'pending':
                return item.position ? `Position ${item.position} in queue` : 'In server queue';
            case 'processing':
                return item.progress ? `${item.progress}% complete` : 'Processing...';
                // Show progress count if available
                if (item.count && item.progress_count !== undefined) {
                    const processed = item.progress_count;
                    const total = item.count;
                    const percentage = Math.round((processed / total) * 100);
                    return `Processing ${processed}/${total} items (${percentage}%)`;
                }
                // Fallback to percentage only
                if (item.progress_percentage !== undefined) {
                    return `${item.progress_percentage}% complete`;
                }
                return 'Processing...';
            case 'completed':
                return 'Successfully completed. Refresh to see changes.';
            case 'merged':
                return item.merged_into
                    ? `Merged with another operation (${item.merged_into.substring(0, 8)}...)`
                    : 'Merged with another operation';
            case 'failed':
                return `Failed: ${item.lastError || 'Unknown error'} (Retry ${item.retries}/${2})`;
            case 'failed_permanent':
@@ -1017,25 +1042,55 @@
        // If we have local operation data, preserve it
        if (localOp && localOp.endpoint) {
            return {
            const mappedOp = {
                ...localOp,
                ...serverOp,
                endpoint: localOp.endpoint,
                method: localOp.method,
                headers: localOp.headers,
                progress_percentage: serverOp.progress_percentage,
                progress_count: serverOp.progress_count,
                count: serverOp.count
            };
            if (serverOp.merged_into) {
                this.handleMergedOperation(mappedOp);
            }
        }
        // Minimal mapping for server-only operations
        // Extract endpoint from type if possible, otherwise use type
        const endpoint = serverOp.type ? serverOp.type.replace('_update', '').replace('_', '/') : 'unknown';
        return {
        const mappedOp = {
            ...serverOp,
            endpoint: endpoint,
            method: 'POST',
            headers: { ...this.headers },
        };
        if (serverOp.merged_into) {
            this.handleMergedOperation(mappedOp);
        }
        return mappedOp
    }
    /**
     * Handle merged operations
     * The target operation already has merged data from server,
     * so we just need to clean up the merged operation locally
     */
    handleMergedOperation(operation) {
        if (!operation.merged_into) return;
        console.log(`[Queue] Operation ${operation.id} merged into ${operation.merged_into}`);
        // Auto-dismiss merged operation after brief display
        // The target operation already has all the merged data from server
        setTimeout(() => {
            this.store.delete(operation.id);
            this.removeOperationFromUI(operation.id);
        }, 3000);
    }
    /****************************************************************************
assets/js/concise/UploadManager.js
@@ -298,17 +298,83 @@
        this.queue.subscribe((event, operation) => {
            if ((event === 'operation-status' || event === 'cancel-operation')
                && ['image_upload', 'video_upload', 'document_upload'].includes(operation.type)) {
                const data = operation.data instanceof FormData
                    ? this.stores.uploads.formDataToObject(operation.data)
                    : operation.data;
                let uploadIds = [];
                let uploads = data['upload_ids'];
                if (!uploads || uploads.length === 0) return;
                if (event === 'cancel-operation') return this.handleOperationCancelled(uploads);
                this.setBulkUpload(uploads, 'status', operation.status).then(()=>{});
                if (operation.data) {
                    // Handle FormData
                    if (operation.data instanceof FormData) {
                        const dataObj = this.stores.uploads.formDataToObject(operation.data);
                        uploadIds = dataObj['upload_ids'] || [];
                    }
                    // Handle regular object
                    else {
                        uploadIds = operation.data['upload_ids'] || [];
                    }
                }
                // If not in data, check result (for completed operations from backend)
                if (uploadIds.length === 0 && operation.result && operation.result.upload_ids) {
                    uploadIds = operation.result.upload_ids;
                }
                // Still no upload_ids? Log warning and bail
                if (!uploadIds || uploadIds.length === 0) {
                    console.warn('[UploadManager] No upload_ids found for operation:', {
                        id: operation.id,
                        type: operation.type,
                        status: operation.status,
                        hasData: !!operation.data,
                        hasResult: !!operation.result
                    });
                    return;
                }
                // Handle cancellation
                if (event === 'cancel-operation') {
                    return this.handleOperationCancelled(uploadIds);
                }
                // Update upload status based on operation status
                this.setBulkUpload(uploadIds, 'status', operation.status).then(() => {
                    // Log for debugging
                    console.log(`[UploadManager] Updated ${uploadIds.length} uploads to status: ${operation.status}`);
                });
                // Handle completion
                if (operation.status === 'completed') {
                    uploads.forEach(upload => {
                        this.removeUpload(upload).then(()=>{});
                    // For group uploads, mark as processed but keep for reference
                    if (operation.type === 'process_upload_groups') {
                        uploadIds.forEach(uploadId => {
                            this.setBulkUpload([uploadId], 'serverProcessed', true).then(() => {});
                        });
                        // Log created posts if available
                        if (operation.result && operation.result.created_posts) {
                            console.log('[UploadManager] Created posts:', operation.result.created_posts);
                        }
                        // Remove uploads after a delay to allow UI to update
                        setTimeout(() => {
                            uploadIds.forEach(uploadId => {
                                this.removeUpload(uploadId).then(() => {});
                            });
                        }, 2000);
                    }
                    // For direct uploads, remove immediately
                    else {
                        uploadIds.forEach(uploadId => {
                            this.removeUpload(uploadId).then(() => {});
                        });
                    }
                }
                // Handle failures
                if (operation.status === 'failed' || operation.status === 'failed_permanent') {
                    console.error('[UploadManager] Operation failed:', {
                        id: operation.id,
                        type: operation.type,
                        uploadIds: uploadIds,
                        error: operation.error_message
                    });
                }
            }
@@ -348,7 +414,7 @@
            fields: {
                field: '[data-upload-field]',
                input: 'input[type="file"]',
                dropZone: '.file-upload-container',
                dropZone: '.file-upload-wrapper',
                preview: '.preview-wrap',
                grid: '.item-grid.preview',
                progress: {
@@ -661,6 +727,13 @@
            if (details) {
                details.open = false;
            }
            this.notify('groups_uploaded', {
                fieldId: fieldId,
                posts: posts,
                content: field.config.content,
            });
        }
        if (operationId) {
            field.operationId = operationId;
@@ -693,7 +766,7 @@
            canMerge: mergable,
            sendNow: endpoint === 'uploads/groups',
            headers: {
                'action_nonce': window.auth.getNonce('dash')
                'X-Action-Nonce': window.auth.getNonce('dash')
            },
            append: '_upload'
        }
@@ -727,6 +800,7 @@
            const fields = this.collectGroupFieldsFromDOM(groupElement, group.id);
            const post = {
                groupId: group.id,
                images: [],
                fields: fields
            };
@@ -762,6 +836,7 @@
        const remaining = uploads.filter(u => !u.group);
        for (const upload of remaining) {
            const post = {
                groupId: window.generateID('group'),
                images: [],
                fields: {}
            };
assets/js/concise/UserSettings.js
@@ -193,7 +193,7 @@
//                  headers: {
//                      'Content-Type': 'application/json',
//                      'X-WP-Nonce': window.auth.getNonce(),
//                      'action_nonce': window.auth.getNonce('dash'),
//                      'X-Action-Nonce': window.auth.getNonce('dash'),
//                  },
//                  body: JSON.stringify({
//                      dark_mode: isDark,
assets/js/min/ContentManager.min.js
@@ -1 +1 @@
window.contentManager=class{constructor(e){this.config={content:"",plural:"",taxonomies:{},selectors:{container:".items-list",grid:".item-grid:not(.preview)",uploadZone:".file-upload-wrapper",statusFilters:".status-filters",dateFilters:".date-filters",taxonomyFilters:".taxonomy-filters",viewControls:".view-controls",bulkControls:".bulk-controls",scrollSentinel:".scroll-sentinel",editModal:".edit-modal",bulkEditModal:".bulk-edit-modal",clearButton:".clear-filters"},createPostPerFile:!0,uploadConfig:{mode:"direct",allowMultiple:!0,createPostPerFile:!0,maxSize:5242880,allowedTypes:["image/jpeg","image/png","image/gif","image/webp"]},...e},this.resetCache=!1,this.queueManager=window.jvbQueue,this.loadingManager=window.jvbLoading,this.cache=window.jvbCache,this.error=window.jvbError,this.state={selected:new Set,filters:{status:"all",taxonomies:{},date:null},view:localStorage.getItem(`${this.config.content}_view`)||"grid",loading:!1},this.queue={all:{items:new Map,page:1,hasMore:!0,totalPages:0},draft:{items:new Map,page:1,hasMore:!0,totalPages:0},publish:{items:new Map,page:1,hasMore:!0,totalPages:0},trash:{items:new Map,page:1,hasMore:!0,totalPages:0}},this.init()}async init(){this.elements={},Object.entries(this.config.selectors).forEach((([e,t])=>{this.elements[e]=document.querySelector(t)})),this.config.uploadConfig&&(this.fileUploader=new window.jvbFileUploader({...this.config.uploadConfig,content:this.config.content,fieldName:null})),this.initStatusFilters(),this.initDateFilters(),this.initTaxonomyFilters(),this.initClearFilters(),this.initViewControls(),this.initBulkControls(),this.initInfiniteScroll(),this.initModals(),await this.loadContent()}queueContentUpdate(e,t){const s={type:"content_update",data:{posts:{[e]:{content:this.config.content,...t}},content:this.config.content}};this.queueManager.addToQueue(s),this.updateLocalState(e,t)}queueBulkUpdate(e,t){const s={};e.forEach((e=>{s[e]={content:this.config.content,...t}}));const i={user:window.auth.getUser(),type:"content_update",data:{posts:s}};this.queueManager.addToQueue(i),e.forEach((e=>this.updateLocalState(e,t)))}updateLocalState(e,t){const s=this.queue[this.state.filters.status].items.get(e);if(s){Object.assign(s,t),this.queue[this.state.filters.status].items.set(e,s);const i=this.elements.grid.querySelector(`[data-id="${e}"]`);i&&this.updateItemElement(i,s)}}processFormData(e){const t={};for(const[s,i]of e.entries())if("status"===s)t.status=i;else if(s.startsWith("taxonomy_")){const e=s.replace("taxonomy_","");t.taxonomies||(t.taxonomies={}),t.taxonomies[e]=Array.isArray(i)?i:[i]}else t[s]=i;return t}updateItemElement(e,t){e.classList.remove("draft","publish","trash"),e.classList.add(t.status);const s=e.querySelector(".action-status");s&&(removeChildren(s),s.append(getIcon(t.status))),t.taxonomies&&e.querySelectorAll(".label-group").forEach((e=>{const s=e.dataset.taxonomy;if(s&&t.taxonomies[s]){const i=t.taxonomies[s].terms;e.querySelector(".terms").innerHTML=this.renderTerms(i)}}))}handleItemAction(e,t){const s=t.dataset.id;switch(e){case"edit":this.editModal.handleOpen(),this.openEditModal(t),this.editModal.form&&new FormFields(this.editModal.form,{onSave:this.editModal.onSave(),itemID:t.dataset.id});break;case"restore":this.queueContentUpdate(s,{status:"draft"}),t.remove();break;case"trash":this.queueContentUpdate(s,{status:"trash"}),t.remove();break;case"delete":confirm(`Hold up! Are you sure you want to permanently delete this ${this.config.content}?\n\nThis is a forever kind of deal - no taking it back.`)&&(this.queueContentUpdate(s,{status:"delete"}),t.remove());break;case"toggle-status":const e="publish"===t.dataset.status?"draft":"publish";this.queueContentUpdate(s,{status:e}),t.dataset.status=e,removeChildren(t.querySelector(".action-status")),t.querySelector(".action-status").append(getIcon(e))}}async handleBulkOperation(e,t){window.jvbLoading.show("Processing bulk changes...");try{const s={};t.forEach((t=>{s[t]={content:this.config.content,status:e},["delete","trash","restore"].includes(e)&&document.querySelector('[data-id="'+t+'"]').remove()})),this.queueManager.addToQueue({type:"content_update",data:{posts:s}}),this.clearSelection(),this.showNotification("Bulk changes queued for processing")}catch(e){console.error("Bulk operation failed:",e),this.showNotification("Failed to queue bulk operation","error")}finally{window.jvbLoading.hide()}}getQueryKey(){return JSON.stringify({status:this.state.filters.status,page:this.state.page,filters:this.state.filters})}toggleItemSelection(e,t){const s=e.dataset.id;t?(this.state.selected.add(s),e.classList.add("selected"),e.querySelector("input[type=checkbox]").checked=!0):(this.state.selected.delete(s),e.classList.remove("selected"),e.querySelector("input[type=checkbox]").checked=!1)}async loadContent(e=!0){if(!this.state.loading)try{this.state.loading=!0,this.loadingManager.show();const t=this.state.filters.status;console.log("Loading Page: "),console.log(this.queue[t].page);const s=new URLSearchParams;s.set("type",this.config.content),s.set("page",this.queue[t].page),s.set("filters",JSON.stringify(this.state.filters)),s.set("user",window.auth.getUser()),e&&(this.queue[t].page=1,this.queue[t].items.clear(),removeChildren(this.elements.grid),this.elements.grid.classList.remove("empty"));const i=await this.cache.fetchWithCache(`${jvbSettings.api}content?`+s,{method:"GET",headers:{"Content-Type":"application/json","X-WP-Nonce":window.auth.getNonce(),action_nonce:window.auth.getNonce("dash")}},{context:window.auth.getUser()+"-"+this.config.content,forceRefresh:!1});i.total>0?(this.elements.grid.classList.remove("empty"),i.items.forEach((e=>{this.queue[t].items.set(e.id,e)})),this.queue[t].page++,this.queue[t].totalPages=i.total_pages,this.queue[t].hasMore=this.queue[t].page<i.total_pages):(this.elements.grid.classList.add("empty"),this.elements.grid.innerHTML=`<div class="empty-state"><h3>${jvbSettings.icons[this.config.content]}Nothing here${jvbSettings.icons[this.config.content]}</h3><p>It doesn't look like you have any ${this.config.plural} yet.</p><p><small><i>Add some by uploading images above.</i></small></p></div>`,this.queue[t].page=1,this.queue[t].hasMore=!1),this.renderContent()}catch(e){console.error("Error loading content:",e),this.loadingManager.showError("Failed to load content")}finally{this.state.loading=!1,this.loadingManager.hide()}}renderContent(){const e=this.state.filters.status,t=this.queue[e].items;t.size>0&&(this.elements.grid.classList.remove("empty"),this.elements.grid.querySelector(".empty-state")&&removeChildren(this.elements.grid));const s=document.createDocumentFragment();t.forEach((t=>{const i=this.elements.grid.querySelector(`[data-id="${t.id}"]`);if(i){if(t.view!==this.state.view){const e=this.createItemElement(t);t.view=this.state.view,i.replaceWith(e)}}else{const e=this.createItemElement(t);t.view=this.state.view,s.appendChild(e)}this.queue[e].items.set(t.id,t)})),s.children.length>0&&this.elements.grid.appendChild(s)}createItemElement(e){let t=window.getTemplate(this.state.view+"View");t.classList.add(e.status),t.dataset.id=e.id,t.dataset.fields=JSON.stringify(e.fields),t.dataset.status=e.status,t.dataset.img=e.thumbnail;let s=t.querySelector(".gallery");if(e.images){t.dataset.images=e.images;let o=s.querySelector("img");for(var i of e.images){let e=o.cloneNode(!0);e.src=i.src,i.alt&&(e.alt=i.alt),s.appendChild(e)}o.remove()}else s.remove();let o=[],a=t.querySelector(".taxonomies"),n=a.querySelector(".label-group"),l=n.querySelector(".tax"),r=!1;for(let s in e.taxonomies){if(Object.keys(e.taxonomies[s].terms).length>0){r=!0,t.dataset[s]=JSON.stringify(e.taxonomies[s].terms);let i=n.cloneNode(!0),o=jvbSettings.icons[s];for(var c in i.innerHTML=o+i.innerHTML,i.querySelector(".screen-reader-text").textContent=e.taxonomies[s].name,e.taxonomies[s].terms){let e=l.cloneNode(!0);e.textContent=c.name,i.appendChild(e)}}else t.dataset[s]=JSON.stringify({});o.push(s)}r?(n.remove(),l.remove()):a.remove(),0===Object.keys(this.config.taxonomies).length&&(this.config.taxonomies=o);let d=t.querySelector("img");d.src=e.thumbnail,e.alt&&(d.alt=e.alt),t.querySelector(".date").textContent=formatDate(e.date);let u="Hide "+e.icon;"draft"===e.status&&(u="Show "+e.icon);let h=t.querySelector('button[data-action="toggle-status"]');return h.prepend(getIcon(e.status)),h.title=u,this.initItemEventListeners(t),t}initItemEventListeners(e){e.addEventListener("click",(t=>t.target.closest(".item-select")?(t.preventDefault(),this.toggleItemSelection(e,!e.classList.contains("selected")),void this.updateBulkControls()):t.target.closest(".action")?(t.preventDefault(),void this.handleItemAction(t.target.closest(".action").dataset.action,e)):void 0))}initInfiniteScroll(){this.elements.scrollSentinel&&new IntersectionObserver((e=>{e.forEach((e=>{e.isIntersecting&&this.queue[this.state.filters.status].hasMore&&this.loadContent(!1)}))})).observe(this.elements.scrollSentinel)}initStatusFilters(){const e=this.elements.container.querySelector(".controls");e&&e.addEventListener("change",(e=>{if("radio"===e.target.type&&"status-filters"===e.target.name){const t=e.target.id;t!==this.state.filters.status&&(this.state.filters.status=t,this.updateBulkActionOptions(),0===this.queue[t].items.size?this.loadContent(!0):this.renderContent())}}))}initDateFilters(){const e=this.elements.container.querySelector("select.date-filter"),t=this.elements.container.querySelector(".date-range");let s;if(e&&(this.hasFilters=!0,e.addEventListener("change",(e=>{const i=e.target.value;if(s=i,"custom"===i)return void t.showModal();t.close();const o=t.querySelector(".month-select");o&&(o.value=""),this.setDateFilter(i)})),e.addEventListener("click",(i=>{"custom"===s&&"custom"===e.value&&t.showModal()}))),t){const e=t.querySelector(".date-start"),s=t.querySelector(".date-end"),i=t.querySelector(".month-select");i&&i.addEventListener("change",(e=>{const[s,i]=e.target.value.split("-");if(s&&i){const e=new Date(s,i-1,1),o=new Date(s,i,0);o.setHours(23,59,59,999),this.setDateFilter("custom",e,o),t.close()}}));const o=()=>{const i=e.value,o=s.value;if(i&&o){const e=new Date(i),s=new Date(o);s.setHours(23,59,59,999),this.setDateFilter("custom",e,s),t.close()}};e.addEventListener("change",o),s.addEventListener("change",o)}}setDateFilter(e,t=null,s=null){const i=new Date;i.setHours(23,59,59,999);let o=t,a=s||i;if(!t&&""!==e)switch(o=new Date,e){case"today":o.setHours(0,0,0,0);break;case"week":o.setDate(i.getDate()-7);break;case"month":o.setMonth(i.getMonth()-1);break;case"year":o.setFullYear(i.getFullYear()-1)}this.state.filters.date=e?{range:{after:o.toISOString(),before:a.toISOString()},custom:"custom"===e}:{range:null,custom:!1},this.updateClearFiltersButton(),this.state.page=1,this.loadContent()}initTaxonomyFilters(){const e=this.elements.container.querySelectorAll(".filter[data-taxonomy]");e.length&&(this.hasFilters=!0,e.forEach((e=>{e.addEventListener("change",(e=>{const t=e.target.dataset.taxonomy,s=e.target.value;s?this.state.filters.taxonomies[t]=[parseInt(s)]:delete this.state.filters.taxonomies[t],this.updateClearFiltersButton(),this.state.page=1,this.loadContent(!0)}))})))}updateClearFiltersButton(){const e=document.querySelector(this.config.selectors.clearButton);if(!e)return;const t=Object.keys(this.state.filters.taxonomies).length>0||null!==this.state.filters.date.range;e.hidden=!t}clearAllFilters(){this.elements.container.querySelectorAll(".filter[data-taxonomy]").forEach((e=>e.value=""));const e=this.elements.container.querySelector("select.date-filter");e&&(e.value=""),this.state.filters={date:{range:null,custom:!1},taxonomies:{}},this.updateClearFiltersButton(),this.state.page=1,this.loadContent(!0)}initClearFilters(){this.config.selectors.clearButton&&document.querySelector(this.config.selectors.clearButton).addEventListener("click",(()=>this.clearAllFilters()))}initViewControls(){const e=this.elements.container.querySelector(".view-controls");if(!e)return;e.addEventListener("change",(e=>{const t=e.target;"radio"===t.type&&(this.setView(t.value),this.loadContent(!0))}));const t=localStorage.getItem(`${this.config.content}_view`)||"grid",s=e.querySelector(`input[value="${t}"]`);s&&(s.checked=!0,this.setView(t))}setView(e){this.state.view=e;const t=new Set(this.state.selected);this.elements.grid.classList.remove("grid-view","list-view"),this.elements.grid.classList.add(`${e}-view`),localStorage.setItem(`${this.config.content}_view`,e),this.loadContent(!0),t.forEach((e=>{const t=this.elements.grid.querySelector(`[data-id="${e}"]`);if(t){const e=t.querySelector('input[type="checkbox"]');e&&(e.checked=!0,t.classList.add("selected"))}})),this.updateBulkControls()}initBulkControls(){if(!this.elements.bulkControls)return;this.selectAll=this.elements.bulkControls.querySelector(".select-all"),this.selectAll&&this.selectAll.addEventListener("change",(()=>{this.getVisibleItems().forEach((e=>{this.toggleItemSelection(e,this.selectAll.checked)})),this.updateBulkControls()}));const e=this.elements.bulkControls.querySelector(".bulk-action-select"),t=this.elements.bulkControls.querySelector(".apply-bulk");t&&e&&(this.updateBulkActionOptions(),this.elements.container.querySelector(".status-filters"),t.addEventListener("click",(()=>{const t=e.value;if(!t)return;const s=Array.from(this.state.selected);switch(t){case"restore":this.handleBulkOperation("restore",s);break;case"delete":confirm(`Hold up! Are you sure you want to permanently delete these ${this.config.plural}?\n\nThis is a forever kind of deal - no taking it back.`)&&this.handleBulkOperation("delete",s);break;case"trash":this.handleBulkOperation("trash",s);break;case"edit":this.openBulkEditModal();const e=document.querySelector(".bulk-edit-modal");if(e){const t=e.querySelector(".selected-count");t&&(t.textContent=`( ${s.length} items )`);const i=e.querySelector(".selected");if(i){let e="";s.forEach((t=>{let s=this.elements.grid.querySelector('[data-id="'+t+'"]');e+='<input type="checkbox" id="selected-'+t+'" name="posts" value="'+t+'" checked><label for="selected-'+t+'"><img width="100%" height="auto" src="'+s.dataset.img+'"></label>'})),i.innerHTML=e}}break;case"publish":case"draft":this.handleBulkOperation(t,s)}e.value=""})));const s=this.elements.bulkControls.querySelector(".cancel-bulk");s&&s.addEventListener("click",(()=>{this.clearSelection()}))}updateBulkActionOptions(){const e=this.elements.bulkControls.querySelector(".bulk-action-select");e&&("trash"===this.state.filters.status?e.innerHTML='\n            <option value="">Bulk Actions...</option>\n            <option value="restore">Restore</option>\n            <option value="delete">Permanently Delete</option>\n        ':e.innerHTML='\n            <option value="">Bulk Actions...</option>\n            <option value="edit">Edit</option>\n            <option value="publish">Show</option>\n            <option value="draft">Hide</option>\n            <option value="trash">Scrap</option>\n        ')}initModals(){this.elements.editModal&&(this.editModal=new window.jvbModal(this.elements.editModal,{open:!1,close:this.elements.editModal.querySelector(".cancel"),save:this.elements.editModal.querySelector(".save"),onSave:()=>{const e=new FormData(this.elements.editModal.querySelector("form"));let t={};const s=this.elements.editModal.querySelectorAll(".taxonomies .jvb-selector");let i=Object.fromEntries(e);s.forEach((e=>{const s=e.dataset.taxonomy.replace(jvbSettings.base||"jvb_","");if(delete i["edit-"+s],e.__instance){const i=e.__instance.selectedItems;i&&Object.keys(i).length>0&&(t[s]=Object.keys(i).join(","))}})),i.taxonomies=t;for(let[e,t]of Object.entries(i))""!==t&&0!==Object.keys(t).length||delete i[e];this.queueContentUpdate(this.elements.editModal.dataset.id,i)}}));const e=this.elements.bulkEditModal;if(e){let t=!1;const s=e.querySelector("form");s?.addEventListener("change",(()=>{t=!0})),e.addEventListener("keydown",(s=>{"Escape"===s.key&&(s.preventDefault(),this.handleModalClose(e,t))})),e.addEventListener("click",(s=>{s.target===e&&this.handleModalClose(e,t)})),e.querySelector(".cancel")?.addEventListener("click",(()=>{this.handleModalClose(e,t),this.clearSelection()})),e.querySelector(".save")?.addEventListener("click",(()=>{const i=new FormData(s),o=Array.from(i.getAll("posts")),a={};""===i.get("term_name")&&(i.delete("term_name"),i.delete("select_parent"));let n={};e.querySelectorAll(".taxonomies .jvb-selector").forEach((e=>{const t=e.dataset.taxonomy.replace(jvbSettings.base||"jvb_","");if(e.__instance){const s=e.__instance.selectedItems;s&&Object.keys(s).length>0&&(n[t]=Object.keys(s).join(","))}})),o.forEach((e=>{a[e]={append:!0,content:this.config.content,status:i.get("bulk_status"),taxonomies:n}})),this.queueManager.addToQueue({type:"content_update",data:{posts:a}}),t=!1,e.close(),this.clearSelection()})),e.addEventListener("submit",(i=>{const o=new FormData(s),a=Array.from(o.getAll("posts")),n={};""===o.get("term_name")&&(o.delete("term_name"),o.delete("select_parent"));let l={};for(const e of this.config.taxonomies)l[e]=o.getAll(e),o.delete(e);a.forEach((e=>{n[e]={append:!0,content:this.config.content,status:o.get("bulk_status"),taxonomies:l}})),this.queueManager.addToQueue({type:"content_update",data:{posts:n}}),t=!1,e.close(),this.clearSelection()}))}this.openEditModal=e=>{console.log("Openening whatsit");const t=this.editModal.modal;if(!t)return;console.log("continuing");let s=e.dataset.id;t.dataset.id=s;let i=JSON.parse(e.dataset.fields),o=e.dataset.status;t.querySelector("input#set-"+o).checked=!0;for(let s in i){let o=i[s];o&&(t.querySelector("[name="+s+"]").value=o,"featured_image"===s&&(console.log(e),t.querySelector("[data-field=featured_image] .image-display").classList.add("has-image"),t.querySelector("[data-field=featured_image] .image-display img").src=e.dataset.img))}t.querySelector(".image")&&document.querySelectorAll(".image").forEach((e=>{const t=e.dataset.field,i=e.querySelector(".file-upload-container"),o=(new window.jvbFileUploader(e,{mode:"direct",content:this.config.content,postID:s,fieldName:t,type:"image_upload",selectors:{dropZone:i,uploader:e},onSuccess:t=>this.handleImageUploadSuccess(t,e),onError:t=>this.handleImageUploadError(t,e)}),e.querySelector(".remove-image"));o&&o.addEventListener("click",(()=>{this.handleImageRemove(e)}));const a=e.querySelector(".replace-image");a&&a.addEventListener("click",(()=>{e.querySelector('input[type="file"]').click()}))})),t.querySelector(".gallery")&&document.querySelectorAll(".gallery").forEach((t=>{const i=t.dataset.field,o=t.querySelector(".gallery-preview");e.dataset.images&&e.dataset.images.split(",").forEach((e=>{this.addToGalleryPreview(e,o)})),new window.jvbFileUploader(t,{mode:"gallery",selectors:{dropZone:t.querySelector(".file-upload-container"),previewGrid:o,uploader:t},type:"image_upload",content:this.config.content,postID:s,fieldName:i,onUploadComplete:e=>{const s=t.querySelector('input[type="hidden"]'),i=s.value?s.value.split(","):[],a=e.data.map((e=>e.attachment_id));s.value=[...i,...a].join(","),e.data.forEach((e=>{const t=document.createElement("div");t.className="preview-item",t.dataset.id=e.attachment_id,t.draggable=!0,t.innerHTML=`\n                        <img src="${e.url}" alt="Upload preview">\n                        <button type="button" class="remove-preview">\n                            ${jvbSettings.icons.delete}\n                        </button>\n                        <button type="button" class="move-image">\n                            ${jvbSettings.icons.grab}\n                        </button>\n                    `,o.appendChild(t)})),s.dispatchEvent(new Event("change",{bubbles:!0}))}}),new Sortable(o,{animation:150,handle:".move-image",onEnd:()=>{const e=t.querySelector('input[type="hidden"]'),s=[...o.querySelectorAll(".preview-item")].map((e=>e.dataset.id));e.value=s.join(","),e.dispatchEvent(new Event("change",{bubbles:!0}))}})})),t.querySelector(".taxonomies")&&t.querySelectorAll(".taxonomies .jvb-selector").forEach((t=>{let s=t.dataset.taxonomy,i=(t.classList.contains("hierarchical"),JSON.parse(t.dataset.config)),o=e.dataset[s]?JSON.parse(e.dataset[s]):{},a=i.common;t.__instance=new window.jvbSelector(t,{title:"Select "+s+"(s)",selected:o,common:a,allowMultiple:i.multiple,createNew:!0})})),t.showModal()},this.openBulkEditModal=()=>{const t=this.elements.bulkEditModal;if(!t)return;const s=this.state.selected,i=t.querySelector(".selected-count");i&&(i.textContent=`(${s.length} items)`),e.querySelectorAll(".taxonomies .jvb-selector").forEach((e=>{const t=e.dataset.taxonomy,s=(e.classList.contains("hierarchical"),JSON.parse(e.dataset.config));e.__instance=new window.jvbSelector(e,{title:`Select ${t}(s)`,values:{},allowMultiple:s.multiple,appendMode:!0,createNew:!0})})),t.showModal()}}handleModalClose(e,t){return t?!!confirm("You have unsaved changes. Are you sure you want to close this window?")&&(e.querySelectorAll(".gallery").forEach((e=>{e.__uploader&&(e.__uploader.cleanup(),delete e.__uploader)})),e.close(),!0):(e.close(),!0)}addToGalleryPreview(e,t){const s=document.createElement("div");return s.className="preview-item",s.draggable=!0,s.innerHTML=`\n        <img src="${e}" alt="Upload preview">\n        <div class="upload-status">\n            <div class="upload-progress"></div>\n        </div>\n        <button type="button" class="remove-preview" title="Remove Image">\n            ${jvbSettings.icons.delete}\n        </button>\n        <button type="button" class="move-image" title="Reorder Image">\n            ${jvbSettings.icons.grab}\n        </button>\n    `,t.appendChild(s),s}handleImageUploadSuccess(e,t){if(!e.data||!e.data.length)return;const s=t.querySelector(".image-display");removeChildren(s),s.classList.add("has-image");let i=[];e.data.forEach((e=>{let t=new Image;t.src=e.url,i.push(e.attachment_id),s.appendChild(t)})),t.querySelector('input[type="hidden"]').value=i.join(","),t.querySelector(".file-upload-container").hidden=!0,this.showNotification("Image updated successfully")}handleImageUploadError(e,t){console.error("Upload error:",e),this.showNotification("Failed to upload image","error"),t.querySelector(".file-upload-container").hidden=!1;const s=t.querySelector(".file-error");s&&(s.textContent="")}handleImageRemove(e){const t=e.querySelector(".image-display"),s=t.querySelector("img"),i=e.querySelector('input[type="hidden"]'),o=e.querySelector(".file-upload-container");i.value="",s.src="",t.classList.remove("has-image"),o.hidden=!1,this.showNotification("Image removed")}clearSelection(){this.getVisibleItems().forEach((e=>this.toggleItemSelection(e,!1))),this.state.selected.clear(),this.selectAll.checked=!1,this.updateBulkControls()}updateBulkControls(){const e=this.state.selected.size>0;this.elements.grid.classList.toggle("selecting",e),this.elements.bulkControls.classList.toggle("has-selection",e),this.elements.bulkControls.querySelector(".bulk-actions").hidden=!e,e&&document.addEventListener("keydown",(e=>{"Escape"===e.key&&this.state.selected.size>0&&(this.clearSelection(),this.showNotification("Selection cleared"))}));const t=this.elements.bulkControls.querySelector(".selected-count");t&&(t.textContent=e?`( ${this.state.selected.size} selected )`:"")}getVisibleItems(){return Array.from(this.elements.grid.querySelectorAll(".item:not([hidden])"))}showNotification(e,t="success"){window.jvbNotifications?window.jvbNotifications.showPopupNotification({message:e,type:t,priority:"medium",duration:3e3}):alert(e)}};
window.contentManager=class{constructor(e){this.config={content:"",plural:"",taxonomies:{},selectors:{container:".items-list",grid:".item-grid:not(.preview)",uploadZone:".file-upload-wrapper",statusFilters:".status-filters",dateFilters:".date-filters",taxonomyFilters:".taxonomy-filters",viewControls:".view-controls",bulkControls:".bulk-controls",scrollSentinel:".scroll-sentinel",editModal:".edit-modal",bulkEditModal:".bulk-edit-modal",clearButton:".clear-filters"},createPostPerFile:!0,uploadConfig:{mode:"direct",allowMultiple:!0,createPostPerFile:!0,maxSize:5242880,allowedTypes:["image/jpeg","image/png","image/gif","image/webp"]},...e},this.resetCache=!1,this.queueManager=window.jvbQueue,this.loadingManager=window.jvbLoading,this.cache=window.jvbCache,this.error=window.jvbError,this.state={selected:new Set,filters:{status:"all",taxonomies:{},date:null},view:localStorage.getItem(`${this.config.content}_view`)||"grid",loading:!1},this.queue={all:{items:new Map,page:1,hasMore:!0,totalPages:0},draft:{items:new Map,page:1,hasMore:!0,totalPages:0},publish:{items:new Map,page:1,hasMore:!0,totalPages:0},trash:{items:new Map,page:1,hasMore:!0,totalPages:0}},this.init()}async init(){this.elements={},Object.entries(this.config.selectors).forEach((([e,t])=>{this.elements[e]=document.querySelector(t)})),this.config.uploadConfig&&(this.fileUploader=new window.jvbFileUploader({...this.config.uploadConfig,content:this.config.content,fieldName:null})),this.initStatusFilters(),this.initDateFilters(),this.initTaxonomyFilters(),this.initClearFilters(),this.initViewControls(),this.initBulkControls(),this.initInfiniteScroll(),this.initModals(),await this.loadContent()}queueContentUpdate(e,t){const s={type:"content_update",data:{posts:{[e]:{content:this.config.content,...t}},content:this.config.content}};this.queueManager.addToQueue(s),this.updateLocalState(e,t)}queueBulkUpdate(e,t){const s={};e.forEach((e=>{s[e]={content:this.config.content,...t}}));const i={user:window.auth.getUser(),type:"content_update",data:{posts:s}};this.queueManager.addToQueue(i),e.forEach((e=>this.updateLocalState(e,t)))}updateLocalState(e,t){const s=this.queue[this.state.filters.status].items.get(e);if(s){Object.assign(s,t),this.queue[this.state.filters.status].items.set(e,s);const i=this.elements.grid.querySelector(`[data-id="${e}"]`);i&&this.updateItemElement(i,s)}}processFormData(e){const t={};for(const[s,i]of e.entries())if("status"===s)t.status=i;else if(s.startsWith("taxonomy_")){const e=s.replace("taxonomy_","");t.taxonomies||(t.taxonomies={}),t.taxonomies[e]=Array.isArray(i)?i:[i]}else t[s]=i;return t}updateItemElement(e,t){e.classList.remove("draft","publish","trash"),e.classList.add(t.status);const s=e.querySelector(".action-status");s&&(removeChildren(s),s.append(getIcon(t.status))),t.taxonomies&&e.querySelectorAll(".label-group").forEach((e=>{const s=e.dataset.taxonomy;if(s&&t.taxonomies[s]){const i=t.taxonomies[s].terms;e.querySelector(".terms").innerHTML=this.renderTerms(i)}}))}handleItemAction(e,t){const s=t.dataset.id;switch(e){case"edit":this.editModal.handleOpen(),this.openEditModal(t),this.editModal.form&&new FormFields(this.editModal.form,{onSave:this.editModal.onSave(),itemID:t.dataset.id});break;case"restore":this.queueContentUpdate(s,{status:"draft"}),t.remove();break;case"trash":this.queueContentUpdate(s,{status:"trash"}),t.remove();break;case"delete":confirm(`Hold up! Are you sure you want to permanently delete this ${this.config.content}?\n\nThis is a forever kind of deal - no taking it back.`)&&(this.queueContentUpdate(s,{status:"delete"}),t.remove());break;case"toggle-status":const e="publish"===t.dataset.status?"draft":"publish";this.queueContentUpdate(s,{status:e}),t.dataset.status=e,removeChildren(t.querySelector(".action-status")),t.querySelector(".action-status").append(getIcon(e))}}async handleBulkOperation(e,t){window.jvbLoading.show("Processing bulk changes...");try{const s={};t.forEach((t=>{s[t]={content:this.config.content,status:e},["delete","trash","restore"].includes(e)&&document.querySelector('[data-id="'+t+'"]').remove()})),this.queueManager.addToQueue({type:"content_update",data:{posts:s}}),this.clearSelection(),this.showNotification("Bulk changes queued for processing")}catch(e){console.error("Bulk operation failed:",e),this.showNotification("Failed to queue bulk operation","error")}finally{window.jvbLoading.hide()}}getQueryKey(){return JSON.stringify({status:this.state.filters.status,page:this.state.page,filters:this.state.filters})}toggleItemSelection(e,t){const s=e.dataset.id;t?(this.state.selected.add(s),e.classList.add("selected"),e.querySelector("input[type=checkbox]").checked=!0):(this.state.selected.delete(s),e.classList.remove("selected"),e.querySelector("input[type=checkbox]").checked=!1)}async loadContent(e=!0){if(!this.state.loading)try{this.state.loading=!0,this.loadingManager.show();const t=this.state.filters.status;console.log("Loading Page: "),console.log(this.queue[t].page);const s=new URLSearchParams;s.set("type",this.config.content),s.set("page",this.queue[t].page),s.set("filters",JSON.stringify(this.state.filters)),s.set("user",window.auth.getUser()),e&&(this.queue[t].page=1,this.queue[t].items.clear(),removeChildren(this.elements.grid),this.elements.grid.classList.remove("empty"));const i=await this.cache.fetchWithCache(`${jvbSettings.api}content?`+s,{method:"GET",headers:{"Content-Type":"application/json","X-WP-Nonce":window.auth.getNonce(),"X-Action-Nonce":window.auth.getNonce("dash")}},{context:window.auth.getUser()+"-"+this.config.content,forceRefresh:!1});i.total>0?(this.elements.grid.classList.remove("empty"),i.items.forEach((e=>{this.queue[t].items.set(e.id,e)})),this.queue[t].page++,this.queue[t].totalPages=i.total_pages,this.queue[t].hasMore=this.queue[t].page<i.total_pages):(this.elements.grid.classList.add("empty"),this.elements.grid.innerHTML=`<div class="empty-state"><h3>${jvbSettings.icons[this.config.content]}Nothing here${jvbSettings.icons[this.config.content]}</h3><p>It doesn't look like you have any ${this.config.plural} yet.</p><p><small><i>Add some by uploading images above.</i></small></p></div>`,this.queue[t].page=1,this.queue[t].hasMore=!1),this.renderContent()}catch(e){console.error("Error loading content:",e),this.loadingManager.showError("Failed to load content")}finally{this.state.loading=!1,this.loadingManager.hide()}}renderContent(){const e=this.state.filters.status,t=this.queue[e].items;t.size>0&&(this.elements.grid.classList.remove("empty"),this.elements.grid.querySelector(".empty-state")&&removeChildren(this.elements.grid));const s=document.createDocumentFragment();t.forEach((t=>{const i=this.elements.grid.querySelector(`[data-id="${t.id}"]`);if(i){if(t.view!==this.state.view){const e=this.createItemElement(t);t.view=this.state.view,i.replaceWith(e)}}else{const e=this.createItemElement(t);t.view=this.state.view,s.appendChild(e)}this.queue[e].items.set(t.id,t)})),s.children.length>0&&this.elements.grid.appendChild(s)}createItemElement(e){let t=window.getTemplate(this.state.view+"View");t.classList.add(e.status),t.dataset.id=e.id,t.dataset.fields=JSON.stringify(e.fields),t.dataset.status=e.status,t.dataset.img=e.thumbnail;let s=t.querySelector(".gallery");if(e.images){t.dataset.images=e.images;let o=s.querySelector("img");for(var i of e.images){let e=o.cloneNode(!0);e.src=i.src,i.alt&&(e.alt=i.alt),s.appendChild(e)}o.remove()}else s.remove();let o=[],a=t.querySelector(".taxonomies"),n=a.querySelector(".label-group"),l=n.querySelector(".tax"),r=!1;for(let s in e.taxonomies){if(Object.keys(e.taxonomies[s].terms).length>0){r=!0,t.dataset[s]=JSON.stringify(e.taxonomies[s].terms);let i=n.cloneNode(!0),o=jvbSettings.icons[s];for(var c in i.innerHTML=o+i.innerHTML,i.querySelector(".screen-reader-text").textContent=e.taxonomies[s].name,e.taxonomies[s].terms){let e=l.cloneNode(!0);e.textContent=c.name,i.appendChild(e)}}else t.dataset[s]=JSON.stringify({});o.push(s)}r?(n.remove(),l.remove()):a.remove(),0===Object.keys(this.config.taxonomies).length&&(this.config.taxonomies=o);let d=t.querySelector("img");d.src=e.thumbnail,e.alt&&(d.alt=e.alt),t.querySelector(".date").textContent=formatDate(e.date);let u="Hide "+e.icon;"draft"===e.status&&(u="Show "+e.icon);let h=t.querySelector('button[data-action="toggle-status"]');return h.prepend(getIcon(e.status)),h.title=u,this.initItemEventListeners(t),t}initItemEventListeners(e){e.addEventListener("click",(t=>t.target.closest(".item-select")?(t.preventDefault(),this.toggleItemSelection(e,!e.classList.contains("selected")),void this.updateBulkControls()):t.target.closest(".action")?(t.preventDefault(),void this.handleItemAction(t.target.closest(".action").dataset.action,e)):void 0))}initInfiniteScroll(){this.elements.scrollSentinel&&new IntersectionObserver((e=>{e.forEach((e=>{e.isIntersecting&&this.queue[this.state.filters.status].hasMore&&this.loadContent(!1)}))})).observe(this.elements.scrollSentinel)}initStatusFilters(){const e=this.elements.container.querySelector(".controls");e&&e.addEventListener("change",(e=>{if("radio"===e.target.type&&"status-filters"===e.target.name){const t=e.target.id;t!==this.state.filters.status&&(this.state.filters.status=t,this.updateBulkActionOptions(),0===this.queue[t].items.size?this.loadContent(!0):this.renderContent())}}))}initDateFilters(){const e=this.elements.container.querySelector("select.date-filter"),t=this.elements.container.querySelector(".date-range");let s;if(e&&(this.hasFilters=!0,e.addEventListener("change",(e=>{const i=e.target.value;if(s=i,"custom"===i)return void t.showModal();t.close();const o=t.querySelector(".month-select");o&&(o.value=""),this.setDateFilter(i)})),e.addEventListener("click",(i=>{"custom"===s&&"custom"===e.value&&t.showModal()}))),t){const e=t.querySelector(".date-start"),s=t.querySelector(".date-end"),i=t.querySelector(".month-select");i&&i.addEventListener("change",(e=>{const[s,i]=e.target.value.split("-");if(s&&i){const e=new Date(s,i-1,1),o=new Date(s,i,0);o.setHours(23,59,59,999),this.setDateFilter("custom",e,o),t.close()}}));const o=()=>{const i=e.value,o=s.value;if(i&&o){const e=new Date(i),s=new Date(o);s.setHours(23,59,59,999),this.setDateFilter("custom",e,s),t.close()}};e.addEventListener("change",o),s.addEventListener("change",o)}}setDateFilter(e,t=null,s=null){const i=new Date;i.setHours(23,59,59,999);let o=t,a=s||i;if(!t&&""!==e)switch(o=new Date,e){case"today":o.setHours(0,0,0,0);break;case"week":o.setDate(i.getDate()-7);break;case"month":o.setMonth(i.getMonth()-1);break;case"year":o.setFullYear(i.getFullYear()-1)}this.state.filters.date=e?{range:{after:o.toISOString(),before:a.toISOString()},custom:"custom"===e}:{range:null,custom:!1},this.updateClearFiltersButton(),this.state.page=1,this.loadContent()}initTaxonomyFilters(){const e=this.elements.container.querySelectorAll(".filter[data-taxonomy]");e.length&&(this.hasFilters=!0,e.forEach((e=>{e.addEventListener("change",(e=>{const t=e.target.dataset.taxonomy,s=e.target.value;s?this.state.filters.taxonomies[t]=[parseInt(s)]:delete this.state.filters.taxonomies[t],this.updateClearFiltersButton(),this.state.page=1,this.loadContent(!0)}))})))}updateClearFiltersButton(){const e=document.querySelector(this.config.selectors.clearButton);if(!e)return;const t=Object.keys(this.state.filters.taxonomies).length>0||null!==this.state.filters.date.range;e.hidden=!t}clearAllFilters(){this.elements.container.querySelectorAll(".filter[data-taxonomy]").forEach((e=>e.value=""));const e=this.elements.container.querySelector("select.date-filter");e&&(e.value=""),this.state.filters={date:{range:null,custom:!1},taxonomies:{}},this.updateClearFiltersButton(),this.state.page=1,this.loadContent(!0)}initClearFilters(){this.config.selectors.clearButton&&document.querySelector(this.config.selectors.clearButton).addEventListener("click",(()=>this.clearAllFilters()))}initViewControls(){const e=this.elements.container.querySelector(".view-controls");if(!e)return;e.addEventListener("change",(e=>{const t=e.target;"radio"===t.type&&(this.setView(t.value),this.loadContent(!0))}));const t=localStorage.getItem(`${this.config.content}_view`)||"grid",s=e.querySelector(`input[value="${t}"]`);s&&(s.checked=!0,this.setView(t))}setView(e){this.state.view=e;const t=new Set(this.state.selected);this.elements.grid.classList.remove("grid-view","list-view"),this.elements.grid.classList.add(`${e}-view`),localStorage.setItem(`${this.config.content}_view`,e),this.loadContent(!0),t.forEach((e=>{const t=this.elements.grid.querySelector(`[data-id="${e}"]`);if(t){const e=t.querySelector('input[type="checkbox"]');e&&(e.checked=!0,t.classList.add("selected"))}})),this.updateBulkControls()}initBulkControls(){if(!this.elements.bulkControls)return;this.selectAll=this.elements.bulkControls.querySelector(".select-all"),this.selectAll&&this.selectAll.addEventListener("change",(()=>{this.getVisibleItems().forEach((e=>{this.toggleItemSelection(e,this.selectAll.checked)})),this.updateBulkControls()}));const e=this.elements.bulkControls.querySelector(".bulk-action-select"),t=this.elements.bulkControls.querySelector(".apply-bulk");t&&e&&(this.updateBulkActionOptions(),this.elements.container.querySelector(".status-filters"),t.addEventListener("click",(()=>{const t=e.value;if(!t)return;const s=Array.from(this.state.selected);switch(t){case"restore":this.handleBulkOperation("restore",s);break;case"delete":confirm(`Hold up! Are you sure you want to permanently delete these ${this.config.plural}?\n\nThis is a forever kind of deal - no taking it back.`)&&this.handleBulkOperation("delete",s);break;case"trash":this.handleBulkOperation("trash",s);break;case"edit":this.openBulkEditModal();const e=document.querySelector(".bulk-edit-modal");if(e){const t=e.querySelector(".selected-count");t&&(t.textContent=`( ${s.length} items )`);const i=e.querySelector(".selected");if(i){let e="";s.forEach((t=>{let s=this.elements.grid.querySelector('[data-id="'+t+'"]');e+='<input type="checkbox" id="selected-'+t+'" name="posts" value="'+t+'" checked><label for="selected-'+t+'"><img width="100%" height="auto" src="'+s.dataset.img+'"></label>'})),i.innerHTML=e}}break;case"publish":case"draft":this.handleBulkOperation(t,s)}e.value=""})));const s=this.elements.bulkControls.querySelector(".cancel-bulk");s&&s.addEventListener("click",(()=>{this.clearSelection()}))}updateBulkActionOptions(){const e=this.elements.bulkControls.querySelector(".bulk-action-select");e&&("trash"===this.state.filters.status?e.innerHTML='\n            <option value="">Bulk Actions...</option>\n            <option value="restore">Restore</option>\n            <option value="delete">Permanently Delete</option>\n        ':e.innerHTML='\n            <option value="">Bulk Actions...</option>\n            <option value="edit">Edit</option>\n            <option value="publish">Show</option>\n            <option value="draft">Hide</option>\n            <option value="trash">Scrap</option>\n        ')}initModals(){this.elements.editModal&&(this.editModal=new window.jvbModal(this.elements.editModal,{open:!1,close:this.elements.editModal.querySelector(".cancel"),save:this.elements.editModal.querySelector(".save"),onSave:()=>{const e=new FormData(this.elements.editModal.querySelector("form"));let t={};const s=this.elements.editModal.querySelectorAll(".taxonomies .jvb-selector");let i=Object.fromEntries(e);s.forEach((e=>{const s=e.dataset.taxonomy.replace(jvbSettings.base||"jvb_","");if(delete i["edit-"+s],e.__instance){const i=e.__instance.selectedItems;i&&Object.keys(i).length>0&&(t[s]=Object.keys(i).join(","))}})),i.taxonomies=t;for(let[e,t]of Object.entries(i))""!==t&&0!==Object.keys(t).length||delete i[e];this.queueContentUpdate(this.elements.editModal.dataset.id,i)}}));const e=this.elements.bulkEditModal;if(e){let t=!1;const s=e.querySelector("form");s?.addEventListener("change",(()=>{t=!0})),e.addEventListener("keydown",(s=>{"Escape"===s.key&&(s.preventDefault(),this.handleModalClose(e,t))})),e.addEventListener("click",(s=>{s.target===e&&this.handleModalClose(e,t)})),e.querySelector(".cancel")?.addEventListener("click",(()=>{this.handleModalClose(e,t),this.clearSelection()})),e.querySelector(".save")?.addEventListener("click",(()=>{const i=new FormData(s),o=Array.from(i.getAll("posts")),a={};""===i.get("term_name")&&(i.delete("term_name"),i.delete("select_parent"));let n={};e.querySelectorAll(".taxonomies .jvb-selector").forEach((e=>{const t=e.dataset.taxonomy.replace(jvbSettings.base||"jvb_","");if(e.__instance){const s=e.__instance.selectedItems;s&&Object.keys(s).length>0&&(n[t]=Object.keys(s).join(","))}})),o.forEach((e=>{a[e]={append:!0,content:this.config.content,status:i.get("bulk_status"),taxonomies:n}})),this.queueManager.addToQueue({type:"content_update",data:{posts:a}}),t=!1,e.close(),this.clearSelection()})),e.addEventListener("submit",(i=>{const o=new FormData(s),a=Array.from(o.getAll("posts")),n={};""===o.get("term_name")&&(o.delete("term_name"),o.delete("select_parent"));let l={};for(const e of this.config.taxonomies)l[e]=o.getAll(e),o.delete(e);a.forEach((e=>{n[e]={append:!0,content:this.config.content,status:o.get("bulk_status"),taxonomies:l}})),this.queueManager.addToQueue({type:"content_update",data:{posts:n}}),t=!1,e.close(),this.clearSelection()}))}this.openEditModal=e=>{console.log("Openening whatsit");const t=this.editModal.modal;if(!t)return;console.log("continuing");let s=e.dataset.id;t.dataset.id=s;let i=JSON.parse(e.dataset.fields),o=e.dataset.status;t.querySelector("input#set-"+o).checked=!0;for(let s in i){let o=i[s];o&&(t.querySelector("[name="+s+"]").value=o,"featured_image"===s&&(console.log(e),t.querySelector("[data-field=featured_image] .image-display").classList.add("has-image"),t.querySelector("[data-field=featured_image] .image-display img").src=e.dataset.img))}t.querySelector(".image")&&document.querySelectorAll(".image").forEach((e=>{const t=e.dataset.field,i=e.querySelector(".file-upload-container"),o=(new window.jvbFileUploader(e,{mode:"direct",content:this.config.content,postID:s,fieldName:t,type:"image_upload",selectors:{dropZone:i,uploader:e},onSuccess:t=>this.handleImageUploadSuccess(t,e),onError:t=>this.handleImageUploadError(t,e)}),e.querySelector(".remove-image"));o&&o.addEventListener("click",(()=>{this.handleImageRemove(e)}));const a=e.querySelector(".replace-image");a&&a.addEventListener("click",(()=>{e.querySelector('input[type="file"]').click()}))})),t.querySelector(".gallery")&&document.querySelectorAll(".gallery").forEach((t=>{const i=t.dataset.field,o=t.querySelector(".gallery-preview");e.dataset.images&&e.dataset.images.split(",").forEach((e=>{this.addToGalleryPreview(e,o)})),new window.jvbFileUploader(t,{mode:"gallery",selectors:{dropZone:t.querySelector(".file-upload-container"),previewGrid:o,uploader:t},type:"image_upload",content:this.config.content,postID:s,fieldName:i,onUploadComplete:e=>{const s=t.querySelector('input[type="hidden"]'),i=s.value?s.value.split(","):[],a=e.data.map((e=>e.attachment_id));s.value=[...i,...a].join(","),e.data.forEach((e=>{const t=document.createElement("div");t.className="preview-item",t.dataset.id=e.attachment_id,t.draggable=!0,t.innerHTML=`\n                        <img src="${e.url}" alt="Upload preview">\n                        <button type="button" class="remove-preview">\n                            ${jvbSettings.icons.delete}\n                        </button>\n                        <button type="button" class="move-image">\n                            ${jvbSettings.icons.grab}\n                        </button>\n                    `,o.appendChild(t)})),s.dispatchEvent(new Event("change",{bubbles:!0}))}}),new Sortable(o,{animation:150,handle:".move-image",onEnd:()=>{const e=t.querySelector('input[type="hidden"]'),s=[...o.querySelectorAll(".preview-item")].map((e=>e.dataset.id));e.value=s.join(","),e.dispatchEvent(new Event("change",{bubbles:!0}))}})})),t.querySelector(".taxonomies")&&t.querySelectorAll(".taxonomies .jvb-selector").forEach((t=>{let s=t.dataset.taxonomy,i=(t.classList.contains("hierarchical"),JSON.parse(t.dataset.config)),o=e.dataset[s]?JSON.parse(e.dataset[s]):{},a=i.common;t.__instance=new window.jvbSelector(t,{title:"Select "+s+"(s)",selected:o,common:a,allowMultiple:i.multiple,createNew:!0})})),t.showModal()},this.openBulkEditModal=()=>{const t=this.elements.bulkEditModal;if(!t)return;const s=this.state.selected,i=t.querySelector(".selected-count");i&&(i.textContent=`(${s.length} items)`),e.querySelectorAll(".taxonomies .jvb-selector").forEach((e=>{const t=e.dataset.taxonomy,s=(e.classList.contains("hierarchical"),JSON.parse(e.dataset.config));e.__instance=new window.jvbSelector(e,{title:`Select ${t}(s)`,values:{},allowMultiple:s.multiple,appendMode:!0,createNew:!0})})),t.showModal()}}handleModalClose(e,t){return t?!!confirm("You have unsaved changes. Are you sure you want to close this window?")&&(e.querySelectorAll(".gallery").forEach((e=>{e.__uploader&&(e.__uploader.cleanup(),delete e.__uploader)})),e.close(),!0):(e.close(),!0)}addToGalleryPreview(e,t){const s=document.createElement("div");return s.className="preview-item",s.draggable=!0,s.innerHTML=`\n        <img src="${e}" alt="Upload preview">\n        <div class="upload-status">\n            <div class="upload-progress"></div>\n        </div>\n        <button type="button" class="remove-preview" title="Remove Image">\n            ${jvbSettings.icons.delete}\n        </button>\n        <button type="button" class="move-image" title="Reorder Image">\n            ${jvbSettings.icons.grab}\n        </button>\n    `,t.appendChild(s),s}handleImageUploadSuccess(e,t){if(!e.data||!e.data.length)return;const s=t.querySelector(".image-display");removeChildren(s),s.classList.add("has-image");let i=[];e.data.forEach((e=>{let t=new Image;t.src=e.url,i.push(e.attachment_id),s.appendChild(t)})),t.querySelector('input[type="hidden"]').value=i.join(","),t.querySelector(".file-upload-container").hidden=!0,this.showNotification("Image updated successfully")}handleImageUploadError(e,t){console.error("Upload error:",e),this.showNotification("Failed to upload image","error"),t.querySelector(".file-upload-container").hidden=!1;const s=t.querySelector(".file-error");s&&(s.textContent="")}handleImageRemove(e){const t=e.querySelector(".image-display"),s=t.querySelector("img"),i=e.querySelector('input[type="hidden"]'),o=e.querySelector(".file-upload-container");i.value="",s.src="",t.classList.remove("has-image"),o.hidden=!1,this.showNotification("Image removed")}clearSelection(){this.getVisibleItems().forEach((e=>this.toggleItemSelection(e,!1))),this.state.selected.clear(),this.selectAll.checked=!1,this.updateBulkControls()}updateBulkControls(){const e=this.state.selected.size>0;this.elements.grid.classList.toggle("selecting",e),this.elements.bulkControls.classList.toggle("has-selection",e),this.elements.bulkControls.querySelector(".bulk-actions").hidden=!e,e&&document.addEventListener("keydown",(e=>{"Escape"===e.key&&this.state.selected.size>0&&(this.clearSelection(),this.showNotification("Selection cleared"))}));const t=this.elements.bulkControls.querySelector(".selected-count");t&&(t.textContent=e?`( ${this.state.selected.size} selected )`:"")}getVisibleItems(){return Array.from(this.elements.grid.querySelectorAll(".item:not([hidden])"))}showNotification(e,t="success"){window.jvbNotifications?window.jvbNotifications.showPopupNotification({message:e,type:t,priority:"medium",duration:3e3}):alert(e)}};
assets/js/min/crud.min.js
@@ -1 +1 @@
(()=>{class e{constructor(){this.container=document.querySelector(".crud[data-content]:not([data-ignore])"),this.container&&(this.content=this.container.dataset.content,this.endpoint=this.container.dataset.endpoint??"content",this.singular=this.container.dataset.singular,this.plural=this.container.dataset.plural,this.queue=window.jvbQueue,this.a11y=window.jvbA11y,this.error=window.jvbError,this.populate=window.jvbPopulate,this.cache=new window.jvbCache(this.content),this.activeItem=null,this.isTimeline=!1,this.isPopulating=!1,this.changes=new Map,this.items=new Map,this.init())}init(){this.initElements(),this.initListeners(),this.defineTemplates();let e=this.initSettings();this.initStore(e),this.checkHideFilters(),this.initIntegrations(),this.initUploader(),this.initModals()}defineTemplates(){const e=window.jvbTemplates,t=this,s=(e,s,i)=>{e.dataset.itemId=i.id;let a=s.checkbox.closest(".preview");window.prefixInput(s.checkbox,`select-${i.id}`,a,!0),s.checkbox.value=i.id,s.checkbox.checked=t.selected.has(parseInt(i.id)),s.selectLabel&&(s.selectLabel.htmlFor=`select-${i.id}`),s.edit&&(s.edit.dataset.id=i.id),s.trash&&(s.trash.dataset.id=i.id)},i=function(e,t,s){if(s?.fields?.post_thumbnail){const e=s.images[s.fields.post_thumbnail]??{};t.img.src=e.medium??"",t.img.alt=e.alt??s.fields.post_title??""}};e.define("gridView",{refs:{img:"img",checkbox:".select-item",selectLabel:"label.select-item-label",edit:'[data-action="edit"]',trash:'[data-action="trash"]'},setup({el:e,refs:t,manyRefs:a,data:l}){s(e,t,l),i(0,t,l)}}),e.define("listView",{refs:{img:"img",checkbox:".select-item",selectLabel:"label.select-item-label",edit:'[data-action="edit"]',trash:'[data-action="trash"]'},manyRefs:{attrs:"[data-attr]",fields:"[data-field]"},setup({el:e,refs:t,manyRefs:a,data:l}){s(e,t,l),i(0,t,l),a?.attrs?.forEach((e=>{const t=l[e.dataset.attr];t&&""!==t?e.textContent=t:e.remove()})),a?.fields?.forEach((e=>{const t=l.fields?.[e.dataset.field];t&&""!==t?"DIV"===e.tagName?e.innerHTML=t:e.textContent=t:e.remove()}))}});let a={};this.isTimeline&&(a.sharedRow="tr.shared",a.point="tr.timeline-point"),e.define("tableView",{refs:{checkbox:".select-item",selectLabel:"label.select-item-label",...a},manyRefs:{inputs:"input,select,textarea",status:'input[name="post_status"]',selectors:'[data-type="selector"]',fields:"[data-field]"},setup({el:e,refs:i,manyRefs:a,data:l}){if(s(e,i,l),a?.inputs?.forEach((e=>{let t=e.closest("[data-field]");window.prefixInput(e,`${l.id}-`,t)})),a?.status?.forEach((e=>{e.value===l.status&&(e.checked=!0)})),t.isTimeline)i.sharedRow&&(i.sharedRow.querySelectorAll("input,select,textarea").forEach((e=>{let t=e.closest("[data-field]");window.prefixInput(e,`${l.id}-`,t)})),t.populate.populate(i.sharedRow,l),i.sharedRow.querySelectorAll('input[name="post_status"]').forEach((e=>{e.value===l.status&&(e.checked=!0)}))),i.point&&l.fields?.timeline&&(Object.entries(l.fields.timeline).forEach((([s,a],n)=>{const o=i.point.cloneNode(!0);o.dataset.index=`${n}`,o.dataset.itemId=a.id,o.querySelectorAll("input,select,textarea").forEach((e=>{let t=e.closest("[data-field]");window.prefixInput(e,`${a.id}-`,t)})),t.populate.populate(o,{fields:a,images:l.images,taxonomies:l.taxonomies});const d=l.images?.[a.post_thumbnail];d&&o.querySelector(".field.upload")?.setAttribute("title",d["image-title"]??""),e.insertBefore(o,i.point)})),i.point.remove());else if(void 0!==t.ui.table.form?.dataset.edit)a?.inputs?.forEach((e=>{let t=e.closest("[data-field]");window.prefixInput(e,`${l.id}-`,t)})),a?.status?.forEach((e=>{e.value===l.status&&(e.checked=!0)})),t.populate.populate(e,l);else{const e=Object.hasOwn(l,"fields")?l.fields:l;a?.fields?.forEach((t=>{if(Object.hasOwn(e,t.dataset.field)&&""!==e[t.dataset.field]){let s=e[t.dataset.field],i=e.children[0];i&&(i.textContent="date"===t.dataset.field?window.formatTimeAgo(s):s)}}))}a?.selectors?.forEach((e=>e.setAttribute("data-lazy","")))}}),e.define("emptyState"),e.define("bulkItem",{refs:{checkbox:"input",img:"img",label:"label"},setup({el:e,refs:t,manyRefs:s,data:i}){t.checkbox&&(t.checkbox.id=`bulk_${i.id}`,t.checkbox.value=i.id,t.checkbox.checked=!0,t.checkbox.name="selected[]");let a=i?.images[i?.fields?.post_thumnbail]??{};t.img&&Object.keys(a).length>0&&(t.img.src=a.medium??"",t.img.alt=a.alt??""),t.label&&(t.label.title=item.fields.post_title)}}),e.define("trashOptions"),e.define("notTrashOptions"),e.define("contentTable")}initElements(){this.allowedFilters=["status","orderby","order","search","date-filter","dateFrom","dateTo"],this.selectors={buttons:{create:".create-item",clearFilters:'[data-action="clear-filters"]'},views:{grid:'input[data-view="grid"]',list:'input[data-view="list"]',table:'input[data-view="table"]'},modals:{create:{modal:"dialog.create",form:"dialog.create form",h2:"dialog.create h2"},edit:{modal:"dialog.edit",form:"dialog.edit form",h2:"dialog.edit h2"},bulkEdit:{modal:"dialog.bulkEdit",selected:"dialog.bulkEdit .selected",h2:"dialog.bulkEdit h2 span",form:"dialog.bulkEdit form"},date:{modal:"dialog.date-range",start:"dialog.date-range .date-start",end:"dialog.date-range .date-end",month:"dialog.date-range .month-select"}},grid:`.${this.content}.item-grid`,table:{nav:"#vertical",form:"form.table",table:"form.table table",body:"form.table body",head:"form.table thead",foot:"form.table tfoot",selectedColumns:".all-filters .multi-select",columns:"thead th"},bulk:{action:".bulk-action-select",count:".bulk-controls .selected-count",control:".bulk-controls .bulk-actions",select:".bulk-controls select",selectAll:".select-all"},filters:{container:"details.all-filters",search:'.all-filters input[type="search"]',status:{all:'[name="status"]#all',publish:'[name="status"]#publish',draft:'[name="status"]#draft',trash:'[name="status"]#trash'},orderby:{date:'[name="orderby"]#date',alphabetical:'[name="orderby"]#alphabetical'},order:{asc:'[name="order"][value="asc"]',desc:'[name="order"][value="desc"]'},date:'[data-filter="date"]'},uploader:"details.uploader"},this.ui=window.uiFromSelectors(this.selectors);const e=document.querySelectorAll('[data-filter="taxonomies"]');e.length>0&&(this.ui.filters.taxonomies={},e.forEach((e=>{const t=e.dataset.taxonomy;this.ui.filters.taxonomies[t]=e,this.allowedFilters.push(`tax_${t}`)}))),this.isTimeline=!!document.querySelector("[data-timeline]")}initUploader(){this.ui.uploader&&(window.jvbUploads.scanFields(this.ui.uploader),window.jvbUploads.subscribe(((e,t)=>{"sent-to-queue"===e&&t===this.ui.uploader.dataset.uploader&&window.debouncer.schedule("crud-complete",(()=>{this.store.clearCache()}))})))}initModals(){this.modals={};for(let[e,t]of Object.entries(this.ui.modals))t.modal&&(this.modals[e]=new window.jvbModal(t.modal),this.modals[e].subscribe(((t,s)=>{if("modal-close"===t){const t=this.ui.modals[e].form.dataset.formId;t&&this.forms.clearForm(t),this.resetForm(this.ui.modals[e].form),"date"===e&&this.handleCustomDateSelection(),["edit","bulkEdit","create"].includes(e)&&window.debouncer.timeouts.has(`save-${this.content}`)&&this.scheduleSave(0)}})))}initStore(e){let t={...this.defaults,...e};const s=window.jvbStore.register(this.content,[{storeName:this.content,keyPath:"id",endpoint:this.endpoint??"content",headers:{action_nonce:window.auth.getNonce("dash")},indexes:[{name:"id",keyPath:"id"},{name:"status",keyPath:"status"},{name:"date",keyPath:"date"},{name:"modified",keyPath:"modified"},{name:"title",keyPath:"title"}],filters:t,ignore:["content","user"],TTL:36e5,showLoading:!0},{storeName:"changes",keyPath:"id"}]);this.changesStore=s.changes,this.store=s[this.content],this.store.subscribe(((e,t)=>{if("data-loaded"===e)this.render(),this.selectionHandler.collectItems()})),this.changesStore.subscribe(((e,t)=>{if("data-ready"===e){let e=this.changesStore.getAll();e.length>0&&(e.forEach((e=>{this.changes.set(e.id,e)})),this.savePosts("",!1).then((()=>{})))}}))}initIntegrations(){this.selected=new Set,this.selectionHandler=new window.jvbHandleSelection(this.container,{selectAll:{checkbox:"#select-all",label:".bulk-select label",span:".bulk-select label span"},wrapper:{wrapper:".wrap"},item:{idAttribute:"itemId"}}),this.selectionHandler.subscribe(((e,t)=>{this.selected=new Set([...t.selectedItems].map((e=>parseInt(e)))),this.ui.bulk.control.hidden=0===this.selected.size,this.ui.bulk.count.hidden=0===this.selected.size,this.ui.bulk.count.textContent=`${this.selected.size} ${this.plural} selected`})),this.forms=window.jvbForm,this.queue.subscribe(((e,t)=>{if(["image_upload","video_upload","document_upload"].includes(t.type)&&"operation-status"===e&&"completed"===t.status&&this.store.clearCache(),"operation-status"===e&&"completed"===t.status&&"uploads/groups"===t.endpoint&&(console.log("Cleared local cache. Refresh to see changes"),this.store.clearCache()),"operation-status"===e&&"completed"===t.status&&"content_update"===t.type){if(console.log("Cleared local cache. Refresh to see changes"),this.store.clearCache(),!t.result||!t.result.posts)return void console.warn("Content update completed but no result.posts",t);const e=Object.keys(t.result.posts).filter((e=>!0===t.result.posts[e]?.success));if(0===e.length)return;this.changesStore.deleteMany(e),e.forEach((e=>this.changes.delete(e)))}}))}initSettings(){this.defaults={content:this.content,user:window.auth.getUser(),page:1,status:"all",orderby:"date",order:"desc",search:""};let e={},t=this.container.dataset.view??"grid";this.view=this.cache.get("view")??t,this.view!==t&&(this.ui.views[this.view].checked=!0),this.status=this.cache.get("status")??this.defaults.status,this.status!==this.defaults.status&&(this.ui.filters.status[this.status].checked=!0,e.status=this.status),this.orderby=this.cache.get("orderby")??this.defaults.orderby,this.orderby!==this.defaults.orderby&&(this.ui.filters.orderby[this.orderby].checked=!0,e.orderBy=this.orderby),this.order=this.cache.get("order")??this.defaults.order,this.order!==this.defaults.order&&(this.ui.filters.order[this.order].checked=!0,e.order=this.order),this.ui.filters.taxonomies&&Object.entries(this.ui.filters.taxonomies).forEach((([t,s])=>{const i=`tax_${t}`,a=this.cache.get(i);a&&(s.value=a,e[i]=a)}));let s=this.cache.get("tabNav")??"horizontal";this.ui.table.nav&&"vertical"===s&&(this.ui.table.nav.checked=!0);let i={showFilters:{element:this.ui.filters.container,default:"closed"},showUploader:{element:this.ui.uploader,default:"open"}};for(let[e,t]of Object.entries(i))if(t.element){let s=this.cache.get(e)??t.default;t.element.open="open"===s,t.element.addEventListener("toggle",(()=>{this.cache.set(e,t.element.open?"open":"closed")}))}return e}initListeners(){this.changeHandler=this.handleChange.bind(this),this.clickHandler=this.handleClick.bind(this),this.inputHandler=this.handleInput.bind(this),this.submitHandler=this.handleModalSubmit.bind(this),document.addEventListener("change",this.changeHandler),document.addEventListener("click",this.clickHandler),this.ui.filters.search&&this.ui.filters.search.addEventListener("input",this.inputHandler);for(let[e,t]of Object.entries(this.ui.modals))t.form&&t.form.addEventListener("submit",this.submitHandler)}handleModalSubmit(e){e.preventDefault();const t=e.target.closest("dialog");if(!t)return;let s=`Saving changes for multiple ${this.plural}`;t.classList.contains("edit")?s="Saving your edits...":t.classList.contains("create")&&(s=`Creating your new ${this.singular}`),this.scheduleSave(0)}handleChange(e){const t=e.target.closest("[data-item-id]"),s=e.target.matches("[data-filter]"),i=e.target.matches(".bulk-action-select"),a=e.target.matches("[data-view]");if(t||s||i||a)if(this.isPopulating||!t||e.target.closest("[data-ignore], .select-item")){if(a)return this.items.clear(),void this.handleViewChange(e.target);if(i)this.handleBulkAction(e.target);else if(s)this.handleFilterChange(e.target);else if("table"===this.view){if(e.target.matches("details.multi-select"))return void this.toggleColumn(e.target.id,e.target.checked);e.target.matches(this.selectors.table.nav)&&(this.tabNav=e.target.checked,this.cache.set("tabNav",e.target.checked?"vertical":"horizontal"))}}else this.handleItemUpdate(e)}handleBulkAction(e){if(e.value.startsWith("tax-")){const t=e.options[e.selectedIndex],s=t.dataset.taxonomy,i=t.dataset.single,a=t.dataset.plural;return window.jvbSelector.openEmpty(s,i,a,(e=>this.handleBulkTaxonomy(e))),void(e.value="")}switch(e.value){case"edit":this.openBulkEditModal();break;case"publish":case"trash":case"delete":this.setBulkStatus(e.value);break;case"draft":case"restore":this.setBulkStatus("draft")}}handleBulkTaxonomy(e){e.termIds.length&&this.selected.size&&(this.selected.forEach((t=>{const s=this.store.get(t);if(!s)return;const i=(s.taxonomies?.[e.taxonomy]||[]).map((e=>e.id)),a=[...new Set([...i,...e.termIds])];this.updateItem(t,e.taxonomy,a)})),this.savePosts(`Adding ${e.terms.length} ${e.taxonomy} to ${this.selected.size} ${this.plural}...`).then((()=>{})),this.selectionHandler.clearSelection())}handleItemUpdate(e){let t=window.targetCheck(e,"[data-item-id]");t&&t.dataset.itemId.split(",").forEach((t=>{let s=this.forms.getField(e.target);if(["repeater","tag-list"].includes(s.dataset.fieldType))return;let i=s.dataset.field,a=this.forms.getFieldValue(e.target);this.updateItem(t,i,a)}))}updateItem(e,t,s){this.changes.has(e)||this.changes.set(e,{id:e,content:this.content}),this.changes.get(e)[t]=s,this.scheduleBackup(),this.scheduleSave()}scheduleBackup(){window.debouncer.schedule(`changes-${this.content}`,(async()=>{this.changes.size>0&&await this.handleBackup()}),2e3)}cancelBackup(){window.debouncer.cancel(`changes-${this.content}`)}async handleBackup(){const e=Array.from(this.changes.values());this.changes.clear();const t=e.map((e=>e.id)),s=await Promise.all(t.map((e=>this.changesStore.get(e)))),i=e.map(((e,t)=>s[t]?window.deepMerge(s[t],e):e));await this.changesStore.saveMany(i)}scheduleSave(e=1e4){window.debouncer.schedule(`save-${this.content}`,(async()=>{this.changes.size>0&&(this.cancelBackup(),await this.handleBackup()),await this.savePosts("",!1)}),e)}handleFilterChange(e){let t=e.dataset.filter;return"date"===t&&"custom"===e.value?(e.value="",void this.modals.date.handleOpen()):"date"===t&&""!==e.value?(this.setFilter("date-filter",e.value),this.deleteFilter("dateFrom"),this.deleteFilter("dateTo"),void this.checkHideFilters()):("taxonomies"===t&&(t=`tax_${e.dataset.taxonomy}`),void this.setFilter(t,e.value))}checkHideFilters(){const e=this.store.filters,t=Object.entries(e).some((([e,t])=>!["content","user","page"].includes(e)&&(this.defaults[e]!==t&&""!==t&&null!==t)));this.ui.buttons.clearFilters.hidden=!t}clearAllFilters(){let e=this.store.filters;this.store.clearFilters();for(let[t,s]of Object.entries(e))this.cache.remove(t),this.deleteFilter(t,s);this.a11y.announce("All filters cleared")}handleCustomDateSelection(){if(this.ui.modals.date.month&&this.ui.modals.date.month.value){const[e,t]=this.ui.modals.date.month.value.split("-"),s=`${e}-${t}-01`,i=new Date(e,parseInt(t),0).getDate(),a=`${e}-${t}-${String(i).padStart(2,"0")}`;this.setFilter("dateFrom",s),this.setFilter("dateTo",a),this.deleteFilter("date-filter"),this.ui.modals.date.month.value=""}else this.ui.modals.date.start&&this.ui.modals.date.start.value&&this.ui.modals.date.end&&this.ui.modals.date.end.value&&(this.setFilter("dateFrom",this.ui.modals.date.start.value),this.setFilter("dateTo",this.ui.modals.date.end.value),this.deleteFilter("date-filter"),this.ui.modals.date.start.value="",this.ui.modals.date.end.value="");this.checkHideFilters()}handleViewChange(e){this.view=e.dataset.view,this.cache.set("view",this.view),this.render()}handleClick(e){if(e.target.matches(".clear-search"))return void this.deleteFilter("search","");const t=e.target.closest("[data-action]");return t?(e.preventDefault(),void this.handleActionButton(t)):e.target.matches(".apply-date-filter")?(this.handleCustomDateSelection(),void this.modals.date.handleClose()):void(e.target.matches(this.selectors.buttons.create)&&this.openCreateModal())}openCreateModal(){this.forms.registerForm(this.ui.modals.create.form,{cache:!1}),this.ui.modals.create.modal.dataset.itemId=window.generateID("new"),this.modals.create.handleOpen()}handleActionButton(e){const t=e.dataset.id;switch(e.dataset.action){case"edit":this.openEditModal(t);break;case"delete":confirm("Delete this item? This cannot be undone")&&(this.updateItem(t,"post_status","delete"),window.fade(e.closest(".item"),!1),this.savePosts(`Permanently deleting ${this.singular}...`).then((()=>{})),this.store.delete(t));break;case"trash":"trash"===this.status?confirm("Delete this item? This cannot be undone")&&(this.updateItem(t,"post_status","delete"),window.fade(e.closest(".item"),!1),this.savePosts(`Permanently deleting ${this.singular}...`).then((()=>{})),this.store.delete(t)):(this.updateItem(t,"post_status","trash"),window.fade(e.closest(".item"),!1),this.savePosts(`Sending ${this.singular} to trash...`).then((()=>{})));break;case"bulk-edit":this.selected.size>0&&this.openBulkEditModal();break;case"bulk-delete":this.handleBulkDelete();break;case"refresh":this.store.clearCache(),this.store.fetch();break;case"clear-filters":this.clearAllFilters()}}handleBulkDelete(){let e="trash"===this.status;if(this.selected.size>0&&confirm(`${e?"Permanently delete":"Send"} ${this.selected.size} ${1===this.selected.size?this.singular:this.plural}${e?"":"to trash"}?`)){this.selected.forEach((t=>{this.store.delete(t),this.updateItem(t,"post_status",e?"delete":"trash")}));let t=e?`Permanently deleting ${this.selected.size} ${1===this.selected.size?this.singular:this.plural}`:`Sending ${this.selected.size} ${1===this.selected.size?this.singular:this.plural} to trash`;this.savePosts(t).then((()=>{})),this.selectionHandler.clearSelection()}}handleInput(e){e.preventDefault(),e.stopPropagation();let t=e.target.value.trim(),s=`${this.content}-search`;0!==t.length?window.debouncer.schedule(s,(()=>{this.a11y.announce(`Searching for "${t}"...`),this.store.setFilters({search:t,page:1})}),300):this.deleteFilter("search","")}handleKeys(e){if(this.tabNav&&"Tab"===e.key){e.preventDefault();const t=e.target.closest("[data-field]"),s=e.target.closest("tr");if(!t||!s)return;const i=t.dataset.field,a=e.shiftKey;let l=this.findNextEditableRow(s,a);l||(l=this.wrapToRow(s,a)),l&&this.focusFieldInRow(l,i,a)}}findNextEditableRow(e,t=!1){let s=t?e.previousElementSibling:e.nextElementSibling;for(;s&&!this.isEditableRow(s);)s=t?s.previousElementSibling:s.nextElementSibling;return s}wrapToRow(e,t=!1){if(this.isTimeline){const s=e.closest("tbody");if(!s)return null;const i=Array.from(s.querySelectorAll("tr")).filter((e=>this.isEditableRow(e)));return t?i[i.length-1]:i[0]}{if(!this.ui.table.body)return null;const e=Array.from(this.ui.table.body.querySelectorAll("tr")).filter((e=>this.isEditableRow(e)));return t?e[e.length-1]:e[0]}}isEditableRow(e){return!e.closest("thead")&&!e.closest("tfoot")&&(this.isTimeline?e.classList.contains("shared")||e.classList.contains("timeline-point"):!!e.dataset.itemId)}focusFieldInRow(e,t,s=!1){const i=e.querySelector(`[data-field="${t}"]`);if(!i)return;const a=this.findFocusableInput(i);if(a){a.focus(),a.select&&"text"===a.type&&a.select();const e=s?"next":"previous";this.a11y?.announce(`Moved to ${t} in ${e} row`)}}findFocusableInput(e){const t=['input:not([type="hidden"]):not([disabled])',"textarea:not([disabled])","select:not([disabled])","button:not([disabled])"];for(const s of t){const t=e.querySelector(s);if(t)return t}return null}openEditModal(e){let t=this.store.get(parseInt(e));t&&(this.activeItem=t.id,this.ui.modals.edit.modal.dataset.itemId=e,this.ui.modals.edit.modal.dataset.content=this.content,this.ui.modals.edit.h2.textContent=`Editing ${""===t.fields.post_title?this.singular:t.fields.post_title}`,this.ui.modals.edit.form.dataset.formId=`edit-${e}`,this.forms.registerForm(this.ui.modals.edit.form,{cache:!1}),this.isPopulating=!0,this.populate.populate(this.ui.modals.edit.form,t),this.isPopulating=!1,this.modals.edit.handleOpen())}openBulkEditModal(){window.removeChildren(this.ui.modals.bulkEdit.selected),this.ui.modals.edit.form.reset(),window.chunkIt(this.selected,(t=>{let s=this.store.get(parseInt(t));if(s)return e.push(s.id),window.jvbTemplates.create("bulkItem",s)}),(e=>this.ui.modals.bulkEdit.selected.append(e))).then((()=>{}));let e=Array.from(this.selected).map((e=>this.store.get(parseInt(e)))).filter(Boolean);this.ui.modals.bulkEdit.modal.dataset.itemId=e.join(","),this.ui.modals.bulkEdit.h2&&(this.ui.modals.bulkEdit.h2.textContent=this.selected.size),this.modals.bulkEdit.handleOpen(),this.forms.registerForm(this.ui.modals.bulkEdit.form,{cache:!1}),this.isPopulating=!0,this.populate.populate(this.ui.modals.edit.form,item),this.isPopulating=!1}async savePosts(e="",t=!1){this.changes.size>0&&(this.cancelBackup(),await this.handleBackup());const s=await this.changesStore.getAll();if(console.log("Saving Changes: ",s),0===s.length)return;""===e&&(e=`Saving ${s.length} ${1===s.length?this.singular:this.plural}`);let i={},a=[];s.forEach((e=>{let t=e.id;const{id:s,...l}=e;i[t]=l,e.post_status&&this.shouldRemoveItemUI(e.post_status)&&a.push(t)})),a.length>0&&this.removeItems(a);let l={endpoint:this.endpoint,headers:{action_nonce:window.auth.getNonce("dash")},data:{posts:i},delay:t,popup:"Saving changes",title:e};this.queue.addToQueue(l)}setBulkStatus(e){if(!["publish","draft","trash","delete"].includes(e))return;let t,s=[];if(this.selected.forEach((t=>{s.push(t),this.updateItem(t,"post_status",e)})),"delete"===e)t="Deleting";else t=window.uppercaseFirst(e)+"ing";this.shouldRemoveItemUI(e)&&this.removeItems(s),this.selectionHandler.clearSelection(),this.savePosts(`${t} ${s.length} ${1===s.length?this.singular:this.plural}...`).then((()=>{}))}render(){const e=this.store.getFiltered();if(0!==e.length){switch(this.view){case"grid":this.renderGrid(e);break;case"table":this.renderTable(e).then((()=>{}));break;case"list":this.renderList(e)}this.updateUI()}else this.renderEmpty()}updateUI(){if(this.ui.bulk.action){let e=!1,t=this.ui.bulk.action.querySelector('[value="edit"]'),s=this.status;"trash"===s&&t?(window.removeChildren(this.ui.bulk.action),e=window.jvbTemplates.create("trashOptions")):"trash"===s||t||(window.removeChildren(this.ui.bulk.action),e=window.jvbTemplates.create("notTrashOptions")),e&&e.querySelectorAll("option").forEach(((e,t)=>{0===t&&(e.checked=!0),this.ui.bulk.action.append(e)})),this.ui.bulk.action.value=""}this.selected.size>0&&this.selectionHandler.updateSelectionUI()}renderEmpty(){this.toggleTable(!1),window.removeChildren(this.ui.grid);const e=window.jvbTemplates.create("emptyState");e&&(this.ui.grid.append(e),this.a11y.announceItems(0,!1,!1))}toggleTable(e=!0){if(this.ui.table.selectedColumns&&(this.ui.table.selectedColumns.hidden=!e),e&&!this.ui.table.form){let e=window.jvbTemplates.create("contentTable");this.container.append(e),this.ui.table=window.uiFromSelectors(this.selectors.table),this.ui.table.columns=this.container.querySelectorAll(this.selectors.table.columns)}this.ui.table.form&&(this.ui.table.form.hidden=!e,e||this.forms.clearForm(this.ui.table.form.dataset.formId),this.ui.table.body&&window.removeChildren(this.ui.table.body)),this.keyHandler=this.handleKeys.bind(this),e?document.addEventListener("keydown",this.keyHandler):document.removeEventListener("keydown",this.keyHandler)}renderGrid(e){window.removeChildren(this.ui.grid),this.toggleTable(!1),this.ui.grid.classList.remove("list-view"),this.ui.grid.classList.add("grid-view"),window.chunkIt(e,(e=>this.renderGridItem(e)),(e=>this.ui.grid.append(e))).then((()=>{}))}renderList(e){window.removeChildren(this.ui.grid),this.toggleTable(!1),this.ui.grid.classList.remove("grid-view"),this.ui.grid.classList.add("list-view"),window.chunkIt(e,(e=>this.renderListItem(e)),(e=>this.ui.grid.append(e))).then((()=>{}))}async renderTable(e){this.toggleTable(),window.removeChildren(this.ui.grid),await window.chunkIt(e,(e=>this.renderTableItem(e)),(e=>{this.ui.table.body?this.ui.table.body.append(e):this.ui.table.table.insertBefore(e,this.ui.table.foot)}),5),requestAnimationFrame((()=>{window.jvbSelector?.scanExistingFields(this.ui.table.table)}))}renderGridItem(e){let t=window.jvbTemplates.create("gridView",e);return this.items.set(e.id,t),t}renderListItem(e){let t=window.jvbTemplates.create("listView",e);return this.items.set(e.id,t),t}renderTableItem(e){let t=window.jvbTemplates.create("tableView",e);return this.items.set(e.id,t),t}toggleColumn(e,t){this.ui.table.table.querySelectorAll(`.${e}`).forEach((e=>{e.hidden=!t}))}shouldRemoveItemUI(e){return"all"===this.status&&!["publish","draft"].includes(e)||e!==this.store.filters.status}removeItems(e){e.forEach((e=>{if(this.items.has(e)){let t=this.items.get(e);t&&window.fade(t,!1)}}))}setFilters(e){for(let[t,s]of Object.entries(e)){if(!this.allowedFilters.includes(t)){delete e[t];continue}this.cache.set(t,s);let i=this.findFilterEl(t);this.setElValue(i,s)}this.store.setFilters(e)}setFilter(e,t){if(!this.allowedFilters.includes(e))return;this.cache.set(e,t),"status"===e&&(this.status=t),"orderby"===e&&(this.orderby=t),"order"===e&&(this.order=t);let s=this.findFilterEl(e,t);this.setElValue(s,t),this.store.setFilter(e,t)}deleteFilter(e,t){if(!this.allowedFilters.includes(e))return;if(Object.hasOwn(this.defaults,e))return void this.setFilter(e,this.defaults[e]);let s=this.findFilterEl(e,t);this.setElValue(s,!1),this.cache.remove(e),this.setFilter(e,"")}setElValue(e,t){if(e){if(!t)return["SELECT","TEXTAREA"].includes(e.tagName)&&(e.value=""),["text","search"].includes(e.type)&&(e.value=""),void("radio"===e.type&&(e.checked=!1));["SELECT","TEXTAREA"].includes(e.tagName)&&(e.value=t),["text","search"].includes(e.type)&&(e.value=t),"radio"===e.type&&(e.checked=!0)}}findFilterEl(e,t){if(["date-filter","dateFrom","dateTo"].includes(e)){switch(e){case"date-filter":e="month";break;case"dateFrom":e="start";break;case"dateTo":e="end"}return this.ui.modals.date[e]}if(e.includes("tax_")){const t=e.replace("tax_",""),s=this.ui.filters.taxonomies?.[t];return s||(console.warn("Taxonomy filter element not found:",t),null)}if(!Object.hasOwn(this.ui.filters,e))return console.warn("Filter el not found: ",e),!1;let s=this.ui.filters[e];if("object"==typeof s){if(!Object.hasOwn(this.ui.filters[e],t))return!1;s=this.ui.filters[e][t]}return s}resetForm(e){e.querySelectorAll('input[type="hidden"], input[type="text"], input[type="number"], input[type="email"], input[type="url"], textarea').forEach((e=>{e.value=""})),e.querySelectorAll('input[type="checkbox"], input[type="radio"]').forEach((e=>{e.checked=!1})),e.querySelectorAll("select").forEach((e=>{e.selectedIndex=0})),e.querySelectorAll(".selected-items").forEach((e=>{window.removeChildren(e)})),e.querySelectorAll(".item-grid.preview").forEach((e=>{window.removeChildren(e)}))}destroy(){window.debouncer.cancel(`changes-${this.content}`),this.changes.size>0&&(this.changesStore.saveMany(this.changes).then((()=>{})),this.changes.clear()),this.timelineSortables&&(this.timelineSortables.forEach((e=>e.destroy())),this.timelineSortables=[]);for(let[e,t]of Object.entries(this.ui.modals))t.form&&t.form.removeEventListener("submit",this.submitHandler);document.removeEventListener("click",this.clickHandler),document.removeEventListener("change",this.changeHandler),this.ui.filters.search&&this.ui.filters.search.removeEventListener("input",this.handleInput)}}document.addEventListener("DOMContentLoaded",(async function(){window.auth.subscribe((t=>{if("auth-loaded"===t){let t=document.querySelector("[data-content]");t&&!Object.hasOwn(t.dataset,"ignore")&&(window.crudManager=new e({content:t.dataset.content}))}}))}))})();
(()=>{class e{constructor(){this.container=document.querySelector(".crud[data-content]:not([data-ignore])"),this.container&&(this.content=this.container.dataset.content,this.endpoint=this.container.dataset.endpoint??"content",this.singular=this.container.dataset.singular,this.plural=this.container.dataset.plural,this.queue=window.jvbQueue,this.a11y=window.jvbA11y,this.error=window.jvbError,this.populate=window.jvbPopulate,this.cache=new window.jvbCache(this.content),this.activeItem=null,this.isTimeline=!1,this.isPopulating=!1,this.changes=new Map,this.items=new Map,this.init())}init(){this.initElements(),this.initListeners(),this.defineTemplates();let e=this.initSettings();this.initStore(e),this.checkHideFilters(),this.initIntegrations(),this.initUploader(),this.initModals()}defineTemplates(){const e=window.jvbTemplates,t=this,s=(e,s,i)=>{e.dataset.itemId=i.id;let a=s.checkbox.closest(".preview");window.prefixInput(s.checkbox,`select-${i.id}`,a,!0),s.checkbox.value=i.id,s.checkbox.checked=t.selected.has(parseInt(i.id)),s.selectLabel&&(s.selectLabel.htmlFor=`select-${i.id}`),s.edit&&(s.edit.dataset.id=i.id),s.trash&&(s.trash.dataset.id=i.id)},i=function(e,t,s){if(s?.fields?.post_thumbnail){const e=s.images[s.fields.post_thumbnail]??{};t.img.src=e.medium??"",t.img.alt=e.alt??s.fields.post_title??""}};e.define("gridView",{refs:{img:"img",checkbox:".select-item",selectLabel:"label.select-item-label",edit:'[data-action="edit"]',trash:'[data-action="trash"]'},setup({el:e,refs:t,manyRefs:a,data:l}){s(e,t,l),i(0,t,l)}}),e.define("listView",{refs:{img:"img",checkbox:".select-item",selectLabel:"label.select-item-label",edit:'[data-action="edit"]',trash:'[data-action="trash"]'},manyRefs:{attrs:"[data-attr]",fields:"[data-field]"},setup({el:e,refs:t,manyRefs:a,data:l}){s(e,t,l),i(0,t,l),a?.attrs?.forEach((e=>{const t=l[e.dataset.attr];t&&""!==t?e.textContent=t:e.remove()})),a?.fields?.forEach((e=>{const t=l.fields?.[e.dataset.field];t&&""!==t?"DIV"===e.tagName?e.innerHTML=t:e.textContent=t:e.remove()}))}});let a={};this.isTimeline&&(a.sharedRow="tr.shared",a.point="tr.timeline-point"),e.define("tableView",{refs:{checkbox:".select-item",selectLabel:"label.select-item-label",...a},manyRefs:{inputs:"input,select,textarea",status:'input[name="post_status"]',selectors:'[data-type="selector"]',fields:"[data-field]"},setup({el:e,refs:i,manyRefs:a,data:l}){if(s(e,i,l),a?.inputs?.forEach((e=>{let t=e.closest("[data-field]");window.prefixInput(e,`${l.id}-`,t)})),a?.status?.forEach((e=>{e.value===l.status&&(e.checked=!0)})),t.isTimeline)i.sharedRow&&(i.sharedRow.querySelectorAll("input,select,textarea").forEach((e=>{let t=e.closest("[data-field]");window.prefixInput(e,`${l.id}-`,t)})),t.populate.populate(i.sharedRow,l),i.sharedRow.querySelectorAll('input[name="post_status"]').forEach((e=>{e.value===l.status&&(e.checked=!0)}))),i.point&&l.fields?.timeline&&(Object.entries(l.fields.timeline).forEach((([s,a],n)=>{const o=i.point.cloneNode(!0);o.dataset.index=`${n}`,o.dataset.itemId=a.id,o.querySelectorAll("input,select,textarea").forEach((e=>{let t=e.closest("[data-field]");window.prefixInput(e,`${a.id}-`,t)})),t.populate.populate(o,{fields:a,images:l.images,taxonomies:l.taxonomies});const d=l.images?.[a.post_thumbnail];d&&o.querySelector(".field.upload")?.setAttribute("title",d["image-title"]??""),e.insertBefore(o,i.point)})),i.point.remove());else if(void 0!==t.ui.table.form?.dataset.edit)a?.inputs?.forEach((e=>{let t=e.closest("[data-field]");window.prefixInput(e,`${l.id}-`,t)})),a?.status?.forEach((e=>{e.value===l.status&&(e.checked=!0)})),t.populate.populate(e,l);else{const e=Object.hasOwn(l,"fields")?l.fields:l;a?.fields?.forEach((t=>{if(Object.hasOwn(e,t.dataset.field)&&""!==e[t.dataset.field]){let s=e[t.dataset.field],i=e.children[0];i&&(i.textContent="date"===t.dataset.field?window.formatTimeAgo(s):s)}}))}a?.selectors?.forEach((e=>e.setAttribute("data-lazy","")))}}),e.define("emptyState"),e.define("bulkItem",{refs:{checkbox:"input",img:"img",label:"label"},setup({el:e,refs:t,manyRefs:s,data:i}){t.checkbox&&(t.checkbox.id=`bulk_${i.id}`,t.checkbox.value=i.id,t.checkbox.checked=!0,t.checkbox.name="selected[]");let a=i?.images[i?.fields?.post_thumnbail]??{};t.img&&Object.keys(a).length>0&&(t.img.src=a.medium??"",t.img.alt=a.alt??""),t.label&&(t.label.title=item.fields.post_title)}}),e.define("trashOptions"),e.define("notTrashOptions"),e.define("contentTable")}initElements(){this.allowedFilters=["status","orderby","order","search","date-filter","dateFrom","dateTo"],this.selectors={buttons:{create:".create-item",clearFilters:'[data-action="clear-filters"]'},views:{grid:'input[data-view="grid"]',list:'input[data-view="list"]',table:'input[data-view="table"]'},modals:{create:{modal:"dialog.create",form:"dialog.create form",h2:"dialog.create h2"},edit:{modal:"dialog.edit",form:"dialog.edit form",h2:"dialog.edit h2"},bulkEdit:{modal:"dialog.bulkEdit",selected:"dialog.bulkEdit .selected",h2:"dialog.bulkEdit h2 span",form:"dialog.bulkEdit form"},date:{modal:"dialog.date-range",start:"dialog.date-range .date-start",end:"dialog.date-range .date-end",month:"dialog.date-range .month-select"}},grid:`.${this.content}.item-grid`,table:{nav:"#vertical",form:"form.table",table:"form.table table",body:"form.table body",head:"form.table thead",foot:"form.table tfoot",selectedColumns:".all-filters .multi-select",columns:"thead th"},bulk:{action:".bulk-action-select",count:".bulk-controls .selected-count",control:".bulk-controls .bulk-actions",select:".bulk-controls select",selectAll:".select-all"},filters:{container:"details.all-filters",search:'.all-filters input[type="search"]',status:{all:'[name="status"]#all',publish:'[name="status"]#publish',draft:'[name="status"]#draft',trash:'[name="status"]#trash'},orderby:{date:'[name="orderby"]#date',alphabetical:'[name="orderby"]#alphabetical'},order:{asc:'[name="order"][value="asc"]',desc:'[name="order"][value="desc"]'},date:'[data-filter="date"]'},uploader:"details.uploader"},this.ui=window.uiFromSelectors(this.selectors);const e=document.querySelectorAll('[data-filter="taxonomies"]');e.length>0&&(this.ui.filters.taxonomies={},e.forEach((e=>{const t=e.dataset.taxonomy;this.ui.filters.taxonomies[t]=e,this.allowedFilters.push(`tax_${t}`)}))),this.isTimeline=!!document.querySelector("[data-timeline]")}initUploader(){this.ui.uploader&&(window.jvbUploads.scanFields(this.ui.uploader),window.jvbUploads.subscribe(((e,t)=>{"sent-to-queue"===e&&t===this.ui.uploader.dataset.uploader&&window.debouncer.schedule("crud-complete",(()=>{this.store.clearCache()}))})))}initModals(){this.modals={};for(let[e,t]of Object.entries(this.ui.modals))t.modal&&(this.modals[e]=new window.jvbModal(t.modal),this.modals[e].subscribe(((t,s)=>{if("modal-close"===t){const t=this.ui.modals[e].form.dataset.formId;t&&this.forms.clearForm(t),this.resetForm(this.ui.modals[e].form),"date"===e&&this.handleCustomDateSelection(),["edit","bulkEdit","create"].includes(e)&&window.debouncer.timeouts.has(`save-${this.content}`)&&this.scheduleSave(0)}})))}initStore(e){let t={...this.defaults,...e};const s=window.jvbStore.register(this.content,[{storeName:this.content,keyPath:"id",endpoint:this.endpoint??"content",headers:{"X-Action-Nonce":window.auth.getNonce("dash")},indexes:[{name:"id",keyPath:"id"},{name:"status",keyPath:"status"},{name:"date",keyPath:"date"},{name:"modified",keyPath:"modified"},{name:"title",keyPath:"title"}],filters:t,ignore:["content","user"],TTL:36e5,showLoading:!0},{storeName:"changes",keyPath:"id"}]);this.changesStore=s.changes,this.store=s[this.content],this.store.subscribe(((e,t)=>{if("data-loaded"===e)this.render(),this.selectionHandler.collectItems()})),this.changesStore.subscribe(((e,t)=>{if("data-ready"===e){let e=this.changesStore.getAll();e.length>0&&(e.forEach((e=>{this.changes.set(e.id,e)})),this.savePosts("",!1).then((()=>{})))}}))}initIntegrations(){this.selected=new Set,this.selectionHandler=new window.jvbHandleSelection(this.container,{selectAll:{checkbox:"#select-all",label:".bulk-select label",span:".bulk-select label span"},wrapper:{wrapper:".wrap"},item:{idAttribute:"itemId"}}),this.selectionHandler.subscribe(((e,t)=>{this.selected=new Set([...t.selectedItems].map((e=>parseInt(e)))),this.ui.bulk.control.hidden=0===this.selected.size,this.ui.bulk.count.hidden=0===this.selected.size,this.ui.bulk.count.textContent=`${this.selected.size} ${this.plural} selected`})),this.forms=window.jvbForm,window.jvbUploads&&window.jvbUploads.subscribe(((e,t)=>{"groups_uploaded"===e&&t.content===this.content&&this.handleGroupsUploaded(t)})),this.queue.subscribe(((e,t)=>{if(["image_upload","video_upload","document_upload"].includes(t.type)&&"operation-status"===e&&"completed"===t.status&&this.store.clearCache(),console.log("CRUD.js queue subscription"),console.log(e,t),"operation-status"===e&&"completed"===t.status&&"uploads/groups"===t.endpoint&&(console.log("Grouped Uploads completed"),t.result&&t.result.group_mappings&&this.handleGroupMappings(t.result.group_mappings),console.log("Cleared local cache. Refresh to see changes"),this.store.clearCache()),"operation-status"===e&&"completed"===t.status&&"content_update"===t.type){if(console.log("Cleared local cache. Refresh to see changes"),this.store.clearCache(),!t.result||!t.result.posts)return void console.warn("Content update completed but no result.posts",t);const e=Object.keys(t.result.posts);if(0===e.length)return;this.changesStore.deleteMany(e),e.forEach((e=>this.changes.delete(e)))}}))}initSettings(){this.defaults={content:this.content,user:window.auth.getUser(),page:1,status:"all",orderby:"date",order:"desc",search:""};let e={},t=this.container.dataset.view??"grid";this.view=this.cache.get("view")??t,this.view!==t&&(this.ui.views[this.view].checked=!0),this.status=this.cache.get("status")??this.defaults.status,this.status!==this.defaults.status&&(this.ui.filters.status[this.status].checked=!0,e.status=this.status),this.orderby=this.cache.get("orderby")??this.defaults.orderby,this.orderby!==this.defaults.orderby&&(this.ui.filters.orderby[this.orderby].checked=!0,e.orderBy=this.orderby),this.order=this.cache.get("order")??this.defaults.order,this.order!==this.defaults.order&&(this.ui.filters.order[this.order].checked=!0,e.order=this.order),this.ui.filters.taxonomies&&Object.entries(this.ui.filters.taxonomies).forEach((([t,s])=>{const i=`tax_${t}`,a=this.cache.get(i);a&&(s.value=a,e[i]=a)}));let s=this.cache.get("tabNav")??"horizontal";this.ui.table.nav&&"vertical"===s&&(this.ui.table.nav.checked=!0);let i={showFilters:{element:this.ui.filters.container,default:"closed"},showUploader:{element:this.ui.uploader,default:"open"}};for(let[e,t]of Object.entries(i))if(t.element){let s=this.cache.get(e)??t.default;t.element.open="open"===s,t.element.addEventListener("toggle",(()=>{this.cache.set(e,t.element.open?"open":"closed")}))}return e}initListeners(){this.changeHandler=this.handleChange.bind(this),this.clickHandler=this.handleClick.bind(this),this.inputHandler=this.handleInput.bind(this),this.submitHandler=this.handleModalSubmit.bind(this),document.addEventListener("change",this.changeHandler),document.addEventListener("click",this.clickHandler),this.ui.filters.search&&this.ui.filters.search.addEventListener("input",this.inputHandler);for(let[e,t]of Object.entries(this.ui.modals))t.form&&t.form.addEventListener("submit",this.submitHandler)}handleModalSubmit(e){e.preventDefault();const t=e.target.closest("dialog");if(!t)return;let s=`Saving changes for multiple ${this.plural}`;t.classList.contains("edit")?s="Saving your edits...":t.classList.contains("create")&&(s=`Creating your new ${this.singular}`),this.scheduleSave(0)}handleChange(e){const t=e.target.closest("[data-item-id]"),s=e.target.matches("[data-filter]"),i=e.target.matches(".bulk-action-select"),a=e.target.matches("[data-view]");if(t||s||i||a)if(this.isPopulating||!t||e.target.closest("[data-ignore], .select-item")){if(a)return this.items.clear(),void this.handleViewChange(e.target);if(i)this.handleBulkAction(e.target);else if(s)this.handleFilterChange(e.target);else if("table"===this.view){if(e.target.matches("details.multi-select"))return void this.toggleColumn(e.target.id,e.target.checked);e.target.matches(this.selectors.table.nav)&&(this.tabNav=e.target.checked,this.cache.set("tabNav",e.target.checked?"vertical":"horizontal"))}}else this.handleItemUpdate(e)}handleBulkAction(e){if(e.value.startsWith("tax-")){const t=e.options[e.selectedIndex],s=t.dataset.taxonomy,i=t.dataset.single,a=t.dataset.plural;return window.jvbSelector.openEmpty(s,i,a,(e=>this.handleBulkTaxonomy(e))),void(e.value="")}switch(e.value){case"edit":this.openBulkEditModal();break;case"publish":case"trash":case"delete":this.setBulkStatus(e.value);break;case"draft":case"restore":this.setBulkStatus("draft")}}handleBulkTaxonomy(e){e.termIds.length&&this.selected.size&&(this.selected.forEach((t=>{const s=this.store.get(t);if(!s)return;const i=(s.taxonomies?.[e.taxonomy]||[]).map((e=>e.id)),a=[...new Set([...i,...e.termIds])];this.updateItem(t,e.taxonomy,a)})),this.savePosts(`Adding ${e.terms.length} ${e.taxonomy} to ${this.selected.size} ${this.plural}...`).then((()=>{})),this.selectionHandler.clearSelection())}handleItemUpdate(e){let t=window.targetCheck(e,"[data-item-id]");t&&t.dataset.itemId.split(",").forEach((t=>{let s=this.forms.getField(e.target);if(["repeater","tag-list"].includes(s.dataset.fieldType))return;let i=s.dataset.field,a=this.forms.getFieldValue(e.target);this.updateItem(t,i,a)}))}updateItem(e,t,s){this.changes.has(e)||this.changes.set(e,{id:e,content:this.content}),this.changes.get(e)[t]=s,this.scheduleBackup(),"number"!=typeof e&&e.includes("group")||this.scheduleSave()}scheduleBackup(){window.debouncer.schedule(`changes-${this.content}`,(async()=>{this.changes.size>0&&await this.handleBackup()}),2e3)}cancelBackup(){window.debouncer.cancel(`changes-${this.content}`)}async handleBackup(){const e=Array.from(this.changes.values());this.changes.clear();const t=e.map((e=>e.id)),s=await Promise.all(t.map((e=>this.changesStore.get(e)))),i=e.map(((e,t)=>s[t]?window.deepMerge(s[t],e):e));await this.changesStore.saveMany(i)}scheduleSave(e=1e4){window.debouncer.schedule(`save-${this.content}`,(async()=>{this.changes.size>0&&(this.cancelBackup(),await this.handleBackup()),await this.savePosts("",!1)}),e)}handleFilterChange(e){let t=e.dataset.filter;return"date"===t&&"custom"===e.value?(e.value="",void this.modals.date.handleOpen()):"date"===t&&""!==e.value?(this.setFilter("date-filter",e.value),this.deleteFilter("dateFrom"),this.deleteFilter("dateTo"),void this.checkHideFilters()):("taxonomies"===t&&(t=`tax_${e.dataset.taxonomy}`),void this.setFilter(t,e.value))}checkHideFilters(){const e=this.store.filters,t=Object.entries(e).some((([e,t])=>!["content","user","page"].includes(e)&&(this.defaults[e]!==t&&""!==t&&null!==t)));this.ui.buttons.clearFilters.hidden=!t}clearAllFilters(){let e=this.store.filters;this.store.clearFilters();for(let[t,s]of Object.entries(e))this.cache.remove(t),this.deleteFilter(t,s);this.a11y.announce("All filters cleared")}handleCustomDateSelection(){if(this.ui.modals.date.month&&this.ui.modals.date.month.value){const[e,t]=this.ui.modals.date.month.value.split("-"),s=`${e}-${t}-01`,i=new Date(e,parseInt(t),0).getDate(),a=`${e}-${t}-${String(i).padStart(2,"0")}`;this.setFilter("dateFrom",s),this.setFilter("dateTo",a),this.deleteFilter("date-filter"),this.ui.modals.date.month.value=""}else this.ui.modals.date.start&&this.ui.modals.date.start.value&&this.ui.modals.date.end&&this.ui.modals.date.end.value&&(this.setFilter("dateFrom",this.ui.modals.date.start.value),this.setFilter("dateTo",this.ui.modals.date.end.value),this.deleteFilter("date-filter"),this.ui.modals.date.start.value="",this.ui.modals.date.end.value="");this.checkHideFilters()}handleViewChange(e){this.view=e.dataset.view,this.cache.set("view",this.view),this.render()}handleClick(e){if(e.target.matches(".clear-search"))return void this.deleteFilter("search","");const t=e.target.closest("[data-action]");return t?(e.preventDefault(),void this.handleActionButton(t)):e.target.matches(".apply-date-filter")?(this.handleCustomDateSelection(),void this.modals.date.handleClose()):void(e.target.matches(this.selectors.buttons.create)&&this.openCreateModal())}openCreateModal(){this.forms.registerForm(this.ui.modals.create.form,{cache:!1}),this.ui.modals.create.modal.dataset.itemId=window.generateID("new"),this.modals.create.handleOpen()}handleActionButton(e){const t=e.dataset.id;switch(e.dataset.action){case"edit":this.openEditModal(t);break;case"delete":confirm("Delete this item? This cannot be undone")&&(this.updateItem(t,"post_status","delete"),window.fade(e.closest(".item"),!1),this.savePosts(`Permanently deleting ${this.singular}...`).then((()=>{})),this.store.delete(t));break;case"trash":"trash"===this.status?confirm("Delete this item? This cannot be undone")&&(this.updateItem(t,"post_status","delete"),window.fade(e.closest(".item"),!1),this.savePosts(`Permanently deleting ${this.singular}...`).then((()=>{})),this.store.delete(t)):(this.updateItem(t,"post_status","trash"),window.fade(e.closest(".item"),!1),this.savePosts(`Sending ${this.singular} to trash...`).then((()=>{})));break;case"bulk-edit":this.selected.size>0&&this.openBulkEditModal();break;case"bulk-delete":this.handleBulkDelete();break;case"refresh":this.store.clearCache(),this.store.fetch();break;case"clear-filters":this.clearAllFilters()}}handleBulkDelete(){let e="trash"===this.status;if(this.selected.size>0&&confirm(`${e?"Permanently delete":"Send"} ${this.selected.size} ${1===this.selected.size?this.singular:this.plural}${e?"":"to trash"}?`)){this.selected.forEach((t=>{this.store.delete(t),this.updateItem(t,"post_status",e?"delete":"trash")}));let t=e?`Permanently deleting ${this.selected.size} ${1===this.selected.size?this.singular:this.plural}`:`Sending ${this.selected.size} ${1===this.selected.size?this.singular:this.plural} to trash`;this.savePosts(t).then((()=>{})),this.selectionHandler.clearSelection()}}handleInput(e){e.preventDefault(),e.stopPropagation();let t=e.target.value.trim(),s=`${this.content}-search`;0!==t.length?window.debouncer.schedule(s,(()=>{this.a11y.announce(`Searching for "${t}"...`),this.store.setFilters({search:t,page:1})}),300):this.deleteFilter("search","")}handleKeys(e){if(this.tabNav&&"Tab"===e.key){e.preventDefault();const t=e.target.closest("[data-field]"),s=e.target.closest("tr");if(!t||!s)return;const i=t.dataset.field,a=e.shiftKey;let l=this.findNextEditableRow(s,a);l||(l=this.wrapToRow(s,a)),l&&this.focusFieldInRow(l,i,a)}}findNextEditableRow(e,t=!1){let s=t?e.previousElementSibling:e.nextElementSibling;for(;s&&!this.isEditableRow(s);)s=t?s.previousElementSibling:s.nextElementSibling;return s}wrapToRow(e,t=!1){if(this.isTimeline){const s=e.closest("tbody");if(!s)return null;const i=Array.from(s.querySelectorAll("tr")).filter((e=>this.isEditableRow(e)));return t?i[i.length-1]:i[0]}{if(!this.ui.table.body)return null;const e=Array.from(this.ui.table.body.querySelectorAll("tr")).filter((e=>this.isEditableRow(e)));return t?e[e.length-1]:e[0]}}isEditableRow(e){return!e.closest("thead")&&!e.closest("tfoot")&&(this.isTimeline?e.classList.contains("shared")||e.classList.contains("timeline-point"):!!e.dataset.itemId)}focusFieldInRow(e,t,s=!1){const i=e.querySelector(`[data-field="${t}"]`);if(!i)return;const a=this.findFocusableInput(i);if(a){a.focus(),a.select&&"text"===a.type&&a.select();const e=s?"next":"previous";this.a11y?.announce(`Moved to ${t} in ${e} row`)}}findFocusableInput(e){const t=['input:not([type="hidden"]):not([disabled])',"textarea:not([disabled])","select:not([disabled])","button:not([disabled])"];for(const s of t){const t=e.querySelector(s);if(t)return t}return null}openEditModal(e){let t=this.store.get(parseInt(e));t&&(this.activeItem=t.id,this.ui.modals.edit.modal.dataset.itemId=e,this.ui.modals.edit.modal.dataset.content=this.content,this.ui.modals.edit.h2.textContent=`Editing ${""===t.fields.post_title?this.singular:t.fields.post_title}`,this.ui.modals.edit.form.dataset.formId=`edit-${e}`,this.forms.registerForm(this.ui.modals.edit.form,{cache:!1}),this.isPopulating=!0,this.populate.populate(this.ui.modals.edit.form,t),this.isPopulating=!1,this.modals.edit.handleOpen())}openBulkEditModal(){window.removeChildren(this.ui.modals.bulkEdit.selected),this.ui.modals.edit.form.reset(),window.chunkIt(this.selected,(t=>{let s=this.store.get(parseInt(t));if(s)return e.push(s.id),window.jvbTemplates.create("bulkItem",s)}),(e=>this.ui.modals.bulkEdit.selected.append(e))).then((()=>{}));let e=Array.from(this.selected).map((e=>this.store.get(parseInt(e)))).filter(Boolean);this.ui.modals.bulkEdit.modal.dataset.itemId=e.join(","),this.ui.modals.bulkEdit.h2&&(this.ui.modals.bulkEdit.h2.textContent=this.selected.size),this.modals.bulkEdit.handleOpen(),this.forms.registerForm(this.ui.modals.bulkEdit.form,{cache:!1}),this.isPopulating=!0,this.populate.populate(this.ui.modals.edit.form,item),this.isPopulating=!1}async savePosts(e="",t=!1){this.changes.size>0&&(this.cancelBackup(),await this.handleBackup());const s=await this.changesStore.getAll();if(console.log("Saving Changes: ",s),0===s.length)return;""===e&&(e=`Saving ${s.length} ${1===s.length?this.singular:this.plural}`);let i={},a=[];s.forEach((e=>{let t=e.id;const{id:s,...l}=e;i[t]=l,e.post_status&&this.shouldRemoveItemUI(e.post_status)&&a.push(t)})),a.length>0&&this.removeItems(a);let l={endpoint:this.endpoint,headers:{"X-Action-Nonce":window.auth.getNonce("dash")},data:{posts:i},delay:t,popup:"Saving changes",title:e};this.queue.addToQueue(l)}setBulkStatus(e){if(!["publish","draft","trash","delete"].includes(e))return;let t,s=[];if(this.selected.forEach((t=>{s.push(t),this.updateItem(t,"post_status",e)})),"delete"===e)t="Deleting";else t=window.uppercaseFirst(e)+"ing";this.shouldRemoveItemUI(e)&&this.removeItems(s),this.selectionHandler.clearSelection(),this.savePosts(`${t} ${s.length} ${1===s.length?this.singular:this.plural}...`).then((()=>{}))}render(){const e=this.store.getFiltered();if(0!==e.length){switch(this.view){case"grid":this.renderGrid(e);break;case"table":this.renderTable(e).then((()=>{}));break;case"list":this.renderList(e)}this.updateUI()}else this.renderEmpty()}updateUI(){if(this.ui.bulk.action){let e=!1,t=this.ui.bulk.action.querySelector('[value="edit"]'),s=this.status;"trash"===s&&t?(window.removeChildren(this.ui.bulk.action),e=window.jvbTemplates.create("trashOptions")):"trash"===s||t||(window.removeChildren(this.ui.bulk.action),e=window.jvbTemplates.create("notTrashOptions")),e&&e.querySelectorAll("option").forEach(((e,t)=>{0===t&&(e.checked=!0),this.ui.bulk.action.append(e)})),this.ui.bulk.action.value=""}this.selected.size>0&&this.selectionHandler.updateSelectionUI()}renderEmpty(){this.toggleTable(!1),window.removeChildren(this.ui.grid);const e=window.jvbTemplates.create("emptyState");e&&(this.ui.grid.append(e),this.a11y.announceItems(0,!1,!1))}toggleTable(e=!0){if(this.ui.table.selectedColumns&&(this.ui.table.selectedColumns.hidden=!e),e&&!this.ui.table.form){let e=window.jvbTemplates.create("contentTable");this.container.append(e),this.ui.table=window.uiFromSelectors(this.selectors.table),this.ui.table.columns=this.container.querySelectorAll(this.selectors.table.columns)}this.ui.table.form&&(this.ui.table.form.hidden=!e,e||this.forms.clearForm(this.ui.table.form.dataset.formId),this.ui.table.body&&window.removeChildren(this.ui.table.body)),this.keyHandler=this.handleKeys.bind(this),e?document.addEventListener("keydown",this.keyHandler):document.removeEventListener("keydown",this.keyHandler)}renderGrid(e){window.removeChildren(this.ui.grid),this.toggleTable(!1),this.ui.grid.classList.remove("list-view"),this.ui.grid.classList.add("grid-view"),window.chunkIt(e,(e=>this.renderGridItem(e)),(e=>this.ui.grid.append(e))).then((()=>{}))}renderList(e){window.removeChildren(this.ui.grid),this.toggleTable(!1),this.ui.grid.classList.remove("grid-view"),this.ui.grid.classList.add("list-view"),window.chunkIt(e,(e=>this.renderListItem(e)),(e=>this.ui.grid.append(e))).then((()=>{}))}async renderTable(e){this.toggleTable(),window.removeChildren(this.ui.grid),await window.chunkIt(e,(e=>this.renderTableItem(e)),(e=>{this.ui.table.body?this.ui.table.body.append(e):this.ui.table.table.insertBefore(e,this.ui.table.foot)}),5),requestAnimationFrame((()=>{window.jvbSelector?.scanExistingFields(this.ui.table.table)}))}renderGridItem(e){let t=window.jvbTemplates.create("gridView",e);return this.items.set(e.id,t),t}renderListItem(e){let t=window.jvbTemplates.create("listView",e);return this.items.set(e.id,t),t}renderTableItem(e){let t=window.jvbTemplates.create("tableView",e);return this.items.set(e.id,t),t}toggleColumn(e,t){this.ui.table.table.querySelectorAll(`.${e}`).forEach((e=>{e.hidden=!t}))}handleGroupsUploaded(e){const{posts:t,fieldId:s}=e;let i=window.jvbUploads,a=i.fields.get(s);console.log(t),console.log(a);let l=[];t.forEach((e=>{const t={id:e.groupId,title:e.fields.post_title||`New ${this.singular}`,status:"draft",date:(new Date).toISOString(),modified:(new Date).toISOString(),thumbnail:null,icon:this.content,taxonomies:{},fields:e.fields,images:{}};e.images.forEach(((e,s)=>{let a=e.upload_id;0===s&&(t.fields.post_thumbnail=e);let l=i.stores.uploads.get(a);l&&(t.images[a]={"image-alt-text":"","image-caption":"","image-title":l.fields.originalName,medium:i.createPreviewUrl(i.formatFile(l))})})),l.push(t)})),this.store.saveMany(l).then((()=>this.render())),this.a11y.announce(`${t.length} ${1===t.length?this.singular:this.plural} created. Waiting for server confirmation...`)}handleGroupMappings(e){console.log("[CRUD] Applying group mappings:",e);for(const[t,s]of Object.entries(e)){let e={};this.changes.has(t)&&(e=this.changes.get(t),this.changes.delete(t));let i=this.changesStore.get(t)??{};(e.size>0||i.size>0)&&(e=window.deepMerge(i,e),this.changes.set(s,e),this.scheduleBackup())}}shouldRemoveItemUI(e){return"all"===this.status&&!["publish","draft"].includes(e)||e!==this.store.filters.status}removeItems(e){e.forEach((e=>{if(this.items.has(e)){let t=this.items.get(e);t&&window.fade(t,!1)}}))}setFilters(e){for(let[t,s]of Object.entries(e)){if(!this.allowedFilters.includes(t)){delete e[t];continue}this.cache.set(t,s);let i=this.findFilterEl(t);this.setElValue(i,s)}this.store.setFilters(e)}setFilter(e,t){if(!this.allowedFilters.includes(e))return;this.cache.set(e,t),"status"===e&&(this.status=t),"orderby"===e&&(this.orderby=t),"order"===e&&(this.order=t);let s=this.findFilterEl(e,t);this.setElValue(s,t),this.store.setFilter(e,t)}deleteFilter(e,t){if(!this.allowedFilters.includes(e))return;if(Object.hasOwn(this.defaults,e))return void this.setFilter(e,this.defaults[e]);let s=this.findFilterEl(e,t);this.setElValue(s,!1),this.cache.remove(e),this.setFilter(e,"")}setElValue(e,t){if(e){if(!t)return["SELECT","TEXTAREA"].includes(e.tagName)&&(e.value=""),["text","search"].includes(e.type)&&(e.value=""),void("radio"===e.type&&(e.checked=!1));["SELECT","TEXTAREA"].includes(e.tagName)&&(e.value=t),["text","search"].includes(e.type)&&(e.value=t),"radio"===e.type&&(e.checked=!0)}}findFilterEl(e,t){if(["date-filter","dateFrom","dateTo"].includes(e)){switch(e){case"date-filter":e="month";break;case"dateFrom":e="start";break;case"dateTo":e="end"}return this.ui.modals.date[e]}if(e.includes("tax_")){const t=e.replace("tax_",""),s=this.ui.filters.taxonomies?.[t];return s||(console.warn("Taxonomy filter element not found:",t),null)}if(!Object.hasOwn(this.ui.filters,e))return console.warn("Filter el not found: ",e),!1;let s=this.ui.filters[e];if("object"==typeof s){if(!Object.hasOwn(this.ui.filters[e],t))return!1;s=this.ui.filters[e][t]}return s}resetForm(e){e.querySelectorAll('input[type="hidden"], input[type="text"], input[type="number"], input[type="email"], input[type="url"], textarea').forEach((e=>{e.value=""})),e.querySelectorAll('input[type="checkbox"], input[type="radio"]').forEach((e=>{e.checked=!1})),e.querySelectorAll("select").forEach((e=>{e.selectedIndex=0})),e.querySelectorAll(".selected-items").forEach((e=>{window.removeChildren(e)})),e.querySelectorAll(".item-grid.preview").forEach((e=>{window.removeChildren(e)}))}destroy(){window.debouncer.cancel(`changes-${this.content}`),this.changes.size>0&&(this.changesStore.saveMany(this.changes).then((()=>{})),this.changes.clear()),this.timelineSortables&&(this.timelineSortables.forEach((e=>e.destroy())),this.timelineSortables=[]);for(let[e,t]of Object.entries(this.ui.modals))t.form&&t.form.removeEventListener("submit",this.submitHandler);document.removeEventListener("click",this.clickHandler),document.removeEventListener("change",this.changeHandler),this.ui.filters.search&&this.ui.filters.search.removeEventListener("input",this.handleInput)}}document.addEventListener("DOMContentLoaded",(async function(){window.auth.subscribe((t=>{if("auth-loaded"===t){let t=document.querySelector("[data-content]");t&&!Object.hasOwn(t.dataset,"ignore")&&(window.crudManager=new e({content:t.dataset.content}))}}))}))})();
assets/js/min/favouritesManager.min.js
@@ -1 +1 @@
window.favouritesManager=class{constructor(){this.queue=window.jvbQueue,this.loadingManager=window.jvbLoading,this.cache=window.jvbCache,this.a11y=window.jvbA11y,this.error=window.jvbError,this.tabs=new window.jvbTabs(document.querySelector(".replace")),this.config={endpoints:{favourites:"favourites",lists:"favourites/lists",shares:"favourites/lists/shares"},selectors:{container:".favourites.container",itemsTab:'.tab-content[data-tab="items"]',listsTab:'.tab-content[data-tab="lists"]',grid:".item-grid",typeFilters:".type-filters",viewControls:".view-controls",bulkControls:".bulk-controls",selectAll:"#select-all",createListModal:".create-list-modal",addToListModal:".add-to-list-modal",shareListModal:".share-list-modal",noItems:".no-favourites",listContainer:".lists-container",listDetails:".list-details",loader:".favourites-loader"},defaultPage:1,defaultPerPage:24,defaultViewMode:"grid",refreshInterval:6e4,toastDuration:3e3},document.addEventListener("keydown",this.handleKeyDown.bind(this)),this.state={selectedItems:new Set,page:this.config.defaultPage,filter:{type:"all",order:"desc",orderBy:"date_added"},view:{mode:localStorage.getItem("favourites_view")||this.config.defaultViewMode,activeTab:"items"},pagination:{hasMore:!1,totalItems:0,totalPages:0},currentListId:null,loading:!1,initialized:!1},this.initDom(),this.initEvents(),this.loadInitialData(),this.state.initialized=!0}initDom(){this.container=document.querySelector(this.config.selectors.container),this.container?(this.grid=this.container.querySelector(this.config.selectors.grid),this.typeFilters=this.container.querySelector(this.config.selectors.typeFilters),this.viewControls=this.container.querySelector(this.config.selectors.viewControls),this.bulkControls=this.container.querySelector(this.config.selectors.bulkControls),this.listContainer=this.container.querySelector(this.config.selectors.listContainer),this.listDetails=this.container.querySelector(this.config.selectors.listDetails),this.loader=this.container.querySelector(this.config.selectors.loader),this.createListModal=document.querySelector(this.config.selectors.createListModal),this.addToListModal=document.querySelector(this.config.selectors.addToListModal),this.shareListModal=document.querySelector(this.config.selectors.shareListModal),this.grid&&this.state.view.mode&&this.grid.classList.add(`${this.state.view.mode}-view`)):console.warn("Favourites container not found")}initEvents(){if(this.typeFilters&&this.typeFilters.addEventListener("click",(t=>{const e=t.target.closest(".type-filter");e&&this.setFilterType(e.dataset.type)})),this.viewControls&&this.viewControls.addEventListener("click",(t=>{const e=t.target.closest(".view-toggle");e&&this.setView(e.dataset.view)})),this.container){const t=this.container.querySelector(this.config.selectors.selectAll);t&&t.addEventListener("change",(()=>{this.toggleSelectAll(t.checked)})),this.container.addEventListener("change",(t=>{t.target.matches(".item-select input[type=checkbox]")&&this.handleItemSelection(t.target)}));const e=this.container.querySelector(".bulk-action-select"),s=this.container.querySelector(".apply-bulk");e&&s&&s.addEventListener("click",(()=>{this.applyBulkAction(e.value)}));const i=this.container.querySelector(".cancel-bulk");i&&i.addEventListener("click",(()=>{this.clearSelection()}))}this.initModalEvents(),this.container.addEventListener("click",this.handleItemActions.bind(this)),this.grid&&this.setupInfiniteScroll()}initModalEvents(){if(this.createListModal){const t=this.createListModal.querySelector("form");t&&t.addEventListener("submit",(e=>{e.preventDefault(),this.handleCreateList(new FormData(t))}));const e=this.createListModal.querySelector(".cancel");e&&e.addEventListener("click",(()=>{this.createListModal.close()}))}if(this.addToListModal){const t=this.addToListModal.querySelector("form");t&&t.addEventListener("submit",(e=>{e.preventDefault(),this.handleAddToList(new FormData(t))}));const e=this.addToListModal.querySelector(".cancel");e&&e.addEventListener("click",(()=>{this.addToListModal.close()}))}if(this.shareListModal){const t=this.shareListModal.querySelector("form");t&&t.addEventListener("submit",(e=>{e.preventDefault(),this.handleShareList(new FormData(t))}));const e=this.shareListModal.querySelector(".cancel");e&&e.addEventListener("click",(()=>{this.shareListModal.close()}));const s=this.shareListModal.querySelector(".add-email");s&&s.addEventListener("click",(()=>{const e=this.shareListModal.querySelector("#share-email");e&&e.value&&this.handleShareList(new FormData(t))}))}}setupInfiniteScroll(){let t=this.container.querySelector(".scroll-sentinel");t||(t=document.createElement("div"),t.className="scroll-sentinel",t.setAttribute("aria-hidden","true"),this.grid.parentNode.appendChild(t)),new IntersectionObserver((t=>{t.forEach((t=>{t.isIntersecting&&this.state.pagination.hasMore&&!this.state.loading&&(this.state.page++,this.loadFavourites())}))}),{rootMargin:"200px"}).observe(t)}async loadInitialData(){this.loadingManager.show();try{await this.loadFavourites(),this.loadLists().catch((t=>{console.error("Error loading lists:",t)}))}catch(t){this.handleError(t,"loading initial data")}finally{this.loadingManager.hide()}}async loadFavourites(t=!0){if(!this.state.loading)try{this.state.loading=!0,this.loadingManager.show();const e=new URLSearchParams({page:this.state.page,per_page:this.config.defaultPerPage,type:"all"!==this.state.filter.type?this.state.filter.type:"",order:this.state.filter.order,orderby:this.state.filter.orderBy});t&&(this.state.page=1,removeChildren(this.grid),this.grid.classList.remove("empty"));const s=await this.cache.fetchWithCache(`${jvbSettings.api}${this.config.endpoints.favourites}?${e}`,{method:"GET",headers:{"X-WP-Nonce":window.auth.getNonce(),action_nonce:window.auth.getNonce("favourites")}},{context:"favouritesManager",forceRefresh:!0});return this.renderFavourites(s.favourites||[],this.state.page>1),s.counts&&this.updateTypeFilters(s.counts),s.pagination&&(this.state.pagination={hasMore:s.pagination.has_more,totalItems:s.pagination.total_items,totalPages:s.pagination.total_pages}),s}catch(t){throw this.handleError(t,"loading favourites"),t}finally{this.state.loading=!1,this.loadingManager.hide()}}renderFavourites(t,e=!1){this.grid&&(0!==t.length||e?(this.hideEmptyState(),e||removeChildren(this.grid),t.forEach((t=>{const e=this.createItemElement(t);this.grid.appendChild(e),this.initItemFunctionality(e,t)})),window.jvbA11y&&window.jvbA11y.announce(`${e?"Added":"Loaded"} ${t.length} favourites`)):this.showEmptyState())}createItemElement(t){const e=document.createElement("div");e.className=`item ${t.type} favourited`,e.dataset.id=t.target_id,e.dataset.type=t.type;const s=sanitizeHtml(t.title||!1),i=sanitizeHtml(t.notes||"");let a="";return t.thumbnail&&(a=`\n                <div class="item-thumbnail">\n                    <a href="${t.url}">${t.thumbnail}</a>\n                </div>\n            `),e.innerHTML=`\n            <div class="item-select">\n                <input type="checkbox"\n                   class="favourite-checkbox"\n                    id="select-${t.target_id}"\n                    value="${t.target_id}">\n                <label for="select-${t.target_id}"><span class="screen-reader-text">Select this ${t.type}</span</label>\n            </div>\n\n            <button type="button" class="favourite-button favourited"\n                onclick="toggleFavourite(this)"\n                data-id="${t.target_id}"\n                data-type="${t.type}"\n                title="Remove from favourites">\n                ${jvbSettings.icons["heart-filled"]}\n            </button>\n\n            ${a}\n\n            <div class="item-info">\n                ${s?`<h3><a href="${t.url}">${s}</a></h3>`:`<a href="${t.url}">View Item</a>`}\n\n                ${t.author?`\n                <div class="item-artist">\n                    <span>By ${t.author.name}</span>\n                </div>`:""}\n\n                ${t.taxonomies?.length?`\n                <div class="taxonomy-lists">\n                    ${t.taxonomies.map((t=>`\n                        <div class="taxonomy-group">\n                            ${jvbSettings.icons[t.icon]}\n                            <ul>\n                                ${t.terms.slice(0,3).map((t=>`\n                                    <li>\n                                        <a href="${t.url}" ${t.umami_click}>\n                                            ${t.title}\n                                        </a>\n                                    </li>\n                                `)).join("")}\n                            </ul>\n                        </div>\n                    `)).join("")}\n                </div>\n            `:""}\n\n                <div class="notes-section">\n                    <button type="button" class="toggle-notes" aria-expanded="false">\n                        ${jvbSettings.icons.note||"Notes"}\n                        <span>Notes</span>\n                    </button>\n\n                    <div class="notes-content" hidden>\n                        <textarea class="notes-input"\n                            placeholder="Add notes about this item..."\n                            data-id="${t.target_id}"\n                            data-type="${t.type}">${i}</textarea>\n                        <button type="button" class="save-notes">Save Notes</button>\n                    </div>\n                </div>\n            </div>\n        `,e}initItemFunctionality(t,e){const s=t.querySelector(".toggle-notes"),i=t.querySelector(".notes-content");s&&i&&s.addEventListener("click",(()=>{const t="true"===s.ariaExpanded;s.ariaExpanded=!t.toString(),i.hidden=t,t||i.querySelector("textarea")?.focus()}));const a=t.querySelector(".save-notes"),n=t.querySelector(".notes-input");a&&n&&(a.addEventListener("click",(()=>{this.saveNotes(n)})),n.addEventListener("keydown",(t=>{"Enter"===t.key&&(t.ctrlKey||t.metaKey)&&(t.preventDefault(),this.saveNotes(n))})))}saveNotes(t){if(!t)return;const e=t.value.trim(),s=t.dataset.id,i=t.dataset.type;s&&i&&(this.queue.addToQueue({type:"favourite_notes",data:{type:i,target_id:parseInt(s),notes:e}}),showToast("Notes saved"),this.a11y.announce("Notes saved"))}showEmptyState(t=!1){const e=this.container.querySelector(this.config.selectors.noItems)??this.createEmptyElement;e&&(e.hidden=!1),this.grid&&this.grid.classList.add("empty"),this.a11y.announce("No favourites to show!")}hideEmptyState(){const t=this.container.querySelector(".no-favourites");t&&t.remove(),this.grid&&this.grid.classList.remove("empty")}createEmptyElement(t=!1){const e=document.createElement("div");e.className="no-favourites",e.innerHTML="\n            <h3>♡ BLANK CANVAS â™¡</h3>\n            <p>You haven't fallen in love with any pieces... yet!</p>\n            <p>Hit that heart icon when something stops your scroll.</p>\n            <p>Your dream collection is waiting to start.</p>\n        ",this.grid.after(e)}showEmptyListState(t=!1){const e=document.createElement("div");e.className="no-favourites",e.innerHTML="\n            <h3>♡ FULL OF POSSIBILITY â™¡</h3>\n            <p>There's nothing in this list yet.</p>\n            <p>Add some gap fillers from the main favourites tab.</p>\n        ",this.grid.after(e),this.grid.classList.add("empty"),this.a11y.announce("No favourites to show!")}async loadMoreItems(){!this.state.loading&&this.state.pagination.hasMore&&(this.state.page+=1,await this.loadFavourites())}updateTypeFilters(t){this.typeFilters&&this.typeFilters.querySelectorAll(".type-filter").forEach((e=>{const s=e.querySelector(".count");if(!s)return;const i=e.dataset.type;if("all"===i){const e=Object.values(t).reduce(((t,e)=>t+(parseInt(e)||0)),0);s.textContent=`(${e})`}else s.textContent=`(${t[i]||0})`}))}setFilterType(t){t!==this.state.filter.type&&(this.typeFilters&&this.typeFilters.querySelectorAll(".type-filter").forEach((e=>{e.classList.toggle("active",e.dataset.type===t),e.setAttribute("aria-selected",e.dataset.type===t)})),this.state.filter.type=t,this.state.page=1,this.loadFavourites(),window.jvbA11y&&window.jvbA11y.announce(`Filtered to show ${"all"===t?"all":t} items`))}setView(t){t!==this.state.view.mode&&(this.viewControls&&this.viewControls.querySelectorAll(".view-toggle").forEach((e=>{const s=e.dataset.view===t;e.setAttribute("aria-pressed",s)})),this.grid&&(this.grid.classList.remove("grid-view","list-view"),this.grid.classList.add(`${t}-view`)),this.state.view.mode=t,localStorage.setItem("favourites_view",t),window.jvbA11y&&window.jvbA11y.announce(`Changed to ${t} view`))}toggleSelectAll(t){const e=this.getVisibleItems();e.forEach((e=>{const s=e.querySelector('.item-select input[type="checkbox"]');s&&(s.checked=t,this.toggleItemSelection(s.value,t))})),this.updateBulkControls(),window.jvbA11y&&window.jvbA11y.announce(t?`Selected all ${e.length} items`:"Deselected all items")}getVisibleItems(){return this.grid?Array.from(this.grid.querySelectorAll(".item:not([hidden])")):[]}toggleItemSelection(t,e){e?this.state.selectedItems.add(t):this.state.selectedItems.delete(t);const s=this.grid.querySelector(`.item[data-id="${t}"]`);s&&s.classList.toggle("selected",e)}handleItemSelection(t){const e=t.checked,s=t.value;if(this.toggleItemSelection(s,e),this.updateBulkControls(),this.updateSelectAllState(),window.jvbA11y){const s=t.closest(".item"),i=s&&s.querySelector("h3")?.textContent||"item";window.jvbA11y.announce(e?`Selected ${i}`:`Deselected ${i}`)}}updateSelectAllState(){const t=this.container.querySelector(this.config.selectors.selectAll);if(!t)return;const e=this.getVisibleItems();if(0===e.length)return t.checked=!1,void(t.indeterminate=!1);const s=e.filter((t=>{const e=t.querySelector('.item-select input[type="checkbox"]');return e&&e.checked})).length;0===s?(t.checked=!1,t.indeterminate=!1):s===e.length?(t.checked=!0,t.indeterminate=!1):(t.checked=!1,t.indeterminate=!0)}updateBulkControls(){if(!this.bulkControls)return;const t=this.bulkControls.querySelector(".bulk-actions");if(!t)return;const e=this.state.selectedItems.size>0;t.hidden=!e;const s=this.bulkControls.querySelector(".selected-count");s&&(s.textContent=e?`${this.state.selectedItems.size} selected`:"")}handleKeyDown(t){"Escape"===t.key&&this.state.selectedItems.size>0&&(t.preventDefault(),this.clearSelection(),window.jvbA11y&&window.jvbA11y.announce("Selection cleared using Escape key"))}clearSelection(){this.state.selectedItems.clear(),this.getVisibleItems().forEach((t=>{const e=t.querySelector('.item-select input[type="checkbox"]');e&&(e.checked=!1),t.classList.remove("selected")}));const t=this.container.querySelector(this.config.selectors.selectAll);t&&(t.checked=!1,t.indeterminate=!1),this.updateBulkControls(),window.jvbA11y&&window.jvbA11y.announce("Selection cleared")}applyBulkAction(t){if(!t||0===this.state.selectedItems.size)return;switch(t){case"unfavourite":this.bulkUnfavourite();break;case"add-to-list":this.showAddToListModal();break;case"create-list":this.showCreateListModal();break;case"add-notes":this.showBulkNotesModal()}const e=this.container.querySelector(".bulk-action-select");e&&(e.value="")}handleItemActions(t){if(t.target.closest(".toggle-notes")){const e=t.target.closest(".toggle-notes"),s="true"===e.getAttribute("aria-expanded"),i=e.closest(".notes-section").querySelector(".notes-content");return e.setAttribute("aria-expanded",!s),i.hidden=s,!s&&i&&i.querySelector("textarea")?.focus(),void t.preventDefault()}if(t.target.closest(".save-notes")){const e=t.target.closest(".save-notes").closest(".notes-content").querySelector("textarea");return e&&this.saveNotes(e),void t.preventDefault()}if(t.target.closest(".view-list")){const e=t.target.closest(".view-list").closest(".list-card");return e&&e.dataset.id&&this.viewList(e.dataset.id),void t.preventDefault()}if(t.target.closest(".share-list")){const e=t.target.closest(".share-list").closest(".list-card");return e&&e.dataset.id&&this.showShareModal(e.dataset.id),void t.preventDefault()}if(t.target.closest(".delete-list")){const e=t.target.closest(".delete-list").closest(".list-card");return e&&e.dataset.id&&this.deleteList(e.dataset.id),void t.preventDefault()}if(t.target.closest(".back-to-lists"))return this.exitListView(),void t.preventDefault()}async loadLists(t=!0){try{this.state.loading=!0,this.loadingManager.show("Loading lists...");const t=await this.cache.fetchWithCache(`${jvbSettings.api}${this.config.endpoints.lists}`,{method:"GET",headers:{"X-WP-Nonce":window.auth.getNonce(),action_nonce:window.auth.getNonce("favourites")}},{context:"favourite-lists",forceRefresh:!1});return t.lists&&this.renderLists(t.lists),t}catch(t){throw this.handleError(t,"loading lists"),t}finally{this.state.loading=!1,this.loadingManager.hide()}}renderLists(t){if(!this.listContainer)return;if(removeChildren(this.listContainer),!t||0===t.length)return void(this.listContainer.innerHTML='\n                <div class="no-lists">\n                    <h3>No Lists Yet</h3>\n                    <p>Select favourites from the main tab to organize into lists.</p>\n                </div>\n            ');const e=t.owned,s=t.shared;if(e.length>0){const t=document.createElement("details");t.className="lists-section owned-lists",t.open=!0,t.innerHTML="<summary>Your Lists:</summary>",e.forEach((e=>{const s=this.createListCard(e);t.appendChild(s)})),this.listContainer.appendChild(t)}if(s.length>0){const t=document.createElement("details");t.className="lists-section shared-lists",t.innerHTML="<summary>Lists Shared with You:</summary>",s.forEach((e=>{const s=this.createListCard(e);t.appendChild(s)})),this.listContainer.appendChild(t)}}createListCard(t){const e=document.createElement("div");e.className="list-card",e.dataset.id=t.id;const s="1"===t.is_shared;s&&e.classList.add("shared"),t.is_temp&&e.classList.add("temp"),t.is_owner;const i=sanitizeHtml(t.name||"Untitled List"),a=sanitizeHtml(t.description||"");return e.innerHTML=`\n            <div class="list-header">\n                <h3>${i}</h3>\n                <div class="list-actions">\n                    <button type="button" class="view-list" title="View List">\n                        ${jvbSettings.icons?.show||"View"}\n                    </button>\n                    ${s?"":`\n                        <button type="button" class="share-list" title="Share List">\n                            ${jvbSettings.icons?.share||"Share"}\n                        </button>\n                        <button type="button" class="delete-list" title="Delete List">\n                            ${jvbSettings.icons?.delete||"Delete"}\n                        </button>\n                    `}\n                </div>\n            </div>\n\n            ${a?`<p class="list-description">${a}</p>`:""}\n\n            <div class="list-meta">\n                <div class="meta-stats">\n                    <span class="item-count">${t.item_count||0} items</span>\n                    <span class="date">${formatDate(t.created_at)}</span>\n                </div>\n\n\n                ${s?`\n                    <div class="owner-info">\n                        Shared by ${t.owner_name||"another user"}\n                    </div>\n                `:t.share_count>0?`\n                    <div class="share-info">\n                        Shared with ${t.share_count} ${1===t.share_count?"person":"people"}\n                    </div>\n                `:""}\n            </div>\n        `,e}async viewList(t){try{this.state.loading=!0,this.loadingManager.show("Loading list..."),this.state.currentListId=t;const e=await this.cache.fetchWithCache(`${jvbSettings.api}${this.config.endpoints.lists}?id=${t}`,{method:"GET",headers:{"X-WP-Nonce":window.auth.getNonce(),action_nonce:window.auth.getNonce("favourites")}},{context:"list-item",forceRefresh:!1});if(!e.list)throw new Error("List not found");this.showListDetails(e.list)}catch(t){this.handleError(t,"viewing list")}finally{this.state.loading=!1,this.loadingManager.hide()}}showListDetails(t){this.listDetails&&this.listContainer&&(console.log(t),this.listDetails.querySelector(".list-title").value=t.name||"Untitled List",this.listDetails.querySelector(".list-description").value=t.description||"",t.is_owner?this.listDetails.querySelector(".list-actions")||this.createListActions():this.listDetails.querySelector(".list-actions")?.remove(),removeChildren(this.grid),this.renderFavourites(t.items||[],!1),0===t.items.length&&this.showEmptyListState(),window.jvbA11y&&window.jvbA11y.announce(`Viewing list: ${t.name} with ${t.items?.length||0} items`))}createListActions(){const t=document.createElement("div");t.className="list-actions",t.innerHTML='\n            <button type="button" class="share-list" title="Share List">\n            <i class="icon icon-share-fat"></i>\n            <span>Share</span>\n        </button>\n        <button type="button" class="duplicate-list" title="Duplicate List">\n            <i class="icon icon-copy"></i>\n            <span>Duplicate</span>\n        </button>\n        <button type="button" class="delete-list" title="Delete List">\n            <i class="icon icon-trash"></i>\n            <span>Delete</span>\n        </button>\n        ',this.listDetails.insertBefore(t,this.listDetails.querySelector(".bulk-controls"))}exitListView(){this.listDetails&&this.listContainer&&(this.listDetails.hidden=!0,this.listContainer.hidden=!1,this.container.classList.remove("viewing-list"),this.state.currentListId=null,window.jvbA11y&&window.jvbA11y.announce("Returned to lists view"))}showCreateListModal(){this.createListModal&&(this.createListModal.querySelector("form")?.reset(),this.createListModal.showModal(),setTimeout((()=>{this.createListModal.querySelector("#list-name")?.focus()}),100),window.jvbA11y&&window.jvbA11y.announce("Create list dialog opened"))}showAddToListModal(){this.addToListModal&&(this.populateAddToListModal(),this.addToListModal.showModal(),window.jvbA11y&&window.jvbA11y.announce("Add to list dialog opened"))}async populateAddToListModal(){if(!this.addToListModal)return;const t=this.addToListModal.querySelector(".lists-options");if(t){removeChildren(t);try{const e=(await this.loadLists()).lists.owned;if(0===e.length)return t.innerHTML='\n                    <div class="no-lists">\n                        <p>You don\'t have any lists yet.</p>\n                        <button type="button" class="create-list-button">Create a list</button>\n                    </div>\n                ',void t.querySelector(".create-list-button")?.addEventListener("click",(()=>{this.addToListModal.close(),this.showCreateListModal()}));e.forEach((e=>{const s=document.createElement("div");s.className="list-option",s.innerHTML=`\n                    <input type="checkbox" id="${e.id}" name="list_ids[]" value="${e.id}">\n                    <label for="${e.id}">\n\n                        <span class="list-name">${sanitizeHtml(e.name)}</span>\n                        <span class="item-count">( ${e.item_count||0} items )</span>\n                    </label>\n                `,t.appendChild(s)}))}catch(e){t.innerHTML='\n                <div class="error-message">\n                    <p>Error loading lists. Please try again.</p>\n                </div>\n            ',console.error("Error loading lists for modal:",e)}}}async handleCreateList(t){const e=t.get("list_name"),s=t.get("list_description");if(e)try{this.showLoader("Creating list...");const t=[];this.state.selectedItems.forEach((e=>{const s=this.grid.querySelector(`.item[data-id="${e}"]`);s&&t.push({type:s.dataset.type,target_id:parseInt(e)})})),this.queue.addToQueue({type:"favourite_list_create",data:{name:e,description:s,items:t}}),showToast(`List "${e}" created`),this.a11y.announce(`List ${e} created with ${t.length} items`),this.createListModal.close(),this.clearSelection(),this.switchTab("lists")}catch(t){this.handleError(t,"creating list")}finally{this.hideLoader()}else showToast("Please enter a list name","error")}async handleAddToList(t){const e=t.getAll("list_ids[]");if(e.length)try{this.showLoader("Adding to list...");const t=[];this.state.selectedItems.forEach((e=>{const s=this.grid.querySelector(`.item[data-id="${e}"]`);s&&t.push({type:s.dataset.type,target_id:parseInt(e)})})),this.queue.addToQueue({type:"favourite_list_add",data:{list_id:e.join(","),items:t}}),showToast(`Added to ${e.length} ${1===e.length?"list":"lists"}`),this.a11y.announce(`Added ${t.length} items to ${e.length} ${1===e.length?"list":"lists"}`),this.addToListModal.close(),this.clearSelection()}catch(t){this.handleError(t,"adding to list")}finally{this.hideLoader()}else showToast("Please select at least one list","error")}async handleRemoveFromList(t){const e=t.getAll("list_ids[]");if(e.length)try{this.showLoader("Removing from list...");const t=[];this.state.selectedItems.forEach((e=>{const s=this.grid.querySelector(`.item[data-id="${e}"]`);s&&t.push({type:s.dataset.type,target_id:parseInt(e)})})),this.queue.addToQueue({type:"favourite_list_remove",data:{list_id:e.join(","),items:t}}),showToast(`Removed from ${e.length} ${1===e.length?"list":"lists"}`),this.a11y.announce(`Removed ${t.length} items to ${e.length} ${1===e.length?"list":"lists"}`),this.addToListModal.close(),this.clearSelection()}catch(t){this.handleError(t,"remove from list")}finally{this.hideLoader()}else showToast("Please select at least one list","error")}showShareModal(t){this.shareListModal&&(this.state.currentListId=t,this.shareListModal.querySelector("form")?.reset(),this.loadSharedUsers(t),this.shareListModal.showModal(),setTimeout((()=>{this.shareListModal.querySelector("#share-email")?.focus()}),100),window.jvbA11y&&window.jvbA11y.announce("Share list dialog opened"))}async loadSharedUsers(t){try{const e=this.shareListModal.querySelector(".shared-users");if(!e)return;e.innerHTML='<div class="loading">Loading shared users...</div>';const s=await this.cache.fetchWithCache(`${jvbSettings.api}${this.config.endpoints.lists}?id=${t}`,{method:"GET",headers:{"X-WP-Nonce":window.auth.getNonce(),action_nonce:window.auth.getNonce("favourites")}},{context:"list-item",forceRefresh:!1});removeChildren(e),s.list&&s.list.shared_users&&s.list.shared_users.length>0?(s.list.shared_users.forEach((t=>{const s=document.createElement("div");s.className=`shared-user ${t.status}`,s.innerHTML=`\n                        <span class="user-email">${t.email}</span>\n                        ${"pending"===t.status?'<span class="pending-badge">Invitation sent</span>':`<span class="permission-badge">${t.permission_type||"view"}</span>`}\n                        <button type="button" class="remove-share" data-email="${t.email}">\n                            ${jvbSettings.icons?.delete||"Remove"}\n                        </button>\n                    `,e.appendChild(s)})),e.querySelectorAll(".remove-share").forEach((t=>{t.addEventListener("click",(()=>{this.unshareList(t.dataset.email)}))}))):e.innerHTML='<div class="no-shares">This list is not shared with anyone yet.</div>'}catch(t){console.error("Error loading shared users:",t)}}async unshareList(t){if(confirm(`Remove ${t}'s access to this list?`))if(this.state.currentListId)try{this.showLoader("Removing access..."),this.queue.addToQueue({type:"favourite_list_unshare",data:{list_id:parseInt(this.state.currentListId),email:t}});const e=Array.from(this.shareListModal.querySelectorAll(".shared-user")).find((e=>e.querySelector(".user-email")?.textContent===t));e&&(e.classList.add("removing"),setTimeout((()=>{if(e.remove(),0===this.shareListModal.querySelectorAll(".shared-user").length){const t=this.shareListModal.querySelector(".shared-users");t&&(t.innerHTML='<div class="no-shares">This list is not shared with anyone yet.</div>')}}),300)),showToast(`Removed ${t}'s access`),this.a11y.announce(`Removed ${t}'s access to list`)}catch(t){this.handleError(t,"removing share access")}finally{this.hideLoader()}else showToast("No list selected","error")}async deleteList(t){if(confirm("Are you sure you want to delete this list? This cannot be undone."))try{this.showLoader("Deleting list..."),this.queue.addToQueue({type:"favourite_list_delete",data:{list_id:parseInt(t)}});const e=this.container.querySelector(`.list-card[data-id="${t}"]`);e&&(e.classList.add("removing"),setTimeout((()=>{e.remove(),0===this.container.querySelectorAll(".list-card").length&&(this.listContainer.innerHTML='\n                                <div class="no-lists">\n                                    <h3>No Lists Yet</h3>\n                                    <p>Create your first list to organize your favourites!</p>\n                                </div>\n                            ')}),300)),showToast("List deleted"),this.a11y.announce("List deleted")}catch(t){this.handleError(t,"deleting list")}finally{this.hideLoader()}}showBulkNotesModal(){let t=document.querySelector(".bulk-notes-modal");t||(t=document.createElement("dialog"),t.className="bulk-notes-modal",t.innerHTML='\n                <form method="dialog" data-save="favourites">\n                    <h2>Add Notes to Selected Items</h2>\n\n                    <div class="field">\n                        <label for="bulk-notes">Notes (will be applied to all selected items)</label>\n                        <textarea id="bulk-notes" name="bulk_notes" rows="5"></textarea>\n                    </div>\n\n                    <div class="actions">\n                        <button type="button" class="cancel">Cancel</button>\n                        <button type="submit" class="save">Save Notes</button>\n                    </div>\n                </form>\n            ',document.body.appendChild(t),t.querySelector("form").addEventListener("submit",(e=>{e.preventDefault();const s=t.querySelector("#bulk-notes").value;this.saveBulkNotes(s),t.close()})),t.querySelector(".cancel").addEventListener("click",(()=>{t.close()}))),t.querySelector("form")?.reset(),t.showModal(),setTimeout((()=>{t.querySelector("#bulk-notes")?.focus()}),100),window.jvbA11y&&window.jvbA11y.announce("Add notes dialog opened")}saveBulkNotes(t){if(t)try{this.showLoader("Saving notes...");let e=Array.from(this.state.selectedItems.values());this.queue.addToQueue({type:"favourite_notes",data:{target_id:e.join(","),notes:t}}),showToast(`Notes saved for ${e.length} items`),this.a11y.announce(`Notes saved for ${e.length} items`),this.clearSelection()}catch(t){this.handleError(t,"saving bulk notes")}finally{this.hideLoader()}}async bulkUnfavourite(){if(confirm("Are you sure you want to remove these items from your favourites?"))try{this.showLoader("Removing from favourites...");const t=[];this.state.selectedItems.forEach((e=>{const s=this.grid.querySelector(`.item[data-id="${e}"]`);if(!s)return;const i=s.dataset.type;t.push({target_id:parseInt(e),type:i,action:"remove"})})),this.queue.addToQueue({type:"favourite_toggle",data:t});const e=[];this.state.selectedItems.forEach((t=>{const s=this.grid.querySelector(`.item[data-id="${t}"]`);if(!s)return;s.style.opacity="0",s.style.transform="scale(0.9)",s.style.transition="opacity 0.3s ease, transform 0.3s ease";const i=new Promise((t=>{setTimeout((()=>{s.remove(),t()}),300)}));e.push(i)})),await Promise.all(e),0===this.grid.children.length&&this.showEmptyState(),this.clearSelection(),showToast(`Removed ${t.length} items from favourites`),this.a11y.announce(`Removed ${t.length} items from favourites`)}catch(t){this.handleError(t,"removing favourites")}finally{this.hideLoader()}}async handleShareList(t){const e=t.get("share_email");if(e)if(this.validateEmail(e))try{this.showLoader("Sharing list..."),this.queue.addToQueue({type:"favourite_list_share",data:{list_id:parseInt(this.state.currentListId),email:e,permission_type:"view"}}),this.shareListModal.querySelector("#share-email").value="",this.loadSharedUsers(this.state.currentListId),showToast(`Invitation sent to ${e}`),this.a11y.announce(`Invitation sent to ${e}`)}catch(t){this.handleError(t,"sharing list")}finally{this.hideLoader()}else showToast("Please enter a valid email address","error");else showToast("Please enter an email address","error")}showLoader(t="Loading..."){if(!this.loader)return;const e=this.loader.querySelector(".loader-message");e&&(e.textContent=t),this.loader.hidden=!1}hideLoader(){this.loader&&(this.loader.hidden=!0)}showToast(t,e){window.jvbNotifications.showToast(t,e)}handleError(t,e){console.error(`Favourites error (${e}):`,t),showToast(`Error ${e}: ${t.message||"Something went wrong"}`,"error"),window.jvbError&&window.jvbError.log(t,{component:"FavouritesManager",action:e}),window.jvbA11y&&window.jvbA11y.announce(`Error ${e}. ${t.message||"Please try again."}`)}validateEmail(t){return/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(t)}};
window.favouritesManager=class{constructor(){this.queue=window.jvbQueue,this.loadingManager=window.jvbLoading,this.cache=window.jvbCache,this.a11y=window.jvbA11y,this.error=window.jvbError,this.tabs=new window.jvbTabs(document.querySelector(".replace")),this.config={endpoints:{favourites:"favourites",lists:"favourites/lists",shares:"favourites/lists/shares"},selectors:{container:".favourites.container",itemsTab:'.tab-content[data-tab="items"]',listsTab:'.tab-content[data-tab="lists"]',grid:".item-grid",typeFilters:".type-filters",viewControls:".view-controls",bulkControls:".bulk-controls",selectAll:"#select-all",createListModal:".create-list-modal",addToListModal:".add-to-list-modal",shareListModal:".share-list-modal",noItems:".no-favourites",listContainer:".lists-container",listDetails:".list-details",loader:".favourites-loader"},defaultPage:1,defaultPerPage:24,defaultViewMode:"grid",refreshInterval:6e4,toastDuration:3e3},document.addEventListener("keydown",this.handleKeyDown.bind(this)),this.state={selectedItems:new Set,page:this.config.defaultPage,filter:{type:"all",order:"desc",orderBy:"date_added"},view:{mode:localStorage.getItem("favourites_view")||this.config.defaultViewMode,activeTab:"items"},pagination:{hasMore:!1,totalItems:0,totalPages:0},currentListId:null,loading:!1,initialized:!1},this.initDom(),this.initEvents(),this.loadInitialData(),this.state.initialized=!0}initDom(){this.container=document.querySelector(this.config.selectors.container),this.container?(this.grid=this.container.querySelector(this.config.selectors.grid),this.typeFilters=this.container.querySelector(this.config.selectors.typeFilters),this.viewControls=this.container.querySelector(this.config.selectors.viewControls),this.bulkControls=this.container.querySelector(this.config.selectors.bulkControls),this.listContainer=this.container.querySelector(this.config.selectors.listContainer),this.listDetails=this.container.querySelector(this.config.selectors.listDetails),this.loader=this.container.querySelector(this.config.selectors.loader),this.createListModal=document.querySelector(this.config.selectors.createListModal),this.addToListModal=document.querySelector(this.config.selectors.addToListModal),this.shareListModal=document.querySelector(this.config.selectors.shareListModal),this.grid&&this.state.view.mode&&this.grid.classList.add(`${this.state.view.mode}-view`)):console.warn("Favourites container not found")}initEvents(){if(this.typeFilters&&this.typeFilters.addEventListener("click",(t=>{const e=t.target.closest(".type-filter");e&&this.setFilterType(e.dataset.type)})),this.viewControls&&this.viewControls.addEventListener("click",(t=>{const e=t.target.closest(".view-toggle");e&&this.setView(e.dataset.view)})),this.container){const t=this.container.querySelector(this.config.selectors.selectAll);t&&t.addEventListener("change",(()=>{this.toggleSelectAll(t.checked)})),this.container.addEventListener("change",(t=>{t.target.matches(".item-select input[type=checkbox]")&&this.handleItemSelection(t.target)}));const e=this.container.querySelector(".bulk-action-select"),s=this.container.querySelector(".apply-bulk");e&&s&&s.addEventListener("click",(()=>{this.applyBulkAction(e.value)}));const i=this.container.querySelector(".cancel-bulk");i&&i.addEventListener("click",(()=>{this.clearSelection()}))}this.initModalEvents(),this.container.addEventListener("click",this.handleItemActions.bind(this)),this.grid&&this.setupInfiniteScroll()}initModalEvents(){if(this.createListModal){const t=this.createListModal.querySelector("form");t&&t.addEventListener("submit",(e=>{e.preventDefault(),this.handleCreateList(new FormData(t))}));const e=this.createListModal.querySelector(".cancel");e&&e.addEventListener("click",(()=>{this.createListModal.close()}))}if(this.addToListModal){const t=this.addToListModal.querySelector("form");t&&t.addEventListener("submit",(e=>{e.preventDefault(),this.handleAddToList(new FormData(t))}));const e=this.addToListModal.querySelector(".cancel");e&&e.addEventListener("click",(()=>{this.addToListModal.close()}))}if(this.shareListModal){const t=this.shareListModal.querySelector("form");t&&t.addEventListener("submit",(e=>{e.preventDefault(),this.handleShareList(new FormData(t))}));const e=this.shareListModal.querySelector(".cancel");e&&e.addEventListener("click",(()=>{this.shareListModal.close()}));const s=this.shareListModal.querySelector(".add-email");s&&s.addEventListener("click",(()=>{const e=this.shareListModal.querySelector("#share-email");e&&e.value&&this.handleShareList(new FormData(t))}))}}setupInfiniteScroll(){let t=this.container.querySelector(".scroll-sentinel");t||(t=document.createElement("div"),t.className="scroll-sentinel",t.setAttribute("aria-hidden","true"),this.grid.parentNode.appendChild(t)),new IntersectionObserver((t=>{t.forEach((t=>{t.isIntersecting&&this.state.pagination.hasMore&&!this.state.loading&&(this.state.page++,this.loadFavourites())}))}),{rootMargin:"200px"}).observe(t)}async loadInitialData(){this.loadingManager.show();try{await this.loadFavourites(),this.loadLists().catch((t=>{console.error("Error loading lists:",t)}))}catch(t){this.handleError(t,"loading initial data")}finally{this.loadingManager.hide()}}async loadFavourites(t=!0){if(!this.state.loading)try{this.state.loading=!0,this.loadingManager.show();const e=new URLSearchParams({page:this.state.page,per_page:this.config.defaultPerPage,type:"all"!==this.state.filter.type?this.state.filter.type:"",order:this.state.filter.order,orderby:this.state.filter.orderBy});t&&(this.state.page=1,removeChildren(this.grid),this.grid.classList.remove("empty"));const s=await this.cache.fetchWithCache(`${jvbSettings.api}${this.config.endpoints.favourites}?${e}`,{method:"GET",headers:{"X-WP-Nonce":window.auth.getNonce(),"X-Action-Nonce":window.auth.getNonce("favourites")}},{context:"favouritesManager",forceRefresh:!0});return this.renderFavourites(s.favourites||[],this.state.page>1),s.counts&&this.updateTypeFilters(s.counts),s.pagination&&(this.state.pagination={hasMore:s.pagination.has_more,totalItems:s.pagination.total_items,totalPages:s.pagination.total_pages}),s}catch(t){throw this.handleError(t,"loading favourites"),t}finally{this.state.loading=!1,this.loadingManager.hide()}}renderFavourites(t,e=!1){this.grid&&(0!==t.length||e?(this.hideEmptyState(),e||removeChildren(this.grid),t.forEach((t=>{const e=this.createItemElement(t);this.grid.appendChild(e),this.initItemFunctionality(e,t)})),window.jvbA11y&&window.jvbA11y.announce(`${e?"Added":"Loaded"} ${t.length} favourites`)):this.showEmptyState())}createItemElement(t){const e=document.createElement("div");e.className=`item ${t.type} favourited`,e.dataset.id=t.target_id,e.dataset.type=t.type;const s=sanitizeHtml(t.title||!1),i=sanitizeHtml(t.notes||"");let a="";return t.thumbnail&&(a=`\n                <div class="item-thumbnail">\n                    <a href="${t.url}">${t.thumbnail}</a>\n                </div>\n            `),e.innerHTML=`\n            <div class="item-select">\n                <input type="checkbox"\n                   class="favourite-checkbox"\n                    id="select-${t.target_id}"\n                    value="${t.target_id}">\n                <label for="select-${t.target_id}"><span class="screen-reader-text">Select this ${t.type}</span</label>\n            </div>\n\n            <button type="button" class="favourite-button favourited"\n                onclick="toggleFavourite(this)"\n                data-id="${t.target_id}"\n                data-type="${t.type}"\n                title="Remove from favourites">\n                ${jvbSettings.icons["heart-filled"]}\n            </button>\n\n            ${a}\n\n            <div class="item-info">\n                ${s?`<h3><a href="${t.url}">${s}</a></h3>`:`<a href="${t.url}">View Item</a>`}\n\n                ${t.author?`\n                <div class="item-artist">\n                    <span>By ${t.author.name}</span>\n                </div>`:""}\n\n                ${t.taxonomies?.length?`\n                <div class="taxonomy-lists">\n                    ${t.taxonomies.map((t=>`\n                        <div class="taxonomy-group">\n                            ${jvbSettings.icons[t.icon]}\n                            <ul>\n                                ${t.terms.slice(0,3).map((t=>`\n                                    <li>\n                                        <a href="${t.url}" ${t.umami_click}>\n                                            ${t.title}\n                                        </a>\n                                    </li>\n                                `)).join("")}\n                            </ul>\n                        </div>\n                    `)).join("")}\n                </div>\n            `:""}\n\n                <div class="notes-section">\n                    <button type="button" class="toggle-notes" aria-expanded="false">\n                        ${jvbSettings.icons.note||"Notes"}\n                        <span>Notes</span>\n                    </button>\n\n                    <div class="notes-content" hidden>\n                        <textarea class="notes-input"\n                            placeholder="Add notes about this item..."\n                            data-id="${t.target_id}"\n                            data-type="${t.type}">${i}</textarea>\n                        <button type="button" class="save-notes">Save Notes</button>\n                    </div>\n                </div>\n            </div>\n        `,e}initItemFunctionality(t,e){const s=t.querySelector(".toggle-notes"),i=t.querySelector(".notes-content");s&&i&&s.addEventListener("click",(()=>{const t="true"===s.ariaExpanded;s.ariaExpanded=!t.toString(),i.hidden=t,t||i.querySelector("textarea")?.focus()}));const a=t.querySelector(".save-notes"),n=t.querySelector(".notes-input");a&&n&&(a.addEventListener("click",(()=>{this.saveNotes(n)})),n.addEventListener("keydown",(t=>{"Enter"===t.key&&(t.ctrlKey||t.metaKey)&&(t.preventDefault(),this.saveNotes(n))})))}saveNotes(t){if(!t)return;const e=t.value.trim(),s=t.dataset.id,i=t.dataset.type;s&&i&&(this.queue.addToQueue({type:"favourite_notes",data:{type:i,target_id:parseInt(s),notes:e}}),showToast("Notes saved"),this.a11y.announce("Notes saved"))}showEmptyState(t=!1){const e=this.container.querySelector(this.config.selectors.noItems)??this.createEmptyElement;e&&(e.hidden=!1),this.grid&&this.grid.classList.add("empty"),this.a11y.announce("No favourites to show!")}hideEmptyState(){const t=this.container.querySelector(".no-favourites");t&&t.remove(),this.grid&&this.grid.classList.remove("empty")}createEmptyElement(t=!1){const e=document.createElement("div");e.className="no-favourites",e.innerHTML="\n            <h3>♡ BLANK CANVAS â™¡</h3>\n            <p>You haven't fallen in love with any pieces... yet!</p>\n            <p>Hit that heart icon when something stops your scroll.</p>\n            <p>Your dream collection is waiting to start.</p>\n        ",this.grid.after(e)}showEmptyListState(t=!1){const e=document.createElement("div");e.className="no-favourites",e.innerHTML="\n            <h3>♡ FULL OF POSSIBILITY â™¡</h3>\n            <p>There's nothing in this list yet.</p>\n            <p>Add some gap fillers from the main favourites tab.</p>\n        ",this.grid.after(e),this.grid.classList.add("empty"),this.a11y.announce("No favourites to show!")}async loadMoreItems(){!this.state.loading&&this.state.pagination.hasMore&&(this.state.page+=1,await this.loadFavourites())}updateTypeFilters(t){this.typeFilters&&this.typeFilters.querySelectorAll(".type-filter").forEach((e=>{const s=e.querySelector(".count");if(!s)return;const i=e.dataset.type;if("all"===i){const e=Object.values(t).reduce(((t,e)=>t+(parseInt(e)||0)),0);s.textContent=`(${e})`}else s.textContent=`(${t[i]||0})`}))}setFilterType(t){t!==this.state.filter.type&&(this.typeFilters&&this.typeFilters.querySelectorAll(".type-filter").forEach((e=>{e.classList.toggle("active",e.dataset.type===t),e.setAttribute("aria-selected",e.dataset.type===t)})),this.state.filter.type=t,this.state.page=1,this.loadFavourites(),window.jvbA11y&&window.jvbA11y.announce(`Filtered to show ${"all"===t?"all":t} items`))}setView(t){t!==this.state.view.mode&&(this.viewControls&&this.viewControls.querySelectorAll(".view-toggle").forEach((e=>{const s=e.dataset.view===t;e.setAttribute("aria-pressed",s)})),this.grid&&(this.grid.classList.remove("grid-view","list-view"),this.grid.classList.add(`${t}-view`)),this.state.view.mode=t,localStorage.setItem("favourites_view",t),window.jvbA11y&&window.jvbA11y.announce(`Changed to ${t} view`))}toggleSelectAll(t){const e=this.getVisibleItems();e.forEach((e=>{const s=e.querySelector('.item-select input[type="checkbox"]');s&&(s.checked=t,this.toggleItemSelection(s.value,t))})),this.updateBulkControls(),window.jvbA11y&&window.jvbA11y.announce(t?`Selected all ${e.length} items`:"Deselected all items")}getVisibleItems(){return this.grid?Array.from(this.grid.querySelectorAll(".item:not([hidden])")):[]}toggleItemSelection(t,e){e?this.state.selectedItems.add(t):this.state.selectedItems.delete(t);const s=this.grid.querySelector(`.item[data-id="${t}"]`);s&&s.classList.toggle("selected",e)}handleItemSelection(t){const e=t.checked,s=t.value;if(this.toggleItemSelection(s,e),this.updateBulkControls(),this.updateSelectAllState(),window.jvbA11y){const s=t.closest(".item"),i=s&&s.querySelector("h3")?.textContent||"item";window.jvbA11y.announce(e?`Selected ${i}`:`Deselected ${i}`)}}updateSelectAllState(){const t=this.container.querySelector(this.config.selectors.selectAll);if(!t)return;const e=this.getVisibleItems();if(0===e.length)return t.checked=!1,void(t.indeterminate=!1);const s=e.filter((t=>{const e=t.querySelector('.item-select input[type="checkbox"]');return e&&e.checked})).length;0===s?(t.checked=!1,t.indeterminate=!1):s===e.length?(t.checked=!0,t.indeterminate=!1):(t.checked=!1,t.indeterminate=!0)}updateBulkControls(){if(!this.bulkControls)return;const t=this.bulkControls.querySelector(".bulk-actions");if(!t)return;const e=this.state.selectedItems.size>0;t.hidden=!e;const s=this.bulkControls.querySelector(".selected-count");s&&(s.textContent=e?`${this.state.selectedItems.size} selected`:"")}handleKeyDown(t){"Escape"===t.key&&this.state.selectedItems.size>0&&(t.preventDefault(),this.clearSelection(),window.jvbA11y&&window.jvbA11y.announce("Selection cleared using Escape key"))}clearSelection(){this.state.selectedItems.clear(),this.getVisibleItems().forEach((t=>{const e=t.querySelector('.item-select input[type="checkbox"]');e&&(e.checked=!1),t.classList.remove("selected")}));const t=this.container.querySelector(this.config.selectors.selectAll);t&&(t.checked=!1,t.indeterminate=!1),this.updateBulkControls(),window.jvbA11y&&window.jvbA11y.announce("Selection cleared")}applyBulkAction(t){if(!t||0===this.state.selectedItems.size)return;switch(t){case"unfavourite":this.bulkUnfavourite();break;case"add-to-list":this.showAddToListModal();break;case"create-list":this.showCreateListModal();break;case"add-notes":this.showBulkNotesModal()}const e=this.container.querySelector(".bulk-action-select");e&&(e.value="")}handleItemActions(t){if(t.target.closest(".toggle-notes")){const e=t.target.closest(".toggle-notes"),s="true"===e.getAttribute("aria-expanded"),i=e.closest(".notes-section").querySelector(".notes-content");return e.setAttribute("aria-expanded",!s),i.hidden=s,!s&&i&&i.querySelector("textarea")?.focus(),void t.preventDefault()}if(t.target.closest(".save-notes")){const e=t.target.closest(".save-notes").closest(".notes-content").querySelector("textarea");return e&&this.saveNotes(e),void t.preventDefault()}if(t.target.closest(".view-list")){const e=t.target.closest(".view-list").closest(".list-card");return e&&e.dataset.id&&this.viewList(e.dataset.id),void t.preventDefault()}if(t.target.closest(".share-list")){const e=t.target.closest(".share-list").closest(".list-card");return e&&e.dataset.id&&this.showShareModal(e.dataset.id),void t.preventDefault()}if(t.target.closest(".delete-list")){const e=t.target.closest(".delete-list").closest(".list-card");return e&&e.dataset.id&&this.deleteList(e.dataset.id),void t.preventDefault()}if(t.target.closest(".back-to-lists"))return this.exitListView(),void t.preventDefault()}async loadLists(t=!0){try{this.state.loading=!0,this.loadingManager.show("Loading lists...");const t=await this.cache.fetchWithCache(`${jvbSettings.api}${this.config.endpoints.lists}`,{method:"GET",headers:{"X-WP-Nonce":window.auth.getNonce(),"X-Action-Nonce":window.auth.getNonce("favourites")}},{context:"favourite-lists",forceRefresh:!1});return t.lists&&this.renderLists(t.lists),t}catch(t){throw this.handleError(t,"loading lists"),t}finally{this.state.loading=!1,this.loadingManager.hide()}}renderLists(t){if(!this.listContainer)return;if(removeChildren(this.listContainer),!t||0===t.length)return void(this.listContainer.innerHTML='\n                <div class="no-lists">\n                    <h3>No Lists Yet</h3>\n                    <p>Select favourites from the main tab to organize into lists.</p>\n                </div>\n            ');const e=t.owned,s=t.shared;if(e.length>0){const t=document.createElement("details");t.className="lists-section owned-lists",t.open=!0,t.innerHTML="<summary>Your Lists:</summary>",e.forEach((e=>{const s=this.createListCard(e);t.appendChild(s)})),this.listContainer.appendChild(t)}if(s.length>0){const t=document.createElement("details");t.className="lists-section shared-lists",t.innerHTML="<summary>Lists Shared with You:</summary>",s.forEach((e=>{const s=this.createListCard(e);t.appendChild(s)})),this.listContainer.appendChild(t)}}createListCard(t){const e=document.createElement("div");e.className="list-card",e.dataset.id=t.id;const s="1"===t.is_shared;s&&e.classList.add("shared"),t.is_temp&&e.classList.add("temp"),t.is_owner;const i=sanitizeHtml(t.name||"Untitled List"),a=sanitizeHtml(t.description||"");return e.innerHTML=`\n            <div class="list-header">\n                <h3>${i}</h3>\n                <div class="list-actions">\n                    <button type="button" class="view-list" title="View List">\n                        ${jvbSettings.icons?.show||"View"}\n                    </button>\n                    ${s?"":`\n                        <button type="button" class="share-list" title="Share List">\n                            ${jvbSettings.icons?.share||"Share"}\n                        </button>\n                        <button type="button" class="delete-list" title="Delete List">\n                            ${jvbSettings.icons?.delete||"Delete"}\n                        </button>\n                    `}\n                </div>\n            </div>\n\n            ${a?`<p class="list-description">${a}</p>`:""}\n\n            <div class="list-meta">\n                <div class="meta-stats">\n                    <span class="item-count">${t.item_count||0} items</span>\n                    <span class="date">${formatDate(t.created_at)}</span>\n                </div>\n\n\n                ${s?`\n                    <div class="owner-info">\n                        Shared by ${t.owner_name||"another user"}\n                    </div>\n                `:t.share_count>0?`\n                    <div class="share-info">\n                        Shared with ${t.share_count} ${1===t.share_count?"person":"people"}\n                    </div>\n                `:""}\n            </div>\n        `,e}async viewList(t){try{this.state.loading=!0,this.loadingManager.show("Loading list..."),this.state.currentListId=t;const e=await this.cache.fetchWithCache(`${jvbSettings.api}${this.config.endpoints.lists}?id=${t}`,{method:"GET",headers:{"X-WP-Nonce":window.auth.getNonce(),"X-Action-Nonce":window.auth.getNonce("favourites")}},{context:"list-item",forceRefresh:!1});if(!e.list)throw new Error("List not found");this.showListDetails(e.list)}catch(t){this.handleError(t,"viewing list")}finally{this.state.loading=!1,this.loadingManager.hide()}}showListDetails(t){this.listDetails&&this.listContainer&&(console.log(t),this.listDetails.querySelector(".list-title").value=t.name||"Untitled List",this.listDetails.querySelector(".list-description").value=t.description||"",t.is_owner?this.listDetails.querySelector(".list-actions")||this.createListActions():this.listDetails.querySelector(".list-actions")?.remove(),removeChildren(this.grid),this.renderFavourites(t.items||[],!1),0===t.items.length&&this.showEmptyListState(),window.jvbA11y&&window.jvbA11y.announce(`Viewing list: ${t.name} with ${t.items?.length||0} items`))}createListActions(){const t=document.createElement("div");t.className="list-actions",t.innerHTML='\n            <button type="button" class="share-list" title="Share List">\n            <i class="icon icon-share-fat"></i>\n            <span>Share</span>\n        </button>\n        <button type="button" class="duplicate-list" title="Duplicate List">\n            <i class="icon icon-copy"></i>\n            <span>Duplicate</span>\n        </button>\n        <button type="button" class="delete-list" title="Delete List">\n            <i class="icon icon-trash"></i>\n            <span>Delete</span>\n        </button>\n        ',this.listDetails.insertBefore(t,this.listDetails.querySelector(".bulk-controls"))}exitListView(){this.listDetails&&this.listContainer&&(this.listDetails.hidden=!0,this.listContainer.hidden=!1,this.container.classList.remove("viewing-list"),this.state.currentListId=null,window.jvbA11y&&window.jvbA11y.announce("Returned to lists view"))}showCreateListModal(){this.createListModal&&(this.createListModal.querySelector("form")?.reset(),this.createListModal.showModal(),setTimeout((()=>{this.createListModal.querySelector("#list-name")?.focus()}),100),window.jvbA11y&&window.jvbA11y.announce("Create list dialog opened"))}showAddToListModal(){this.addToListModal&&(this.populateAddToListModal(),this.addToListModal.showModal(),window.jvbA11y&&window.jvbA11y.announce("Add to list dialog opened"))}async populateAddToListModal(){if(!this.addToListModal)return;const t=this.addToListModal.querySelector(".lists-options");if(t){removeChildren(t);try{const e=(await this.loadLists()).lists.owned;if(0===e.length)return t.innerHTML='\n                    <div class="no-lists">\n                        <p>You don\'t have any lists yet.</p>\n                        <button type="button" class="create-list-button">Create a list</button>\n                    </div>\n                ',void t.querySelector(".create-list-button")?.addEventListener("click",(()=>{this.addToListModal.close(),this.showCreateListModal()}));e.forEach((e=>{const s=document.createElement("div");s.className="list-option",s.innerHTML=`\n                    <input type="checkbox" id="${e.id}" name="list_ids[]" value="${e.id}">\n                    <label for="${e.id}">\n\n                        <span class="list-name">${sanitizeHtml(e.name)}</span>\n                        <span class="item-count">( ${e.item_count||0} items )</span>\n                    </label>\n                `,t.appendChild(s)}))}catch(e){t.innerHTML='\n                <div class="error-message">\n                    <p>Error loading lists. Please try again.</p>\n                </div>\n            ',console.error("Error loading lists for modal:",e)}}}async handleCreateList(t){const e=t.get("list_name"),s=t.get("list_description");if(e)try{this.showLoader("Creating list...");const t=[];this.state.selectedItems.forEach((e=>{const s=this.grid.querySelector(`.item[data-id="${e}"]`);s&&t.push({type:s.dataset.type,target_id:parseInt(e)})})),this.queue.addToQueue({type:"favourite_list_create",data:{name:e,description:s,items:t}}),showToast(`List "${e}" created`),this.a11y.announce(`List ${e} created with ${t.length} items`),this.createListModal.close(),this.clearSelection(),this.switchTab("lists")}catch(t){this.handleError(t,"creating list")}finally{this.hideLoader()}else showToast("Please enter a list name","error")}async handleAddToList(t){const e=t.getAll("list_ids[]");if(e.length)try{this.showLoader("Adding to list...");const t=[];this.state.selectedItems.forEach((e=>{const s=this.grid.querySelector(`.item[data-id="${e}"]`);s&&t.push({type:s.dataset.type,target_id:parseInt(e)})})),this.queue.addToQueue({type:"favourite_list_add",data:{list_id:e.join(","),items:t}}),showToast(`Added to ${e.length} ${1===e.length?"list":"lists"}`),this.a11y.announce(`Added ${t.length} items to ${e.length} ${1===e.length?"list":"lists"}`),this.addToListModal.close(),this.clearSelection()}catch(t){this.handleError(t,"adding to list")}finally{this.hideLoader()}else showToast("Please select at least one list","error")}async handleRemoveFromList(t){const e=t.getAll("list_ids[]");if(e.length)try{this.showLoader("Removing from list...");const t=[];this.state.selectedItems.forEach((e=>{const s=this.grid.querySelector(`.item[data-id="${e}"]`);s&&t.push({type:s.dataset.type,target_id:parseInt(e)})})),this.queue.addToQueue({type:"favourite_list_remove",data:{list_id:e.join(","),items:t}}),showToast(`Removed from ${e.length} ${1===e.length?"list":"lists"}`),this.a11y.announce(`Removed ${t.length} items to ${e.length} ${1===e.length?"list":"lists"}`),this.addToListModal.close(),this.clearSelection()}catch(t){this.handleError(t,"remove from list")}finally{this.hideLoader()}else showToast("Please select at least one list","error")}showShareModal(t){this.shareListModal&&(this.state.currentListId=t,this.shareListModal.querySelector("form")?.reset(),this.loadSharedUsers(t),this.shareListModal.showModal(),setTimeout((()=>{this.shareListModal.querySelector("#share-email")?.focus()}),100),window.jvbA11y&&window.jvbA11y.announce("Share list dialog opened"))}async loadSharedUsers(t){try{const e=this.shareListModal.querySelector(".shared-users");if(!e)return;e.innerHTML='<div class="loading">Loading shared users...</div>';const s=await this.cache.fetchWithCache(`${jvbSettings.api}${this.config.endpoints.lists}?id=${t}`,{method:"GET",headers:{"X-WP-Nonce":window.auth.getNonce(),"X-Action-Nonce":window.auth.getNonce("favourites")}},{context:"list-item",forceRefresh:!1});removeChildren(e),s.list&&s.list.shared_users&&s.list.shared_users.length>0?(s.list.shared_users.forEach((t=>{const s=document.createElement("div");s.className=`shared-user ${t.status}`,s.innerHTML=`\n                        <span class="user-email">${t.email}</span>\n                        ${"pending"===t.status?'<span class="pending-badge">Invitation sent</span>':`<span class="permission-badge">${t.permission_type||"view"}</span>`}\n                        <button type="button" class="remove-share" data-email="${t.email}">\n                            ${jvbSettings.icons?.delete||"Remove"}\n                        </button>\n                    `,e.appendChild(s)})),e.querySelectorAll(".remove-share").forEach((t=>{t.addEventListener("click",(()=>{this.unshareList(t.dataset.email)}))}))):e.innerHTML='<div class="no-shares">This list is not shared with anyone yet.</div>'}catch(t){console.error("Error loading shared users:",t)}}async unshareList(t){if(confirm(`Remove ${t}'s access to this list?`))if(this.state.currentListId)try{this.showLoader("Removing access..."),this.queue.addToQueue({type:"favourite_list_unshare",data:{list_id:parseInt(this.state.currentListId),email:t}});const e=Array.from(this.shareListModal.querySelectorAll(".shared-user")).find((e=>e.querySelector(".user-email")?.textContent===t));e&&(e.classList.add("removing"),setTimeout((()=>{if(e.remove(),0===this.shareListModal.querySelectorAll(".shared-user").length){const t=this.shareListModal.querySelector(".shared-users");t&&(t.innerHTML='<div class="no-shares">This list is not shared with anyone yet.</div>')}}),300)),showToast(`Removed ${t}'s access`),this.a11y.announce(`Removed ${t}'s access to list`)}catch(t){this.handleError(t,"removing share access")}finally{this.hideLoader()}else showToast("No list selected","error")}async deleteList(t){if(confirm("Are you sure you want to delete this list? This cannot be undone."))try{this.showLoader("Deleting list..."),this.queue.addToQueue({type:"favourite_list_delete",data:{list_id:parseInt(t)}});const e=this.container.querySelector(`.list-card[data-id="${t}"]`);e&&(e.classList.add("removing"),setTimeout((()=>{e.remove(),0===this.container.querySelectorAll(".list-card").length&&(this.listContainer.innerHTML='\n                                <div class="no-lists">\n                                    <h3>No Lists Yet</h3>\n                                    <p>Create your first list to organize your favourites!</p>\n                                </div>\n                            ')}),300)),showToast("List deleted"),this.a11y.announce("List deleted")}catch(t){this.handleError(t,"deleting list")}finally{this.hideLoader()}}showBulkNotesModal(){let t=document.querySelector(".bulk-notes-modal");t||(t=document.createElement("dialog"),t.className="bulk-notes-modal",t.innerHTML='\n                <form method="dialog" data-save="favourites">\n                    <h2>Add Notes to Selected Items</h2>\n\n                    <div class="field">\n                        <label for="bulk-notes">Notes (will be applied to all selected items)</label>\n                        <textarea id="bulk-notes" name="bulk_notes" rows="5"></textarea>\n                    </div>\n\n                    <div class="actions">\n                        <button type="button" class="cancel">Cancel</button>\n                        <button type="submit" class="save">Save Notes</button>\n                    </div>\n                </form>\n            ',document.body.appendChild(t),t.querySelector("form").addEventListener("submit",(e=>{e.preventDefault();const s=t.querySelector("#bulk-notes").value;this.saveBulkNotes(s),t.close()})),t.querySelector(".cancel").addEventListener("click",(()=>{t.close()}))),t.querySelector("form")?.reset(),t.showModal(),setTimeout((()=>{t.querySelector("#bulk-notes")?.focus()}),100),window.jvbA11y&&window.jvbA11y.announce("Add notes dialog opened")}saveBulkNotes(t){if(t)try{this.showLoader("Saving notes...");let e=Array.from(this.state.selectedItems.values());this.queue.addToQueue({type:"favourite_notes",data:{target_id:e.join(","),notes:t}}),showToast(`Notes saved for ${e.length} items`),this.a11y.announce(`Notes saved for ${e.length} items`),this.clearSelection()}catch(t){this.handleError(t,"saving bulk notes")}finally{this.hideLoader()}}async bulkUnfavourite(){if(confirm("Are you sure you want to remove these items from your favourites?"))try{this.showLoader("Removing from favourites...");const t=[];this.state.selectedItems.forEach((e=>{const s=this.grid.querySelector(`.item[data-id="${e}"]`);if(!s)return;const i=s.dataset.type;t.push({target_id:parseInt(e),type:i,action:"remove"})})),this.queue.addToQueue({type:"favourite_toggle",data:t});const e=[];this.state.selectedItems.forEach((t=>{const s=this.grid.querySelector(`.item[data-id="${t}"]`);if(!s)return;s.style.opacity="0",s.style.transform="scale(0.9)",s.style.transition="opacity 0.3s ease, transform 0.3s ease";const i=new Promise((t=>{setTimeout((()=>{s.remove(),t()}),300)}));e.push(i)})),await Promise.all(e),0===this.grid.children.length&&this.showEmptyState(),this.clearSelection(),showToast(`Removed ${t.length} items from favourites`),this.a11y.announce(`Removed ${t.length} items from favourites`)}catch(t){this.handleError(t,"removing favourites")}finally{this.hideLoader()}}async handleShareList(t){const e=t.get("share_email");if(e)if(this.validateEmail(e))try{this.showLoader("Sharing list..."),this.queue.addToQueue({type:"favourite_list_share",data:{list_id:parseInt(this.state.currentListId),email:e,permission_type:"view"}}),this.shareListModal.querySelector("#share-email").value="",this.loadSharedUsers(this.state.currentListId),showToast(`Invitation sent to ${e}`),this.a11y.announce(`Invitation sent to ${e}`)}catch(t){this.handleError(t,"sharing list")}finally{this.hideLoader()}else showToast("Please enter a valid email address","error");else showToast("Please enter an email address","error")}showLoader(t="Loading..."){if(!this.loader)return;const e=this.loader.querySelector(".loader-message");e&&(e.textContent=t),this.loader.hidden=!1}hideLoader(){this.loader&&(this.loader.hidden=!0)}showToast(t,e){window.jvbNotifications.showToast(t,e)}handleError(t,e){console.error(`Favourites error (${e}):`,t),showToast(`Error ${e}: ${t.message||"Something went wrong"}`,"error"),window.jvbError&&window.jvbError.log(t,{component:"FavouritesManager",action:e}),window.jvbA11y&&window.jvbA11y.announce(`Error ${e}. ${t.message||"Please try again."}`)}validateEmail(t){return/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(t)}};
assets/js/min/news.min.js
@@ -1 +1 @@
window.newsManager=class{constructor(){this.queue=window.jvbQueue,this.loading=window.jvbLoading,this.cache=window.jvbCache,this.a11y=window.jvbA11y,this.error=window.jvbError,this.activeTab="all",this.tabs=new window.jvbTabs(document.querySelector(".replace"),{news:()=>{this.activeTab="all",this.resetFilters(),this.loadItems(!0).then((()=>{}))},mine:()=>{console.log("switching to mine tab"),this.activeTab="own",this.resetFilters(),this.filters.artist=window.auth.getUser(),this.loadItems(!0).then((()=>{}))},watching:()=>{this.activeTab="watching",this.resetFilters(),this.filters.watched=!0,this.loadItems(!0).then((()=>{}))}}),this.isLoading=!1,this.alreadyHandling=!1,this.template=new Map,this.endpoints={news:"news",vote:"news/vote"},this.resetFilters(),this.state={hasMore:!0,pages:1,items:0},this.initElements(),this.initEvents(),this.loadItems()}resetFilters(){this.filters={page:1,order:"DESC",orderby:"date",shop:null,type:null,artist:null,watched:!1}}initElements(){this.container=document.querySelector(".replace"),this.grid=this.container.querySelector(".item-grid"),this.addButton=this.container.querySelector(".add-item-btn"),this.addModal=new window.jvbModal(this.container.querySelector(".create-modal"),{render:this.renderModal.bind(this),open:this.addButton,content:"news",openMessage:"Opened modal to create a news post.",onSave:this.saveModal.bind(this)}),this.filterForm=this.container.querySelector("form"),this.dateRangeFilter=new window.jvbModal(this.container.querySelector("dialog.date-range"),{open:!1}),this.clearFilters=this.container.querySelector(".clear-filters"),this.replyModal=new window.jvbModal(this.container.querySelector(".create-response"),{open:!1,content:"response",openMessage:"Opened Response modal",onSave:this.saveCreatedResponse.bind(this)})}initEvents(){this.filterForm.addEventListener("change",(e=>{let t=e.target.value;if(!e.target.closest(".date-range"))if("custom"===t)this.handleCustomDateRange();else{let s=e.target.name;s?this.filters[s]=t:this.resetFilters(),this.loadItems(!0)}})),document.addEventListener("click",(e=>{if(e.target===this.clearFilters&&(this.filterForm.reset(),this.resetFilters(),this.loadItems(!0)),e.target.closest("button.reply")){let t=e.target.closest("button"),s=t.closest(".item").dataset.id,n="";"news"===t.dataset.type?n=t.closest(".item").querySelector(".item-info").innerHTML:(n=t.closest(".response").querySelector(".content").innerHTML,this.replyModal.modal.dataset.parent_id=t.id.replace("reply-to","")),this.replyModal.modal.dataset.id=s,this.replyModal.modal.dataset.type=t.dataset.type,this.replyModal.modal.querySelector(".original").innerHTML="<h5>Replying to:</h5>"+n,this.replyModal.handleOpen()}}))}renderModal(){}handleCustomDateRange(){this.dateRangeFilter.handleOpen();let e=this.dateRangeFilter.modal.querySelector("input.date-start"),t=this.dateRangeFilter.modal.querySelector("input.date-end"),s=this.dateRangeFilter.modal.querySelector("select");this.dateRangeFilter.modal.querySelectorAll("input, select").forEach((n=>{n.addEventListener("change",(i=>{n===e&&""!==t.value||n===t&&""!==e.value?(this.filters.dateFrom=e.value,this.filters.dateTo=t.value,this.dateRangeFilter.handleClose(),this.loadItems(!0)):n===s&&(this.filters.customDate=s.value,this.dateRangeFilter.handleClose(),this.loadItems(!0))}))}))}async saveModal(e){const t=new FormData(this.addModal.modal.querySelector("form"));t.append("user",window.auth.getUser()),this.queue.addToQueue({type:"new_news",data:t})}async loadItems(e=!0){if(!this.isLoading)try{this.isLoading=!0,this.loading.show(),e&&(this.filters.page=1,removeChildren(this.grid),this.grid.classList.remove("empty"));const t=this.buildFilters(),s=await this.cache.fetchWithCache(`${jvbSettings.api}${this.endpoints.news}?${t.toString()}`,{method:"GET",headers:{"X-WP-Nonce":window.auth.getNonce(),action_nonce:window.auth.getNonce("dash")}},{context:"news",forceRefresh:!0});return this.renderItems(s.items||[],this.filters.page>1),s.pagination&&(this.state={hasMore:s.has_more,items:s.items,pages:s.pages}),s}catch(e){throw this.handleError(e,"loading news"),e}finally{this.isLoading=!1,this.loading.hide()}}buildFilters(){const e=JSON.parse(JSON.stringify(this.filters));let t={};for(var[s,n]of Object.entries(e))!1!==n&&null!==n&&(t[s]=n);return new URLSearchParams(t)}renderItems(e,t=!1){if(t||removeChildren(this.grid),0===e.length)return this.a11y.announceItems(0,t),void this.showEmptyState();const s=document.createDocumentFragment(),n=i=>{const a=Math.min(i+10,e.length);for(let t=i;t<a;t++){const n=e[t],i=this.createItemElement(n);s.appendChild(i)}a<e.length?requestAnimationFrame((()=>{n(a)})):(this.grid.appendChild(s),this.a11y.makeNavigable(this.grid.querySelectorAll(".item:not([data-keyboard-nav])")),this.a11y.announceItems(e.length,t,this.state.hasMore))};e.length>0?n(0):this.a11y.announceItems(0,t)}createItemElement(e){const t=window.getTemplate(`template-${this.activeTab}`);t.id=`news-${e.id}`,t.dataset.id=e.id;const[s]=t.getElementsByTagName("h3"),[n]=t.getElementsByClassName("published"),[i]=t.getElementsByClassName("artist"),[a]=t.getElementsByClassName("shop"),[o]=t.getElementsByClassName("tldr"),[r]=t.getElementsByClassName("item-info"),[l]=t.getElementsByClassName("image");[s.textContent,n.textContent,i.href,i.textContent,o.textContent,r.innerHTML]=[e.title,formatTimeAgo(e.date),e.artist.url,e.artist.name,e.tldr,e.post_content],e.shop?[a.href,a.innerHTML]=[e.shop.url,jvbSettings.icons.shop+e.shop.name]:a.hidden=!0;const[d]=t.getElementsByClassName("favourite-button");if("own"!==this.activeTab)[d.dataset.id,d.dataset.artist]=[e.id,e.artist.id],window.userFavourites.news?.includes(parseInt(e.id))?(removeChildren(d),d.append(getIcon("star-fi"))):(removeChildren(d),d.append(getIcon("star")));else{d.hidden=!0;const[s]=t.getElementsByClassName("select-checkbox"),[n]=t.getElementsByTagName("label");[s.id,s.value,n.for]=[`item-${e.id}`,e.id,`item-${e.id}`]}let h="";window.userVotes?.news?.has(e.id)&&(h=window.userVotes.news.get(e.id)),console.log(e),t.querySelector(".summary").appendChild(formatVote(e,h));let c=window.getTemplate("commentsButton");c.href=`#responses-to-${e.id}`,c.querySelector(".count").textContent=e.comments.items.length;let m=window.getTemplate("responses");m.id=`responses-to-${e.id}`,m.querySelector("summary").textContent+=" { "+e.comments.items.length+" }";let p=window.getTemplate("replyButton");return p.id="reply-to-"+e.id,p.dataset.type="news",p.dataset.action="reply",t.appendChild(p),e.comments.items.length>0&&e.comments.items.forEach((e=>{m.appendChild(this.formatComment(e))})),t.appendChild(m),t.querySelector(".vote").prepend(c,t.querySelector(".vote button")),e.image&&e.image.replace(/src="([^"]+)"/,'data-src="$1"'),t}formatComment(e,t=null){let s=window.getTemplate("response");s.id="response-"+e.id;let n=s.querySelector("summary");n.querySelector(".content").innerHTML=e.response,n.querySelector(".created").textContent=formatTimeAgo(e.created_at);let i=checkVoteStatus("response",e.id);e.content="response",s.querySelector(".footer").appendChild(formatVote(e,i)),console.log(e);let a=window.getTemplate("replyButton");a.id="reply-to-"+e.id,t&&(a.dataset.parent_id=t),a.dataset.action="reply",a.dataset.type=e.content,n.querySelector(".vote").prepend(a,n.querySelector(".vote").firstElementChild);let o=n.querySelector(".artist"),r=n.querySelector(".shop");if(console.log(e),e.artist?(e.artist.shop||r.remove(),[o.href,o.textContent,r.href,r.textContent]=[e.artist.url,e.artist.name,e.artist.shop.url,e.artist.shop.name]):(o.remove(),r.remove()),e.children.items.length>0){let t=window.getTemplate("responses");t.id="replies-to-"+e.id,t.querySelector("summary").textContent="See Responses {"+e.children.items.length+"}",e.children.items.forEach((s=>{t.appendChild(this.formatComment(s,e.id))})),s.appendChild(t)}return s}renderResponseCreate(){}saveCreatedResponse(){console.log("Saving create response"),console.log(this.replyModal.modal.id);const e=this.replyModal.modal;let t={user:window.auth.getUser(),item_id:e.dataset.id,response:e.querySelector(".ql-editor").innerHTML,content:e.dataset.type,action:"create"};e.dataset.parent_id&&(t.parent_id=e.dataset.parent_id),console.log(t),this.queue.addToQueue({type:"new_response",data:t})}showEmptyState(){const e=document.createElement("div");e.className="no-news",e.innerHTML="\n            <h3>Nothing here</h3>\n            <p>No updates here.</p>\n            <p>Add some gap fillers from the main favourites tab.</p>\n        ",this.grid.appendChild(e),this.grid.classList.add("empty"),this.a11y.announce("No favourites to show!")}hideEmptyState(){let e=this.grid.querySelector(".no-news");e&&e.remove()}handleError(e,t){console.error(`News error (${t}):`,e),window.jvbError&&window.jvbError.log(e,{component:"NewsManager",action:t}),window.jvbA11y&&window.jvbA11y.announce(`Error ${t}. ${e.message||"Please try again."}`)}};
window.newsManager=class{constructor(){this.queue=window.jvbQueue,this.loading=window.jvbLoading,this.cache=window.jvbCache,this.a11y=window.jvbA11y,this.error=window.jvbError,this.activeTab="all",this.tabs=new window.jvbTabs(document.querySelector(".replace"),{news:()=>{this.activeTab="all",this.resetFilters(),this.loadItems(!0).then((()=>{}))},mine:()=>{console.log("switching to mine tab"),this.activeTab="own",this.resetFilters(),this.filters.artist=window.auth.getUser(),this.loadItems(!0).then((()=>{}))},watching:()=>{this.activeTab="watching",this.resetFilters(),this.filters.watched=!0,this.loadItems(!0).then((()=>{}))}}),this.isLoading=!1,this.alreadyHandling=!1,this.template=new Map,this.endpoints={news:"news",vote:"news/vote"},this.resetFilters(),this.state={hasMore:!0,pages:1,items:0},this.initElements(),this.initEvents(),this.loadItems()}resetFilters(){this.filters={page:1,order:"DESC",orderby:"date",shop:null,type:null,artist:null,watched:!1}}initElements(){this.container=document.querySelector(".replace"),this.grid=this.container.querySelector(".item-grid"),this.addButton=this.container.querySelector(".add-item-btn"),this.addModal=new window.jvbModal(this.container.querySelector(".create-modal"),{render:this.renderModal.bind(this),open:this.addButton,content:"news",openMessage:"Opened modal to create a news post.",onSave:this.saveModal.bind(this)}),this.filterForm=this.container.querySelector("form"),this.dateRangeFilter=new window.jvbModal(this.container.querySelector("dialog.date-range"),{open:!1}),this.clearFilters=this.container.querySelector(".clear-filters"),this.replyModal=new window.jvbModal(this.container.querySelector(".create-response"),{open:!1,content:"response",openMessage:"Opened Response modal",onSave:this.saveCreatedResponse.bind(this)})}initEvents(){this.filterForm.addEventListener("change",(e=>{let t=e.target.value;if(!e.target.closest(".date-range"))if("custom"===t)this.handleCustomDateRange();else{let s=e.target.name;s?this.filters[s]=t:this.resetFilters(),this.loadItems(!0)}})),document.addEventListener("click",(e=>{if(e.target===this.clearFilters&&(this.filterForm.reset(),this.resetFilters(),this.loadItems(!0)),e.target.closest("button.reply")){let t=e.target.closest("button"),s=t.closest(".item").dataset.id,n="";"news"===t.dataset.type?n=t.closest(".item").querySelector(".item-info").innerHTML:(n=t.closest(".response").querySelector(".content").innerHTML,this.replyModal.modal.dataset.parent_id=t.id.replace("reply-to","")),this.replyModal.modal.dataset.id=s,this.replyModal.modal.dataset.type=t.dataset.type,this.replyModal.modal.querySelector(".original").innerHTML="<h5>Replying to:</h5>"+n,this.replyModal.handleOpen()}}))}renderModal(){}handleCustomDateRange(){this.dateRangeFilter.handleOpen();let e=this.dateRangeFilter.modal.querySelector("input.date-start"),t=this.dateRangeFilter.modal.querySelector("input.date-end"),s=this.dateRangeFilter.modal.querySelector("select");this.dateRangeFilter.modal.querySelectorAll("input, select").forEach((n=>{n.addEventListener("change",(i=>{n===e&&""!==t.value||n===t&&""!==e.value?(this.filters.dateFrom=e.value,this.filters.dateTo=t.value,this.dateRangeFilter.handleClose(),this.loadItems(!0)):n===s&&(this.filters.customDate=s.value,this.dateRangeFilter.handleClose(),this.loadItems(!0))}))}))}async saveModal(e){const t=new FormData(this.addModal.modal.querySelector("form"));t.append("user",window.auth.getUser()),this.queue.addToQueue({type:"new_news",data:t})}async loadItems(e=!0){if(!this.isLoading)try{this.isLoading=!0,this.loading.show(),e&&(this.filters.page=1,removeChildren(this.grid),this.grid.classList.remove("empty"));const t=this.buildFilters(),s=await this.cache.fetchWithCache(`${jvbSettings.api}${this.endpoints.news}?${t.toString()}`,{method:"GET",headers:{"X-WP-Nonce":window.auth.getNonce(),"X-Action-Nonce":window.auth.getNonce("dash")}},{context:"news",forceRefresh:!0});return this.renderItems(s.items||[],this.filters.page>1),s.pagination&&(this.state={hasMore:s.has_more,items:s.items,pages:s.pages}),s}catch(e){throw this.handleError(e,"loading news"),e}finally{this.isLoading=!1,this.loading.hide()}}buildFilters(){const e=JSON.parse(JSON.stringify(this.filters));let t={};for(var[s,n]of Object.entries(e))!1!==n&&null!==n&&(t[s]=n);return new URLSearchParams(t)}renderItems(e,t=!1){if(t||removeChildren(this.grid),0===e.length)return this.a11y.announceItems(0,t),void this.showEmptyState();const s=document.createDocumentFragment(),n=i=>{const a=Math.min(i+10,e.length);for(let t=i;t<a;t++){const n=e[t],i=this.createItemElement(n);s.appendChild(i)}a<e.length?requestAnimationFrame((()=>{n(a)})):(this.grid.appendChild(s),this.a11y.makeNavigable(this.grid.querySelectorAll(".item:not([data-keyboard-nav])")),this.a11y.announceItems(e.length,t,this.state.hasMore))};e.length>0?n(0):this.a11y.announceItems(0,t)}createItemElement(e){const t=window.getTemplate(`template-${this.activeTab}`);t.id=`news-${e.id}`,t.dataset.id=e.id;const[s]=t.getElementsByTagName("h3"),[n]=t.getElementsByClassName("published"),[i]=t.getElementsByClassName("artist"),[a]=t.getElementsByClassName("shop"),[o]=t.getElementsByClassName("tldr"),[r]=t.getElementsByClassName("item-info"),[l]=t.getElementsByClassName("image");[s.textContent,n.textContent,i.href,i.textContent,o.textContent,r.innerHTML]=[e.title,formatTimeAgo(e.date),e.artist.url,e.artist.name,e.tldr,e.post_content],e.shop?[a.href,a.innerHTML]=[e.shop.url,jvbSettings.icons.shop+e.shop.name]:a.hidden=!0;const[d]=t.getElementsByClassName("favourite-button");if("own"!==this.activeTab)[d.dataset.id,d.dataset.artist]=[e.id,e.artist.id],window.userFavourites.news?.includes(parseInt(e.id))?(removeChildren(d),d.append(getIcon("star-fi"))):(removeChildren(d),d.append(getIcon("star")));else{d.hidden=!0;const[s]=t.getElementsByClassName("select-checkbox"),[n]=t.getElementsByTagName("label");[s.id,s.value,n.for]=[`item-${e.id}`,e.id,`item-${e.id}`]}let h="";window.userVotes?.news?.has(e.id)&&(h=window.userVotes.news.get(e.id)),console.log(e),t.querySelector(".summary").appendChild(formatVote(e,h));let c=window.getTemplate("commentsButton");c.href=`#responses-to-${e.id}`,c.querySelector(".count").textContent=e.comments.items.length;let m=window.getTemplate("responses");m.id=`responses-to-${e.id}`,m.querySelector("summary").textContent+=" { "+e.comments.items.length+" }";let p=window.getTemplate("replyButton");return p.id="reply-to-"+e.id,p.dataset.type="news",p.dataset.action="reply",t.appendChild(p),e.comments.items.length>0&&e.comments.items.forEach((e=>{m.appendChild(this.formatComment(e))})),t.appendChild(m),t.querySelector(".vote").prepend(c,t.querySelector(".vote button")),e.image&&e.image.replace(/src="([^"]+)"/,'data-src="$1"'),t}formatComment(e,t=null){let s=window.getTemplate("response");s.id="response-"+e.id;let n=s.querySelector("summary");n.querySelector(".content").innerHTML=e.response,n.querySelector(".created").textContent=formatTimeAgo(e.created_at);let i=checkVoteStatus("response",e.id);e.content="response",s.querySelector(".footer").appendChild(formatVote(e,i)),console.log(e);let a=window.getTemplate("replyButton");a.id="reply-to-"+e.id,t&&(a.dataset.parent_id=t),a.dataset.action="reply",a.dataset.type=e.content,n.querySelector(".vote").prepend(a,n.querySelector(".vote").firstElementChild);let o=n.querySelector(".artist"),r=n.querySelector(".shop");if(console.log(e),e.artist?(e.artist.shop||r.remove(),[o.href,o.textContent,r.href,r.textContent]=[e.artist.url,e.artist.name,e.artist.shop.url,e.artist.shop.name]):(o.remove(),r.remove()),e.children.items.length>0){let t=window.getTemplate("responses");t.id="replies-to-"+e.id,t.querySelector("summary").textContent="See Responses {"+e.children.items.length+"}",e.children.items.forEach((s=>{t.appendChild(this.formatComment(s,e.id))})),s.appendChild(t)}return s}renderResponseCreate(){}saveCreatedResponse(){console.log("Saving create response"),console.log(this.replyModal.modal.id);const e=this.replyModal.modal;let t={user:window.auth.getUser(),item_id:e.dataset.id,response:e.querySelector(".ql-editor").innerHTML,content:e.dataset.type,action:"create"};e.dataset.parent_id&&(t.parent_id=e.dataset.parent_id),console.log(t),this.queue.addToQueue({type:"new_response",data:t})}showEmptyState(){const e=document.createElement("div");e.className="no-news",e.innerHTML="\n            <h3>Nothing here</h3>\n            <p>No updates here.</p>\n            <p>Add some gap fillers from the main favourites tab.</p>\n        ",this.grid.appendChild(e),this.grid.classList.add("empty"),this.a11y.announce("No favourites to show!")}hideEmptyState(){let e=this.grid.querySelector(".no-news");e&&e.remove()}handleError(e,t){console.error(`News error (${t}):`,e),window.jvbError&&window.jvbError.log(e,{component:"NewsManager",action:t}),window.jvbA11y&&window.jvbA11y.announce(`Error ${t}. ${e.message||"Please try again."}`)}};
assets/js/min/notificationManager.min.js
@@ -1 +1 @@
(()=>{class t{constructor(){this.resetFilters(),this.activeTab="all",this.isLoading=!1,this.loading=window.jvbLoading,this.container=document.querySelector(".container"),this.grid=this.container.querySelector(".notifications-list"),this.tabs=new window.jvbTabs(this.container,{all:()=>{this.resetFilters(),this.activeTab="all",this.loadNotifications()},favourite:()=>{this.resetFilters(),this.activeTab="favourite",this.filters.content="favourite",this.loadNotifications()},artist:()=>{this.resetFilters(),this.activeTab="artist",this.filters.content="artist",this.loadNotifications()},shop:()=>{this.resetFilters(),this.activeTab="shop",this.filters.content="shop",this.loadNotifications()},event:()=>{this.resetFilters(),this.activeTab="favourite",this.filters.content="favourite",this.loadNotifications()},news:()=>{this.resetFilters(),this.activeTab="news",this.filters.content="news",this.loadNotifications()},system:()=>{this.resetFilters(),this.activeTab="system",this.filters.content="system",this.loadNotifications()}}),this.loadNotifications()}resetFilters(){this.filters={content:"all",date:""},this.hasMore=!0}async loadNotifications(t=!0){if(!this.isLoading&&this.hasMore)try{this.isLoading=!0,this.loading.show(),t&&(this.filters.page=1,this.grid.classList.remove("empty"));const i=this.buildFilters();console.log(this.filters),console.log("Reset? ",this.reset);const s=await this.cache.fetchWithCache(`${jvbSettings.api}notifications?${i.toString()}`,{method:"GET",headers:{"X-WP-Nonce":window.auth.getNonce(),action_nonce:jvbAdmin.nonce}},{context:"admin",forceRefresh:!0});return console.log(s),s}catch(t){throw this.handleError(t,"loading notifications"),t}finally{this.isLoading=!1,this.loading.hide()}}buildFilters(){const t=JSON.parse(JSON.stringify(this.filters));let i={};for(var[s,e]of Object.entries(t))!1!==e&&null!==e&&(i[s]=e);return i.context="admin",i.user=window.auth.getUser(),new URLSearchParams(i)}}document.addEventListener("DOMContentLoaded",(()=>{window.notificationsDash=new t,console.log(jvbSettings)}))})();
(()=>{class t{constructor(){this.resetFilters(),this.activeTab="all",this.isLoading=!1,this.loading=window.jvbLoading,this.container=document.querySelector(".container"),this.grid=this.container.querySelector(".notifications-list"),this.tabs=new window.jvbTabs(this.container,{all:()=>{this.resetFilters(),this.activeTab="all",this.loadNotifications()},favourite:()=>{this.resetFilters(),this.activeTab="favourite",this.filters.content="favourite",this.loadNotifications()},artist:()=>{this.resetFilters(),this.activeTab="artist",this.filters.content="artist",this.loadNotifications()},shop:()=>{this.resetFilters(),this.activeTab="shop",this.filters.content="shop",this.loadNotifications()},event:()=>{this.resetFilters(),this.activeTab="favourite",this.filters.content="favourite",this.loadNotifications()},news:()=>{this.resetFilters(),this.activeTab="news",this.filters.content="news",this.loadNotifications()},system:()=>{this.resetFilters(),this.activeTab="system",this.filters.content="system",this.loadNotifications()}}),this.loadNotifications()}resetFilters(){this.filters={content:"all",date:""},this.hasMore=!0}async loadNotifications(t=!0){if(!this.isLoading&&this.hasMore)try{this.isLoading=!0,this.loading.show(),t&&(this.filters.page=1,this.grid.classList.remove("empty"));const i=this.buildFilters();console.log(this.filters),console.log("Reset? ",this.reset);const s=await this.cache.fetchWithCache(`${jvbSettings.api}notifications?${i.toString()}`,{method:"GET",headers:{"X-WP-Nonce":window.auth.getNonce(),"X-Action-Nonce":jvbAdmin.nonce}},{context:"admin",forceRefresh:!0});return console.log(s),s}catch(t){throw this.handleError(t,"loading notifications"),t}finally{this.isLoading=!1,this.loading.hide()}}buildFilters(){const t=JSON.parse(JSON.stringify(this.filters));let i={};for(var[s,e]of Object.entries(t))!1!==e&&null!==e&&(i[s]=e);return i.context="admin",i.user=window.auth.getUser(),new URLSearchParams(i)}}document.addEventListener("DOMContentLoaded",(()=>{window.notificationsDash=new t,console.log(jvbSettings)}))})();
assets/js/min/notifications.min.js
@@ -1 +1 @@
(()=>{class t{constructor(t={}){this.popupQueue=[],this.isLoading=!1,this.cache=window.jvbCache,this.isProcessingQueue=!1,this.options={maxVisibleNotifications:5,displayDuration:{high:7e3,medium:5e3,low:3e3},position:"bottom-right",pollingInterval:6e4,...t},this.button=document.querySelector(".toggle.notifications"),this.submenu=document.querySelector(".notifications-preview"),this.toasts=document.querySelector(".toasts"),this.notificationsLoaded=!1,this.pollTimer=null,this.lastCheck=null,this.button&&this.submenu&&this.init(),this.clickListeners=this.checkClicks.bind(this),this.updateListeners()}init(){this.submenu.addEventListener("click",(t=>{const e=t.target.closest(".mark-read");if(e){const t=e.closest(".notification-preview");t&&this.markAsRead(t.dataset.id)}})),this.loadNotifications(),this.initializePolling()}checkClicks(t){if(t.target.closest(".close-toast")){let e=t.target.closest(".toast");e.classList.add("hiding"),setTimeout((()=>{e.remove(),this.updateListeners()}),300)}}updateListeners(){this.toasts.addEventListener("click",this.clickListeners)}toggleDropdown(){this.notificationsLoaded||this.loadNotifications()}async loadNotifications(t=!1){if(!this.isLoading)try{this.isLoading=!0;const t=new URLSearchParams({user:window.auth.getUser(),status:"unread",limit:5}),e=await this.cache.fetchWithCache(`${jvbSettings.api}notifications?${t.toString()}`,{method:"GET",headers:{"X-WP-Nonce":window.auth.getNonce(),action_nonce:window.auth.getNonce("notifications")}},{context:"notifications",forceRefresh:!0});this.renderPreviewNotifications(e.notifications),this.updateUnreadCount(e.total),this.notificationsLoaded=!0,this.lastCheck=(new Date).toUTCString()}catch(t){console.error("Error loading notifications:",t),this.renderErrorState(t.message)}}renderErrorState(t){const e=this.submenu.querySelector("#view-all");this.submenu.querySelectorAll("li:not(#view-all)").forEach((t=>t.remove()));const i=document.createElement("li");i.className="error-state",i.innerHTML=`\n        <p>${t}</p>\n        <button onclick="window.jvbNotifications.loadNotifications()">\n            Try Again\n        </button>\n    `,this.submenu.insertBefore(i,e)}renderPreviewNotifications(t){this.submenu.querySelector("#view-all");this.submenu.querySelectorAll("li:not(#view-all)").forEach((t=>t.remove())),t.forEach((t=>{let e=window.getTemplate("notificationItem");e.classList.add(t.status,`priority-${t.priority}`),e.dataset.id=t.id,e.prepend(getIcon(t.icon));let i=e.querySelector("p"),o=e.querySelector("time");[i.textContent,o.datetime,o.textContent]=[t.message,new Date(t.created_at).toISOString(),formatTimeAgo(t.created_at)];let s=window.getTemplate("notificationActions"),n=s.querySelector("button");t.actions.length>0&&(t.actions.forEach((e=>{let i=n.cloneNode(!0);e.primary&&i.classList.add("primary"),[i.dataset.id,i.dataset.action,i.textContent]=[t.id,e.label.toLowerCase(),e.label],s.append(i)})),n.remove()),e.append(s),this.submenu.prepend(e)})),0===t.length&&this.submenu.prepend(window.getTemplate("emptyNotification"))}queuePopupNotification(t){this.popupQueue.push(t),this.processPopupQueue()}async processPopupQueue(){if(!this.isProcessingQueue&&0!==this.popupQueue.length){for(this.isProcessingQueue=!0;this.popupQueue.length>0;){const t=this.popupQueue.shift();await this.showToast(t.message,t.type,t.actions),this.popupQueue.length>0&&await new Promise((t=>setTimeout(t,300)))}this.isProcessingQueue=!1}}showToast(t,e="success",i={}){let o=window.getTemplate("notificationPopup");if(o.classList.add(e),o.querySelector("p").textContent=t,Object.entries(i).length>0){let t=window.getTemplate("notificationActions"),e=i.querySelector("button");notification.actions.forEach((i=>{let o=e.cloneNode(!0);i.primary&&o.classList.add("primary"),[o.dataset.action,o.textContent]=[i.label.toLowerCase(),i.label],t.prepend(o)}))}this.toasts.append(o),setTimeout((()=>{o.classList.add("show")}),10),setTimeout((()=>{o.classList.add("hiding"),setTimeout((()=>{o.remove()}),300)}),3e3)}createNotificationElement(t){this.showToast(t.message),this.renderPreviewNotifications([t])}removePopupNotification(t){t.classList.remove("show"),setTimeout((()=>{t.remove()}),300)}updateUnreadCount(t){let e=this.button.querySelector("span");this.button.classList.remove("has"),[e.textContent,e.ariaLabel]=["","Notifications"],t&&!isNaN(t)&&(t=parseInt(t,10))>0&&(this.button.classList.add("has"),[e.textContent,e.ariaLabel]=[t,t+" unread notification"+(t>1?"s":"")])}async markAsRead(t){try{const e=this.submenu.querySelector(`[data-id="${t}"]`);if(!e)return;e.classList.add("slide-out");const i=await fetch(`${jvbSettings.api}notifications`,{method:"POST",headers:{"X-WP-Nonce":window.auth.getNonce(),action_nonce:window.auth.getNonce("dash")},body:{notification:t,user:window.auth.getUser()}});if(!i.ok)throw new Error(notificationSettings.strings.error);const o=await i.json();o.success&&(setTimeout((()=>{e.remove();if(0===this.submenu.querySelectorAll(".notification-preview").length){const t=this.submenu.querySelector("#view-all"),e=document.createElement("li");e.className="empty-state fade-in",e.textContent=notificationSettings.strings.noNotifications,this.submenu.insertBefore(e,t),requestAnimationFrame((()=>{e.classList.remove("fade-in")}))}}),300),this.updateUnreadCount(o.total))}catch(t){console.error("Error marking notification as read:",t)}}initializePolling(){this.pollTimer=setInterval((()=>{this.checkNotifications()}),this.options.pollingInterval),document.addEventListener("visibilitychange",(()=>{document.hidden||this.checkNotifications()}))}async checkNotifications(){try{const t=new URLSearchParams({user:window.auth.getUser(),status:"unread"}),e=await fetch(`${jvbSettings.api}notifications?${t.toString()}`,{headers:{"X-WP-Nonce":window.auth.getNonce(),action_nonce:window.auth.getNonce("dash"),"If-Modified-Since":this.lastCheck}});if(!e.ok)return;(await e.json()).has_new&&await this.loadNotifications(!0)}catch(t){console.error("Check notifications error:",t)}}destroy(){this.pollTimer&&(clearInterval(this.pollTimer),this.pollTimer=null)}}document.addEventListener("DOMContentLoaded",(async function(){window.auth.subscribe((e=>{"auth-loaded"===e&&(window.jvbNotifications=new t({position:"bottom-right",maxVisibleNotifications:5,displayDuration:5e3}))}))})),window.addNotification=function(t,e="info"){window.jvbNotifications.showToast(t,e)}})();
(()=>{class t{constructor(t={}){this.popupQueue=[],this.isLoading=!1,this.cache=window.jvbCache,this.isProcessingQueue=!1,this.options={maxVisibleNotifications:5,displayDuration:{high:7e3,medium:5e3,low:3e3},position:"bottom-right",pollingInterval:6e4,...t},this.button=document.querySelector(".toggle.notifications"),this.submenu=document.querySelector(".notifications-preview"),this.toasts=document.querySelector(".toasts"),this.notificationsLoaded=!1,this.pollTimer=null,this.lastCheck=null,this.button&&this.submenu&&this.init(),this.clickListeners=this.checkClicks.bind(this),this.updateListeners()}init(){this.submenu.addEventListener("click",(t=>{const e=t.target.closest(".mark-read");if(e){const t=e.closest(".notification-preview");t&&this.markAsRead(t.dataset.id)}})),this.loadNotifications(),this.initializePolling()}checkClicks(t){if(t.target.closest(".close-toast")){let e=t.target.closest(".toast");e.classList.add("hiding"),setTimeout((()=>{e.remove(),this.updateListeners()}),300)}}updateListeners(){this.toasts.addEventListener("click",this.clickListeners)}toggleDropdown(){this.notificationsLoaded||this.loadNotifications()}async loadNotifications(t=!1){if(!this.isLoading)try{this.isLoading=!0;const t=new URLSearchParams({user:window.auth.getUser(),status:"unread",limit:5}),e=await this.cache.fetchWithCache(`${jvbSettings.api}notifications?${t.toString()}`,{method:"GET",headers:{"X-WP-Nonce":window.auth.getNonce(),"X-Action-Nonce":window.auth.getNonce("notifications")}},{context:"notifications",forceRefresh:!0});this.renderPreviewNotifications(e.notifications),this.updateUnreadCount(e.total),this.notificationsLoaded=!0,this.lastCheck=(new Date).toUTCString()}catch(t){console.error("Error loading notifications:",t),this.renderErrorState(t.message)}}renderErrorState(t){const e=this.submenu.querySelector("#view-all");this.submenu.querySelectorAll("li:not(#view-all)").forEach((t=>t.remove()));const i=document.createElement("li");i.className="error-state",i.innerHTML=`\n        <p>${t}</p>\n        <button onclick="window.jvbNotifications.loadNotifications()">\n            Try Again\n        </button>\n    `,this.submenu.insertBefore(i,e)}renderPreviewNotifications(t){this.submenu.querySelector("#view-all");this.submenu.querySelectorAll("li:not(#view-all)").forEach((t=>t.remove())),t.forEach((t=>{let e=window.getTemplate("notificationItem");e.classList.add(t.status,`priority-${t.priority}`),e.dataset.id=t.id,e.prepend(getIcon(t.icon));let i=e.querySelector("p"),o=e.querySelector("time");[i.textContent,o.datetime,o.textContent]=[t.message,new Date(t.created_at).toISOString(),formatTimeAgo(t.created_at)];let s=window.getTemplate("notificationActions"),n=s.querySelector("button");t.actions.length>0&&(t.actions.forEach((e=>{let i=n.cloneNode(!0);e.primary&&i.classList.add("primary"),[i.dataset.id,i.dataset.action,i.textContent]=[t.id,e.label.toLowerCase(),e.label],s.append(i)})),n.remove()),e.append(s),this.submenu.prepend(e)})),0===t.length&&this.submenu.prepend(window.getTemplate("emptyNotification"))}queuePopupNotification(t){this.popupQueue.push(t),this.processPopupQueue()}async processPopupQueue(){if(!this.isProcessingQueue&&0!==this.popupQueue.length){for(this.isProcessingQueue=!0;this.popupQueue.length>0;){const t=this.popupQueue.shift();await this.showToast(t.message,t.type,t.actions),this.popupQueue.length>0&&await new Promise((t=>setTimeout(t,300)))}this.isProcessingQueue=!1}}showToast(t,e="success",i={}){let o=window.getTemplate("notificationPopup");if(o.classList.add(e),o.querySelector("p").textContent=t,Object.entries(i).length>0){let t=window.getTemplate("notificationActions"),e=i.querySelector("button");notification.actions.forEach((i=>{let o=e.cloneNode(!0);i.primary&&o.classList.add("primary"),[o.dataset.action,o.textContent]=[i.label.toLowerCase(),i.label],t.prepend(o)}))}this.toasts.append(o),setTimeout((()=>{o.classList.add("show")}),10),setTimeout((()=>{o.classList.add("hiding"),setTimeout((()=>{o.remove()}),300)}),3e3)}createNotificationElement(t){this.showToast(t.message),this.renderPreviewNotifications([t])}removePopupNotification(t){t.classList.remove("show"),setTimeout((()=>{t.remove()}),300)}updateUnreadCount(t){let e=this.button.querySelector("span");this.button.classList.remove("has"),[e.textContent,e.ariaLabel]=["","Notifications"],t&&!isNaN(t)&&(t=parseInt(t,10))>0&&(this.button.classList.add("has"),[e.textContent,e.ariaLabel]=[t,t+" unread notification"+(t>1?"s":"")])}async markAsRead(t){try{const e=this.submenu.querySelector(`[data-id="${t}"]`);if(!e)return;e.classList.add("slide-out");const i=await fetch(`${jvbSettings.api}notifications`,{method:"POST",headers:{"X-WP-Nonce":window.auth.getNonce(),"X-Action-Nonce":window.auth.getNonce("dash")},body:{notification:t,user:window.auth.getUser()}});if(!i.ok)throw new Error(notificationSettings.strings.error);const o=await i.json();o.success&&(setTimeout((()=>{e.remove();if(0===this.submenu.querySelectorAll(".notification-preview").length){const t=this.submenu.querySelector("#view-all"),e=document.createElement("li");e.className="empty-state fade-in",e.textContent=notificationSettings.strings.noNotifications,this.submenu.insertBefore(e,t),requestAnimationFrame((()=>{e.classList.remove("fade-in")}))}}),300),this.updateUnreadCount(o.total))}catch(t){console.error("Error marking notification as read:",t)}}initializePolling(){this.pollTimer=setInterval((()=>{this.checkNotifications()}),this.options.pollingInterval),document.addEventListener("visibilitychange",(()=>{document.hidden||this.checkNotifications()}))}async checkNotifications(){try{const t=new URLSearchParams({user:window.auth.getUser(),status:"unread"}),e=await fetch(`${jvbSettings.api}notifications?${t.toString()}`,{headers:{"X-WP-Nonce":window.auth.getNonce(),"X-Action-Nonce":window.auth.getNonce("dash"),"If-Modified-Since":this.lastCheck}});if(!e.ok)return;(await e.json()).has_new&&await this.loadNotifications(!0)}catch(t){console.error("Check notifications error:",t)}}destroy(){this.pollTimer&&(clearInterval(this.pollTimer),this.pollTimer=null)}}document.addEventListener("DOMContentLoaded",(async function(){window.auth.subscribe((e=>{"auth-loaded"===e&&(window.jvbNotifications=new t({position:"bottom-right",maxVisibleNotifications:5,displayDuration:5e3}))}))})),window.addNotification=function(t,e="info"){window.jvbNotifications.showToast(t,e)}})();
assets/js/min/queue.min.js
@@ -1 +1 @@
(()=>{class e{constructor(){this.a11y=window.jvbA11y,this.error=window.jvbError,this.user=window.auth.getUser(),this.canUpdateUI=!0,this.isProcessing=!1,this.isPolling=!1,this.queue=new Map,this.items=new Map,this.subscribers=new Set,this.api=jvbSettings.api,this.endpoint="queue",this.queueItems=new Map,this.init()}init(){this.headers={"X-WP-Nonce":window.auth.getNonce()},this.initElements(),this.initListeners(),this.initStore(),this.canUpdateUI&&this.ui.panel&&(this.popup=new window.jvbPopup({popup:this.ui.panel,toggle:this.ui.toggle.button,name:"Queue Panel"})),this.defineTemplates()}initElements(){this.panelStatuses=["syncing","synced","pending","offline"],this.statuses=["queued","localProcessing","uploading","pending","processing","completed","failed","failed_permanent"],this.pendingStatuses=["queued","localProcessing","uploading"],this.workingStatuses=["pending","processing"],this.completedStatuses=["completed","failed","failed_permanent"],this.icons={queued:"arrows-clockwise",localProcessing:"arrows-clockwise",uploading:"syncing",pending:"cloud",processing:"syncing",completed:"cloud-check",failed:"cloud-warning",failed_permanent:"cloud-warning"},this.selectors={panel:"aside#queue",toggle:{button:"button.qtoggle",indicator:".qtoggle .indicator",count:".qtoggle .count"},refresh:{button:"#queue .refresh .refreshNow",countdown:"#queue .refresh .countdown"},popup:{popup:"#queue .popup",message:"#queue .popup span"},items:{container:"#queue .qitems"},actions:{retry:"#queue .retry-all",clear:"#queue .dismiss-all"},filters:{filter:"#queue [data-filter]",all:{label:'#queue [for="qfilter-all"]',radio:'#queue [data-filter="all"]',count:'#queue [data-filter="all"] .count'},queued:{label:'#queue [for="qfilter-queued"]',input:'#queue [data-filter="queued"]',count:'#queue [for="qfilter-queued"] .count'},localProcessing:{label:'#queue [for="qfilter-localProcessing"]',input:'#queue [data-filter="localProcessing"]',count:'#queue [for="qfilter-localProcessing"] .count'},uploading:{label:'#queue [for="qfilter-uploading"]',input:'#queue [data-filter="uploading"]',count:'#queue [for="qfilter-uploading"] .count'},pending:{label:'#queue [for="qfilter-pending"]',input:'#queue [data-filter="pending"]',count:'#queue [for="qfilter-pending"] .count'},processing:{label:'#queue [for="qfilter-processing"]',input:'#queue [data-filter="processing"]',count:'#queue [for="qfilter-processing"] .count'},completed:{label:'#queue [for="qfilter-completed"]',input:'#queue [data-filter="completed"]',count:'#queue [for="qfilter-completed"] .count'},failed:{label:'#queue [for="qfilter-failed"]',input:'#queue [data-filter="failed"]',count:'#queue [for="qfilter-failed"] .count'}},item:{type:".type",status:".status",details:".info .details",icon:".status .icon",startedAt:".started time",completed:{wrap:".completed",label:".completed span",time:".completed time"},progress:{progress:".progress",fill:".progress .fill",details:".progress .details",icon:".progress .icon"},actions:{cancel:"button.cancel",retry:"button.retry",refresh:"button.refresh",dismiss:"button.dismiss"}}},this.ui=window.uiFromSelectors(this.selectors),this.ui.panel||(this.canUpdateUI=!1)}defineTemplates(){const e=window.jvbTemplates;e.define("emptyState"),e.define("queueItem",{setup({el:e,refs:t,manyRefs:s,data:i}){e.dataset.id=i.id}})}initListeners(){this.activityListeners=null,this.clickHandler=this.handleClick.bind(this),this.onlineHandler=this.handleOnline.bind(this),this.offlineHandler=this.handleOffline.bind(this),this.unloadHandler=this.handleBeforeUnload.bind(this),document.addEventListener("click",this.clickHandler),window.addEventListener("online",this.onlineHandler),window.addEventListener("offline",this.offlineHandler),window.addEventListener("beforeunload",this.unloadHandler)}handleOnline(){this.updatePanel("synced"),this.getQueueByStatus(this.pendingStatuses).length>0&&this.processQueue()}handleOffline(){this.updatePanel("offline")}handleBeforeUnload(e){if(!this.ui.panel)return;return this.getQueueByStatus(this.pendingStatuses).length>0?(e.preventDefault(),e.returnValue="",""):void 0}handleClick(e){if(!window.targetCheck(e,this.selectors.panel+", "+this.selectors.toggle.button))return;if(window.targetCheck(e,this.selectors.refresh.button))return this.ui.refresh.button.classList.add("fetching"),this.store.clearCache(),this.store.clearFilters(),void this.store.fetch().finally((()=>{this.ui.refresh.button.classList.remove("fetching")}));if(window.targetCheck(e,this.selectors.actions.refresh))return void this.handleRefresh(opId);if(window.targetCheck(e,this.selectors.actions.clear))return void this.opActions("completed","dismiss").then((()=>{}));if(window.targetCheck(e,this.selectors.actions.retry))return void this.opActions("failed","retry").then((()=>{}));const t=window.targetCheck(e,"[data-action]");if(t){const e=t.closest("[data-id]")?.dataset.id;return void(e&&this.opActions(e,t.dataset.action))}const s=window.targetCheck(e,this.selectors.filters.filter);s&&this.setFilter(s.dataset.filter)}setFilter(e){Object.values(this.ui.filters).forEach((t=>{t.input?.dataset.filter===e&&(t.input.checked=!0)})),"all"===e?this.store.clearFilters():this.store.setFilter("status",e)}trackActivity(){if(!this.activityListeners){const e=["mousedown","mousemove","keypress","scroll","touchstart"];this.activityListeners=e.map((e=>{const t=()=>this.resetActivityTimer();return document.addEventListener(e,t,{passive:!0}),{event:e,handler:t}}))}this.resetActivityTimer()}resetActivityTimer(){this.activityTimer&&clearTimeout(this.activityTimer),this.activityTimer=setTimeout((()=>{this.processQueue()}),1750)}stopActivityTracking(){this.activityTimer&&(clearTimeout(this.activityTimer),this.activityTimer=null),this.activityListeners&&(this.activityListeners.forEach((({event:e,handler:t})=>{document.removeEventListener(e,t)})),this.activityListeners=null)}initStore(){if(!this.user)return;const e=window.jvbStore.register("queue",{storeName:"queue",keyPath:"id",endpoint:this.endpoint,TTL:1/0,indexes:[{name:"status",keyPath:"status"},{name:"type",keyPath:"type"}],filters:{user:window.auth.getUser()},showLoading:!1});this.store=e.queue,this.store.subscribe(((e,t)=>{switch(e){case"data-loaded":this.store.getAll().forEach((e=>{const t=this.queue.get(e.id),s=this.mapServerOperation(e);this.queue.set(s.id,s),t&&t.status!==s.status&&this.notify("operation-status",s)})),this.maybeStartPolling(),this.updateUI();break;case"items-save":this.maybeStartPolling(),this.updateUI();break;case"item-saved":t.item&&(this.queue.set(t.item.id,t.item),t.previousItem?.status!==t.item.status&&this.notify("operation-status",t.item)),this.maybeStartPolling()}}))}handleRefresh(e){const t=this.getQueue(e);if(!t)return;let s=null;if(s={content_update:t.data?.posts?Object.values(t.data.posts)[0]?.content:null,batch_creation:t.data?.content,image_upload:"uploads",video_upload:"uploads",document_upload:"uploads"}[t.type],s&&window.jvbStore){if(window.jvbStore.stores.get(s)){window.jvbStore.clearCache(s),window.jvbStore.fetch(s);const t=this.items.get(e)?.ui?.actions?.refresh;if(t){const e=t.querySelector("span").textContent;t.querySelector("span").textContent="Refreshed!",t.disabled=!0,setTimeout((()=>{t.querySelector("span").textContent=e,t.disabled=!1}),2e3)}}}else confirm("Refresh the page to see changes?")&&window.location.reload()}addToQueue(e){const t={id:`u${this.user}_${Date.now()}_${Math.random().toString(36).substring(2,9)}`,endpoint:null,method:"POST",headers:{},data:{},delay:!1,canMerge:!0,popup:"Saving changes...",title:"Operation",status:"queued",timestamp:Date.now(),created_at:(new Date).toISOString(),retries:0,user:this.user,...e};if(t.headers={...this.headers,...t.headers},!t.endpoint||!t.data)return null;if(t.popup&&this.ui.popup?.message&&(this.ui.popup.message.textContent=t.popup,this.ui.popup.popup.hidden=!1,setTimeout((()=>this.ui.popup.popup.hidden=!0),2e3)),!t.delay)return this.queue.set(t.id,t),this.processOperation(t).then((()=>{})),this.store.clearCache(),this.maybeStartPolling(),this.toggleQueue(),t.id;const s=Array.from(this.getAllQueue()).filter((e=>"queued"===e.status&&e.endpoint===t.endpoint&&e.canMerge));if(s.length>0){const e=s[0];return e.data=window.deepMerge(e.data,t.data),e.timestamp=Date.now(),this.setQueue(e),this.updateOperationStatus(e.id,e.status),this.updateUI(),this.trackActivity(),e.id}return this.store.clearCache(),this.setQueue(t),this.updateOperationStatus(t.id,t.status),this.updateUI(),this.trackActivity(),t.id}async opActions(e,t){if(this.statuses.includes(e)?e=this.getQueueByStatus(e).map((e=>e.id)):"string"==typeof e&&(e=[e]),0===e.length)return;if(!["cancel","dismiss","retry"].includes(t))return;const s=["cancel","dismiss"].includes(t);s&&e.forEach((e=>{this.removeOperationUI(e)}));try{const i=await fetch(`${this.api}${this.endpoint}`,{method:"POST",headers:{"Content-Type":"application/json",...this.headers},body:JSON.stringify({action:t,ids:e,user:this.user})});if(!i.ok)throw new Error(`${t} failed: ${i.status}`);const n=await i.json();if(!n.success)throw new Error(n.message||`${t} operation failed`);return e.forEach((e=>{let i=this.getQueue(e);if(i&&this.notify(`${t}-operation`,i),s)this.clearQueue(e);else{let t=this.getQueue(e);t.status="queued",this.setQueue(t),this.updateOperationStatus(t.id,t.status)}})),"retry"===t&&this.trackActivity(),this.updateUI(),n}catch(s){return await window.jvbError.log(s,{component:"Queue",operation:"performQueueAction",action:t,operationIds:e,itemCount:e.length},(()=>this.opActions(e,t))),{success:!1,error:s.message}}}async processQueue(){if(this.isProcessing)return;const e=this.getQueueByStatus("queued");if(0===e.length)return void this.stopActivityTracking();this.setProcessing();for(const t of e)await this.processOperation(t);this.setProcessing(!1);0===this.getQueueByStatus("queued").length?this.stopActivityTracking():this.trackActivity(),this.toggleQueue(this.maybeStartPolling())}async processOperation(e){try{this.queue.has(e.id)||this.queue.set(e.id,e);let t,s=!1;if(e.data?._isFormData&&!e.data instanceof FormData&&(s=!0,e.data=await this.store.objectToFormData(e.data)),this.updateOperationStatus(e.id,"uploading"),e.data instanceof FormData?(e.data.append("id",e.id),e.data.append("user",window.auth.getUser()),t=e.data):(t=JSON.stringify({...e.data,id:e.id,user:window.auth.getUser()}),e.headers["Content-Type"]="application/json"),null==t)return;const i=await fetch(`${this.api}${e.endpoint}`,{method:e.method,headers:e.headers,body:t}),n=await i.json();if(s&&(e.data={}),!i.ok||!n.success)throw new Error(n.message||`HTTP ${i.status}`);n.id&&e.id!==n.id?e=await this.handleServerMerge(e,n):(e.status=n.status??"pending",e.serverData=n,this.updateOperationStatus(e.id,e.status)),this.a11y.announce(`${e.title} sent to server for processing`),this.setQueue(e)}catch(t){console.error("Operation failed: ",t),e.retries++,e.lastError=t.message,e.retries>=3?e.status="failed_permanent":e.status="failed",this.updateOperationStatus(e.id,e.status),this.setQueue(e)}}async handleServerMerge(e,t){const s=this.getQueue(t.id);return s?(e.status=t.status||"pending",e.serverData=t,this.mergeOp(s,e)):(this.clearQueue(e.id),this.setQueue(t),t)}mergeOp(e,t){return e.data=window.deepMerge(e.data,t.data),e.status=t.status,Object.hasOwn(t,"serverData")&&(e.serverData=t.serverData),this.updateOperationStatus(e.id,e.status),this.removeOperationUI(t.id),this.clearQueue(t.id),e}sortByDate(e){return e.sort(((e,t)=>(e.updated_at??e.timestamp??0)-(t.updated_at??t.timestamp??0)))}sortOperations(e){const t={processing:0,uploading:1,pending:2,queued:3,localProcessing:4,failed:5,completed:6,failed_permanent:7};return e.sort(((e,s)=>{const i=(t[e.status]??99)-(t[s.status]??99);if(0!==i)return i;const n=e.updated_at??e.timestamp??0,a=s.updated_at??s.timestamp??0;return new Date(a)-new Date(n)}))}getAllQueue(){let e=[...new Set([...Array.from(this.store.data.values()),...Array.from(this.queue.values())])];return this.sortOperations(e)}getQueueByStatus(e){"string"==typeof e&&(e=[e]);let t=[...new Set([...Array.from(this.store.filterByIndex({status:e})),...Array.from(this.queue.values()).filter((t=>e.includes(t.status)))])];return this.sortOperations(t)}updateOperationStatus(e,t){let s=this.getQueue(e);s&&this.statuses.includes(t)&&(s.status=t,this.notify("operation-status",s),this.setQueue(s))}setQueue(e){this.store.save(e),this.queue.set(e.id,e)}getQueue(e){return this.queue.has(e)?this.queue.get(e):this.store.get(e)}clearQueue(e){this.queue.delete(e),this.store.delete(e)}maybeStartPolling(){return this.getQueueByStatus([...this.pendingStatuses,...this.workingStatuses]).length>0?(this.startPolling(),!0):(this.updatePanel("synced"),!1)}startPolling(){this.isPolling||(this.isPolling=!0,this.updatePanel("pending"),this.runPollCycle())}async runPollCycle(){if(this.isPolling){try{if(this.ui.refresh.button.classList.add("fetching"),this.store.clearCache(),await this.store.fetch(),this.ui.refresh.button.classList.remove("fetching"),!this.maybeStartPolling())return this.stopPolling(),void this.updatePanel("synced")}catch(e){console.error("Polling error:",e)}this.startCountdown(5,(()=>this.runPollCycle()))}}startCountdown(e,t){this.ui.refresh.countdown?(this.ui.refresh.countdown.classList.add("counting"),this.ui.refresh.countdown.textContent=e,this.countdownTimer=setInterval((()=>{--e>0?this.ui.refresh.countdown.textContent=e:(this.stopCountdown(),t&&t())}),1e3)):console.warn("Countdown element not found")}stopPolling(){this.isPolling&&(this.isPolling=!1,this.pollTimer&&(clearInterval(this.pollTimer),this.pollTimer=null),this.stopCountdown())}stopCountdown(){this.countdownTimer&&(clearInterval(this.countdownTimer),this.countdownTimer=null),this.ui.refresh.countdown.classList.remove("counting"),this.ui.refresh.countdown.textContent=""}updateUI(){this.canUpdateUI&&window.debouncer.schedule("queue-ui",this.handleUpdateUI.bind(this))}handleUpdateUI(){const e=this.getAllQueue();this.ui.actions.retry.disabled=0===e.filter((e=>"failed"===e.status)).length,this.ui.actions.clear.disabled=0===e.filter((e=>"completed"===e.status)).length;const t=e.filter((e=>[...this.pendingStatuses,...this.workingStatuses].includes(e.status))).length;this.ui.toggle.count.hidden=0===t,this.ui.toggle.count.textContent=t;for(let t of this.statuses){if("failed_permanent"===t)continue;let s=e.filter((e=>e.status===t)).length;this.ui.filters[t].label.hidden=0===s,this.ui.filters[t].input.dataset.count=`${s}`,this.ui.filters[t].count.textContent=s>0?s:""}this.renderOperations()}renderOperations(){if(!this.ui.items.container)return;const e=this.store.filters?.status??"all",t="all"===e?this.getAllQueue():this.getQueueByStatus(e),s=this.sortOperations(t);if(0===s.length){window.removeChildren(this.ui.items.container);const e=window.jvbTemplates.create("emptyQueue");return this.ui.items.container.append(e),void this.a11y.announce("No items in queue")}this.ui.items.container.querySelector(".empty-group")?.remove();const i=new Set(s.map((e=>e.id)));this.items.forEach(((e,t)=>{i.has(t)||(e.element?.remove(),this.items.delete(t))})),s.forEach(((e,t)=>{let s=this.items.get(e.id);s||(s=this.createOperationElement(e)),s?.element&&(this.updateOperationUI(e.id),this.ui.items.container.append(s.element))}))}createOperationElement(e){const t=window.jvbTemplates.create("queueItem",e),s={element:t,ui:window.uiFromSelectors(this.selectors.item,t)};return this.items.set(e.id,s),s}updateOperationUI(e){let t=this.items.has(e)?this.items.get(e):this.createOperationElement(e);if(!t)return;let s=this.getQueue(e),i=t.element;i.classList.remove(this.statuses),i.classList.add(s.status);let n=this.getProgress(s);t.ui.type&&t.ui.type.textContent!==s.title&&(t.ui.type.textContent=s.title),t.ui.status&&(t.ui.status.title=this.statusLabel(s.status)),t.ui.icon&&(t.ui.icon.className=`icon icon-${this.icons[s.status]}`),t.ui.details&&(t.ui.details.textContent=this.itemMessage(s)),t.ui.startedAt&&(t.ui.startedAt.setAttribute("datetime",s.created_at),t.ui.startedAt.textContent=window.formatTimeAgo(s.created_at));s.status;const a="completed"===s.status&&(s.completed_at||s.updated_at);if(t.ui.completed.wrap.hidden=!a,a){const e=s.completed_at??s.updated_at;t.ui.completed.label.textContent="Completed: ",t.ui.completed.time.setAttribute("datetime",e),t.ui.completed.time.textContent=window.formatTimeAgo(e)}window.showProgress(t.ui.progress,n,100,this.statusLabel(s.status)),t.ui.actions.cancel&&(t.ui.actions.cancel.hidden=this.completedStatuses.includes(s.status)),t.ui.actions.retry&&(s.retries>=3&&(t.ui.actions.retry.disabled=!0),t.ui.actions.retry.hidden="failed"!==s.status),t.ui.actions.dismiss&&(t.ui.actions.dismiss.hidden=this.pendingStatuses.includes(s.status)),t.ui.actions.refresh&&(t.ui.actions.refresh.hidden="completed"!==s.status)}getProgress(e){if(e.progress)return e.progress;if(!this.statuses.includes(e.status))return 0;return{queued:10,uploading:25,pending:40,processing:70,completed:100,failed:0,failed_permanent:0}[e.status]??0}removeOperationUI(e){let t=this.items.get(e);t&&window.fade(t.element,!1)}updatePanel(e="syncing"){this.ui.panel&&this.panelStatuses.includes(e)&&(this.ui.panel.classList.remove(...this.panelStatuses),this.ui.panel.classList.add(e))}statusLabel(e){if(!this.statuses.includes(e))return"";return{queued:"Queued",localProcessing:"Processing locally",uploading:"Uploading",pending:"Waiting on server",processing:"Processing",completed:"Completed",failed:"Failed",failed_permanent:"Failed permanently"}[e]}itemMessage(e){if(Object.hasOwn(e,"message")&&""!==e.message)return e.message;if(Object.hasOwn(e,"error_message")&&e.error_message)return e.error_message;switch(e.status){case"queued":return"Waiting to send...";case"uploading":return"Sending to server...";case"pending":return e.position?`Position ${e.position} in queue`:"In server queue";case"processing":return e.progress?`${e.progress}% complete`:"Processing...";case"completed":return"Successfully completed. Refresh to see changes.";case"failed":return`Failed: ${e.lastError||"Unknown error"} (Retry ${e.retries}/2)`;case"failed_permanent":return`Failed: ${e.lastError||"Unknown error"}`;default:return""}}toggleQueue(e=!0){this.ui.panel&&(this.ui.panel.hidden=!e,this.ui.toggle.button.hidden=!e)}setProcessing(e=!0){this.isProcessing=e,this.ui.toggle.button.classList.toggle("saving",e)}mapServerOperation(e){const t=this.queue.get(e.id);if(t&&t.endpoint)return{...t,...e,endpoint:t.endpoint,method:t.method,headers:t.headers};const s=e.type?e.type.replace("_update","").replace("_","/"):"unknown";return{...e,endpoint:s,method:"POST",headers:{...this.headers}}}subscribe(e){if(this.subscribers)return this.subscribers.add(e),()=>this.subscribers.delete(e)}notify(e,t){this.subscribers.forEach((s=>s(e,t)))}destroy(){this.isPolling&&this.stopPolling(),this.stopActivityTracking(),document.removeEventListener("click",this.clickHandler),this.subscribers.clear()}}document.addEventListener("DOMContentLoaded",(async function(){window.auth.subscribe((t=>{"auth-loaded"===t&&(window.jvbQueue=new e)}))}))})();
(()=>{class e{constructor(){this.a11y=window.jvbA11y,this.error=window.jvbError,this.user=window.auth.getUser(),this.canUpdateUI=!0,this.isProcessing=!1,this.isPolling=!1,this.queue=new Map,this.items=new Map,this.subscribers=new Set,this.api=jvbSettings.api,this.endpoint="queue",this.queueItems=new Map,this.init()}init(){this.headers={"X-WP-Nonce":window.auth.getNonce()},this.initElements(),this.initListeners(),this.initStore(),this.canUpdateUI&&this.ui.panel&&(this.popup=new window.jvbPopup({popup:this.ui.panel,toggle:this.ui.toggle.button,name:"Queue Panel"})),this.defineTemplates()}initElements(){this.panelStatuses=["syncing","synced","pending","offline"],this.statuses=["queued","localProcessing","uploading","pending","processing","completed","failed","failed_permanent"],this.pendingStatuses=["queued","localProcessing","uploading"],this.workingStatuses=["pending","processing"],this.completedStatuses=["completed","failed","failed_permanent"],this.icons={queued:"arrows-clockwise",localProcessing:"arrows-clockwise",uploading:"syncing",pending:"cloud",processing:"syncing",completed:"cloud-check",failed:"cloud-warning",failed_permanent:"cloud-warning"},this.selectors={panel:"aside#queue",toggle:{button:"button.qtoggle",indicator:".qtoggle .indicator",count:".qtoggle .count"},refresh:{button:"#queue .refresh .refreshNow",countdown:"#queue .refresh .countdown"},popup:{popup:"#queue .popup",message:"#queue .popup span"},items:{container:"#queue .qitems"},actions:{retry:"#queue .retry-all",clear:"#queue .dismiss-all"},filters:{filter:"#queue [data-filter]",all:{label:'#queue [for="qfilter-all"]',radio:'#queue [data-filter="all"]',count:'#queue [data-filter="all"] .count'},queued:{label:'#queue [for="qfilter-queued"]',input:'#queue [data-filter="queued"]',count:'#queue [for="qfilter-queued"] .count'},localProcessing:{label:'#queue [for="qfilter-localProcessing"]',input:'#queue [data-filter="localProcessing"]',count:'#queue [for="qfilter-localProcessing"] .count'},uploading:{label:'#queue [for="qfilter-uploading"]',input:'#queue [data-filter="uploading"]',count:'#queue [for="qfilter-uploading"] .count'},pending:{label:'#queue [for="qfilter-pending"]',input:'#queue [data-filter="pending"]',count:'#queue [for="qfilter-pending"] .count'},processing:{label:'#queue [for="qfilter-processing"]',input:'#queue [data-filter="processing"]',count:'#queue [for="qfilter-processing"] .count'},completed:{label:'#queue [for="qfilter-completed"]',input:'#queue [data-filter="completed"]',count:'#queue [for="qfilter-completed"] .count'},failed:{label:'#queue [for="qfilter-failed"]',input:'#queue [data-filter="failed"]',count:'#queue [for="qfilter-failed"] .count'}},item:{type:".type",status:".status",details:".info .details",icon:".status .icon",startedAt:".started time",completed:{wrap:".completed",label:".completed span",time:".completed time"},progress:{progress:".progress",fill:".progress .fill",details:".progress .details",icon:".progress .icon"},actions:{cancel:"button.cancel",retry:"button.retry",refresh:"button.refresh",dismiss:"button.dismiss"}}},this.ui=window.uiFromSelectors(this.selectors),this.ui.panel||(this.canUpdateUI=!1)}defineTemplates(){const e=window.jvbTemplates;e.define("emptyState"),e.define("queueItem",{setup({el:e,refs:t,manyRefs:s,data:i}){e.dataset.id=i.id}})}initListeners(){this.activityListeners=null,this.clickHandler=this.handleClick.bind(this),this.onlineHandler=this.handleOnline.bind(this),this.offlineHandler=this.handleOffline.bind(this),this.unloadHandler=this.handleBeforeUnload.bind(this),document.addEventListener("click",this.clickHandler),window.addEventListener("online",this.onlineHandler),window.addEventListener("offline",this.offlineHandler),window.addEventListener("beforeunload",this.unloadHandler)}handleOnline(){this.updatePanel("synced"),this.getQueueByStatus(this.pendingStatuses).length>0&&this.processQueue()}handleOffline(){this.updatePanel("offline")}handleBeforeUnload(e){if(!this.ui.panel)return;return this.getQueueByStatus(this.pendingStatuses).length>0?(e.preventDefault(),e.returnValue="",""):void 0}handleClick(e){if(!window.targetCheck(e,this.selectors.panel+", "+this.selectors.toggle.button))return;if(window.targetCheck(e,this.selectors.refresh.button))return this.ui.refresh.button.classList.add("fetching"),this.store.clearCache(),this.store.clearFilters(),void this.store.fetch().finally((()=>{this.ui.refresh.button.classList.remove("fetching")}));if(window.targetCheck(e,this.selectors.actions.refresh))return void this.handleRefresh(opId);if(window.targetCheck(e,this.selectors.actions.clear))return void this.opActions("completed","dismiss").then((()=>{}));if(window.targetCheck(e,this.selectors.actions.retry))return void this.opActions("failed","retry").then((()=>{}));const t=window.targetCheck(e,"[data-action]");if(t){const e=t.closest("[data-id]")?.dataset.id;return void(e&&this.opActions(e,t.dataset.action))}const s=window.targetCheck(e,this.selectors.filters.filter);s&&this.setFilter(s.dataset.filter)}setFilter(e){Object.values(this.ui.filters).forEach((t=>{t.input?.dataset.filter===e&&(t.input.checked=!0)})),"all"===e?this.store.clearFilters():this.store.setFilter("status",e)}trackActivity(){if(!this.activityListeners){const e=["mousedown","mousemove","keypress","scroll","touchstart"];this.activityListeners=e.map((e=>{const t=()=>this.resetActivityTimer();return document.addEventListener(e,t,{passive:!0}),{event:e,handler:t}}))}this.resetActivityTimer()}resetActivityTimer(){this.activityTimer&&clearTimeout(this.activityTimer),this.activityTimer=setTimeout((()=>{this.processQueue()}),1750)}stopActivityTracking(){this.activityTimer&&(clearTimeout(this.activityTimer),this.activityTimer=null),this.activityListeners&&(this.activityListeners.forEach((({event:e,handler:t})=>{document.removeEventListener(e,t)})),this.activityListeners=null)}initStore(){if(!this.user)return;const e=window.jvbStore.register("queue",{storeName:"queue",keyPath:"id",endpoint:this.endpoint,TTL:5e3,indexes:[{name:"status",keyPath:"status"},{name:"type",keyPath:"type"}],filters:{user:window.auth.getUser()},showLoading:!1});this.store=e.queue,this.store.subscribe(((e,t)=>{switch(e){case"data-loaded":this.store.getAll().forEach((e=>{const t=this.queue.get(e.id),s=this.mapServerOperation(e);this.queue.set(s.id,s),t&&t.status!==s.status&&this.notify("operation-status",s)})),this.maybeStartPolling(),this.updateUI();break;case"items-save":this.maybeStartPolling(),this.updateUI();break;case"item-saved":t.item&&(this.queue.set(t.item.id,t.item),t.previousItem?.status!==t.item.status&&this.notify("operation-status",t.item)),this.maybeStartPolling()}}))}handleRefresh(e){const t=this.getQueue(e);if(!t)return;let s=null;if(s={content_update:t.data?.posts?Object.values(t.data.posts)[0]?.content:null,batch_creation:t.data?.content,image_upload:"uploads",video_upload:"uploads",document_upload:"uploads"}[t.type],s&&window.jvbStore){if(window.jvbStore.stores.get(s)){window.jvbStore.clearCache(s),window.jvbStore.fetch(s);const t=this.items.get(e)?.ui?.actions?.refresh;if(t){const e=t.querySelector("span").textContent;t.querySelector("span").textContent="Refreshed!",t.disabled=!0,setTimeout((()=>{t.querySelector("span").textContent=e,t.disabled=!1}),2e3)}}}else confirm("Refresh the page to see changes?")&&window.location.reload()}addToQueue(e){const t={id:`u${this.user}_${Date.now()}_${Math.random().toString(36).substring(2,9)}`,endpoint:null,method:"POST",headers:{},data:{},delay:!1,canMerge:!0,popup:"Saving changes...",title:"Operation",status:"queued",timestamp:Date.now(),created_at:(new Date).toISOString(),retries:0,user:this.user,...e};if(t.headers={...this.headers,...t.headers},!t.endpoint||!t.data)return null;if(t.popup&&this.ui.popup?.message&&(this.ui.popup.message.textContent=t.popup,this.ui.popup.popup.hidden=!1,setTimeout((()=>this.ui.popup.popup.hidden=!0),2e3)),!t.delay)return this.queue.set(t.id,t),this.processOperation(t).then((()=>{})),this.store.clearCache(),this.maybeStartPolling(),this.toggleQueue(),t.id;const s=Array.from(this.getAllQueue()).filter((e=>"queued"===e.status&&e.endpoint===t.endpoint&&e.canMerge));if(s.length>0){const e=s[0];return e.data=window.deepMerge(e.data,t.data),e.timestamp=Date.now(),this.setQueue(e),this.updateOperationStatus(e.id,e.status),this.updateUI(),this.trackActivity(),e.id}return this.store.clearCache(),this.setQueue(t),this.updateOperationStatus(t.id,t.status),this.updateUI(),this.trackActivity(),t.id}async opActions(e,t){if(this.statuses.includes(e)?e=this.getQueueByStatus(e).map((e=>e.id)):"string"==typeof e&&(e=[e]),0===e.length)return;if(!["cancel","dismiss","retry"].includes(t))return;const s=["cancel","dismiss"].includes(t);s&&e.forEach((e=>{this.removeOperationUI(e)}));try{const i=await fetch(`${this.api}${this.endpoint}`,{method:"POST",headers:{"Content-Type":"application/json",...this.headers},body:JSON.stringify({action:t,ids:Array.isArray(e)?e:[e],user:this.user})});if(!i.ok)throw new Error(`${t} failed: ${i.status}`);const n=await i.json();if(!n.success)throw new Error(n.message||`${t} operation failed`);return e.forEach((e=>{let i=this.getQueue(e);if(i&&this.notify(`${t}-operation`,i),s)this.clearQueue(e);else{let t=this.getQueue(e);t.status="queued",this.setQueue(t),this.updateOperationStatus(t.id,t.status)}})),"retry"===t&&this.trackActivity(),this.updateUI(),n}catch(s){return await window.jvbError.log(s,{component:"Queue",operation:"performQueueAction",action:t,operationIds:e,itemCount:e.length},(()=>this.opActions(e,t))),{success:!1,error:s.message}}}async processQueue(){if(this.isProcessing)return;const e=this.getQueueByStatus("queued");if(0===e.length)return void this.stopActivityTracking();this.setProcessing();for(const t of e)await this.processOperation(t);this.setProcessing(!1);0===this.getQueueByStatus("queued").length?this.stopActivityTracking():this.trackActivity(),this.toggleQueue(this.maybeStartPolling())}async processOperation(e){try{this.queue.has(e.id)||this.queue.set(e.id,e);let t,s=!1;if(e.data?._isFormData&&!e.data instanceof FormData&&(s=!0,e.data=await this.store.objectToFormData(e.data)),this.updateOperationStatus(e.id,"uploading"),e.data instanceof FormData?(e.data.append("id",e.id),e.data.append("user",window.auth.getUser()),t=e.data):(t=JSON.stringify({...e.data,id:e.id,user:window.auth.getUser()}),e.headers["Content-Type"]="application/json"),null==t)return;const i=await fetch(`${this.api}${e.endpoint}`,{method:e.method,headers:e.headers,body:t}),n=await i.json();if(s&&(e.data={}),!i.ok||!n.success)throw new Error(n.message||`HTTP ${i.status}`);n.id&&e.id!==n.id?e=await this.handleServerMerge(e,n):(e.status=n.status??"pending",e.serverData=n,this.updateOperationStatus(e.id,e.status)),this.a11y.announce(`${e.title} sent to server for processing`),this.setQueue(e)}catch(t){console.error("Operation failed: ",t),e.retries++,e.lastError=t.message,e.retries>=3?e.status="failed_permanent":e.status="failed",this.updateOperationStatus(e.id,e.status),this.setQueue(e)}}async handleServerMerge(e,t){const s=this.getQueue(t.id);return s?(e.status=t.status||"pending",e.serverData=t,this.mergeOp(s,e)):(this.clearQueue(e.id),this.setQueue(t),t)}mergeOp(e,t){return e.data=window.deepMerge(e.data,t.data),e.status=t.status,Object.hasOwn(t,"serverData")&&(e.serverData=t.serverData),this.updateOperationStatus(e.id,e.status),this.removeOperationUI(t.id),this.clearQueue(t.id),e}sortByDate(e){return e.sort(((e,t)=>(e.updated_at??e.timestamp??0)-(t.updated_at??t.timestamp??0)))}sortOperations(e){const t={processing:0,uploading:1,pending:2,queued:3,localProcessing:4,failed:5,completed:6,failed_permanent:7};return e.sort(((e,s)=>{const i=(t[e.status]??99)-(t[s.status]??99);if(0!==i)return i;const n=e.updated_at??e.timestamp??0,r=s.updated_at??s.timestamp??0;return new Date(r)-new Date(n)}))}getAllQueue(){let e=[...new Set([...Array.from(this.store.data.values()),...Array.from(this.queue.values())])];return this.sortOperations(e)}getQueueByStatus(e){"string"==typeof e&&(e=[e]);let t=[...new Set([...Array.from(this.store.filterByIndex({status:e})),...Array.from(this.queue.values()).filter((t=>e.includes(t.status)))])];return this.sortOperations(t)}updateOperationStatus(e,t){let s=this.getQueue(e);s&&this.statuses.includes(t)&&(s.status=t,this.notify("operation-status",s),this.setQueue(s))}setQueue(e){this.store.save(e),this.queue.set(e.id,e)}getQueue(e){return this.queue.has(e)?this.queue.get(e):this.store.get(e)}clearQueue(e){this.queue.delete(e),this.store.delete(e)}maybeStartPolling(){return this.getQueueByStatus([...this.pendingStatuses,...this.workingStatuses]).length>0?(this.startPolling(),!0):(this.updatePanel("synced"),!1)}startPolling(){this.isPolling||(this.isPolling=!0,this.updatePanel("pending"),this.runPollCycle())}async runPollCycle(){if(this.isPolling){try{if(this.ui.refresh.button.classList.add("fetching"),this.store.clearCache(),await this.store.fetch(),this.ui.refresh.button.classList.remove("fetching"),!this.maybeStartPolling())return this.stopPolling(),void this.updatePanel("synced")}catch(e){console.error("Polling error:",e)}this.startCountdown(5,(()=>this.runPollCycle()))}}startCountdown(e,t){this.ui.refresh.countdown?(this.ui.refresh.countdown.classList.add("counting"),this.ui.refresh.countdown.textContent=e,this.countdownTimer=setInterval((()=>{--e>0?this.ui.refresh.countdown.textContent=e:(this.stopCountdown(),t&&t())}),1e3)):console.warn("Countdown element not found")}stopPolling(){this.isPolling&&(this.isPolling=!1,this.pollTimer&&(clearInterval(this.pollTimer),this.pollTimer=null),this.stopCountdown())}stopCountdown(){this.countdownTimer&&(clearInterval(this.countdownTimer),this.countdownTimer=null),this.ui.refresh.countdown.classList.remove("counting"),this.ui.refresh.countdown.textContent=""}updateUI(){this.canUpdateUI&&window.debouncer.schedule("queue-ui",this.handleUpdateUI.bind(this))}handleUpdateUI(){const e=this.getAllQueue();this.ui.actions.retry.disabled=0===e.filter((e=>"failed"===e.status)).length,this.ui.actions.clear.disabled=0===e.filter((e=>"completed"===e.status)).length;const t=e.filter((e=>[...this.pendingStatuses,...this.workingStatuses].includes(e.status))).length;this.ui.toggle.count.hidden=0===t,this.ui.toggle.count.textContent=t;for(let t of this.statuses){if("failed_permanent"===t)continue;let s=e.filter((e=>e.status===t)).length;this.ui.filters[t].label.hidden=0===s,this.ui.filters[t].input.dataset.count=`${s}`,this.ui.filters[t].count.textContent=s>0?s:""}this.renderOperations()}renderOperations(){if(!this.ui.items.container)return;const e=this.store.filters?.status??"all",t="all"===e?this.getAllQueue():this.getQueueByStatus(e),s=this.sortOperations(t);if(0===s.length){window.removeChildren(this.ui.items.container);const e=window.jvbTemplates.create("emptyQueue");return this.ui.items.container.append(e),void this.a11y.announce("No items in queue")}this.ui.items.container.querySelector(".empty-group")?.remove();const i=new Set(s.map((e=>e.id)));this.items.forEach(((e,t)=>{i.has(t)||(e.element?.remove(),this.items.delete(t))})),s.forEach(((e,t)=>{let s=this.items.get(e.id);s||(s=this.createOperationElement(e)),s?.element&&(this.updateOperationUI(e.id),this.ui.items.container.append(s.element))}))}createOperationElement(e){const t=window.jvbTemplates.create("queueItem",e),s={element:t,ui:window.uiFromSelectors(this.selectors.item,t)};return this.items.set(e.id,s),s}updateOperationUI(e){let t=this.items.has(e)?this.items.get(e):this.createOperationElement(e);if(!t)return;let s=this.getQueue(e),i=t.element;i.classList.remove(this.statuses),i.classList.add(s.status);let n=this.getProgress(s);t.ui.type&&t.ui.type.textContent!==s.title&&(t.ui.type.textContent=s.title),t.ui.status&&(t.ui.status.title=this.statusLabel(s.status)),t.ui.icon&&(t.ui.icon.className=`icon icon-${this.icons[s.status]}`),t.ui.details&&(t.ui.details.textContent=this.itemMessage(s)),t.ui.startedAt&&(t.ui.startedAt.setAttribute("datetime",s.created_at),t.ui.startedAt.textContent=window.formatTimeAgo(s.created_at));s.status;const r="completed"===s.status&&(s.completed_at||s.updated_at);if(t.ui.completed.wrap.hidden=!r,r){const e=s.completed_at??s.updated_at;t.ui.completed.label.textContent="Completed: ",t.ui.completed.time.setAttribute("datetime",e),t.ui.completed.time.textContent=window.formatTimeAgo(e)}window.showProgress(t.ui.progress,n,100,this.statusLabel(s.status)),t.ui.actions.cancel&&(t.ui.actions.cancel.hidden=this.completedStatuses.includes(s.status)),t.ui.actions.retry&&(s.retries>=3&&(t.ui.actions.retry.disabled=!0),t.ui.actions.retry.hidden="failed"!==s.status),t.ui.actions.dismiss&&(t.ui.actions.dismiss.hidden=this.pendingStatuses.includes(s.status)),t.ui.actions.refresh&&(t.ui.actions.refresh.hidden="completed"!==s.status)}getProgress(e){if(void 0!==e.progress_percentage)return e.progress_percentage;if(void 0!==e.progress)return e.progress;if(!this.statuses.includes(e.status))return 0;return{queued:10,uploading:25,pending:40,processing:70,completed:100,failed:0,failed_permanent:0}[e.status]??0}removeOperationUI(e){let t=this.items.get(e);t&&window.fade(t.element,!1)}updatePanel(e="syncing"){this.ui.panel&&this.panelStatuses.includes(e)&&(this.ui.panel.classList.remove(...this.panelStatuses),this.ui.panel.classList.add(e))}statusLabel(e){if(!this.statuses.includes(e))return"";return{queued:"Queued",localProcessing:"Processing locally",uploading:"Uploading",pending:"Waiting on server",processing:"Processing",completed:"Completed",failed:"Failed",failed_permanent:"Failed permanently",merged:"Merged"}[e]}itemMessage(e){if(Object.hasOwn(e,"message")&&""!==e.message)return e.message;if(Object.hasOwn(e,"error_message")&&e.error_message)return e.error_message;switch(e.status){case"queued":return"Waiting to send...";case"uploading":return"Sending to server...";case"pending":return e.position?`Position ${e.position} in queue`:"In server queue";case"processing":if(e.count&&void 0!==e.progress_count){const t=e.progress_count,s=e.count;return`Processing ${t}/${s} items (${Math.round(t/s*100)}%)`}return void 0!==e.progress_percentage?`${e.progress_percentage}% complete`:"Processing...";case"completed":return"Successfully completed. Refresh to see changes.";case"merged":return e.merged_into?`Merged with another operation (${e.merged_into.substring(0,8)}...)`:"Merged with another operation";case"failed":return`Failed: ${e.lastError||"Unknown error"} (Retry ${e.retries}/2)`;case"failed_permanent":return`Failed: ${e.lastError||"Unknown error"}`;default:return""}}toggleQueue(e=!0){this.ui.panel&&(this.ui.panel.hidden=!e,this.ui.toggle.button.hidden=!e)}setProcessing(e=!0){this.isProcessing=e,this.ui.toggle.button.classList.toggle("saving",e)}mapServerOperation(e){const t=this.queue.get(e.id);if(t&&t.endpoint){const s={...t,...e,endpoint:t.endpoint,method:t.method,headers:t.headers,progress_percentage:e.progress_percentage,progress_count:e.progress_count,count:e.count};e.merged_into&&this.handleMergedOperation(s)}const s=e.type?e.type.replace("_update","").replace("_","/"):"unknown",i={...e,endpoint:s,method:"POST",headers:{...this.headers}};return e.merged_into&&this.handleMergedOperation(i),i}handleMergedOperation(e){e.merged_into&&(console.log(`[Queue] Operation ${e.id} merged into ${e.merged_into}`),setTimeout((()=>{this.store.delete(e.id),this.removeOperationFromUI(e.id)}),3e3))}subscribe(e){if(this.subscribers)return this.subscribers.add(e),()=>this.subscribers.delete(e)}notify(e,t){this.subscribers.forEach((s=>s(e,t)))}destroy(){this.isPolling&&this.stopPolling(),this.stopActivityTracking(),document.removeEventListener("click",this.clickHandler),this.subscribers.clear()}}document.addEventListener("DOMContentLoaded",(async function(){window.auth.subscribe((t=>{"auth-loaded"===t&&(window.jvbQueue=new e)}))}))})();
assets/js/min/uploader.min.js
@@ -1 +1 @@
(()=>{class e{constructor(){this.a11y=window.jvbA11y,this.queue=window.jvbQueue,this.error=window.jvbError,this.templates=window.jvbTemplates,this.subscribers=new Set,this.initStores(),this.initWorker(),this.fields=new Map,this.uploads=new Map,this.groups=new Map,this.selected=new Map,this.selectionHandlers=new Map,this.sortables=new Map,this.changes=new Map,this.previewUrls=new Set,this.initElements(),this.initListeners(),this.defineTemplates()}defineTemplates(){const e=this.templates,t=this;e.define("uploadItem",{refs:{select:'[name="select-item"]',featured:'[name="featured"]',img:"img",video:"video",file:"label > span",details:"details",alt:'[name="image-alt-text"]',title:'[name="image-title"]',description:'[name="image-caption"]'},manyRefs:{inputs:"input, select, textarea"},setup({el:e,refs:s,manyRefs:i,data:r}){let a,o,l,d=!1;switch(Object.hasOwn(r,"file")?(e.dataset.uploadId=r.uploadId,a=t.getSubtypeFromMime(r.file.type)||"image",o="document"!==a&&t.createPreviewUrl(r.file),d=o,l=r.file.name||""):(e.dataset.id=r.id,a=t.getSubtypeFromURL(r.medium??r.src),o=r.medium??r.src,l=r["image-alt-text"]??""),e.dataset.subtype=a,s.featured&&(s.featured.value=r.uploadId),a){case"image":s.img&&(s.img.src=o,s.img.alt=l,d&&(s.img.dataset.previewUrl=d)),s.video&&s.video.remove(),s.file&&s.file.remove();break;case"video":s.video&&(s.video.src=o,s.video.alt=l,d&&(s.video.dataset.previewUrl=d)),s.img&&s.img.remove(),s.file&&s.file.remove();break;case"document":if(s.preview){let e=r.file.name.split(".").pop()?.toLowerCase()??"",t={pdf:"file-pdf",csv:"file-csv",doc:"file-doc",docx:"file-doc",txt:"file-txt",xls:"file-xls",xlsx:"file-xls"},i=window.getIcon(t[e]??"file");s.preview.innerText=r.file.name??r.title,s.preview.prepend(i)}s.img&&s.img.remove(),s.video&&s.video.remove()}if(s.details&&(Object.hasOwn(r,"field")&&Object.hasOwn(r.field,"config")&&Object.hasOwn(r.field.config,"showMeta")&&!r.field.config.showMeta?s.details.remove():(Object.hasOwn(r,"id")?s.details.dataset.attachmentId=r.id:Object.hasOwn(r,"uploadId")&&(s.details.dataset.uploadId=r.uploadId),s.details.setAttribute("data-ignore",""),"image"!==a&&s.alt?s.alt.closest(".field")?.remove():Object.hasOwn(r,"image-alt-text")&&s.alt&&(s.alt.value=r["image-alt-text"]),(Object.hasOwn(r,"title")||Object.hasOwn(r,"file"))&&s.title&&(s.title.value=r.title||r.file.name),Object.hasOwn(r,"image-caption")&&s.description&&(s.description.value=r["image-caption"]))),e.draggable="single"!==e.dataset.mode,i.inputs)for(let t of i.inputs){let s=t.closest("[data-field]")??e;window.prefixInput(t,`${r.id??r.uploadId}-`,s)}}}),e.define("imageGroup",{refs:{selectAll:"[data-select-all]",fields:".fields",details:"details",grid:".item-grid"},setup({el:t,refs:s,manyRefs:i,data:r}){if(t.dataset.groupId=r.groupId,s.selectAll){let e=s.selectAll.closest(".field");window.prefixInput(s.selectAll,`select-all-${r.groupId}`,e,!0)}let a=e.create("groupMetadata",{groupId:r.groupId});a?s.fields.append(a):s.details.remove(),s.grid&&(s.grid.dataset.groupId=r.groupId)}}),e.define("groupMetadata",{manyRefs:{inputs:"input,textarea,select"},setup({el:e,refs:t,manyRefs:s,data:i}){t.inputs&&t.inputs.forEach((e=>{let t=e.closest("[data-field]");window.prefixInput(e,`${i.groupId}-`,t)}))}}),e.define("restoreNotification",{refs:{details:".details",wrap:".wrap"},setup({el:t,refs:s,manyRefs:i,data:r}){if(s.details){let e=r.bySource.size>1?` across ${r.bySource.size} pages`:"",t=r.pendingUploads.length>1?"uploads":"upload";s.details.textContent=`${r.pendingUploads.length} ${t} can be recovered${e}`}if(!s.wrap)return void console.warn("No wrap element in template");let a=1;for(const[t,i]of r.bySource){let r={index:a,isCurrent:t===window.location.href,src:t,uploads:i};s.wrap.append(e.create("restoreField",r)),a++}}}),e.define("restoreField",{refs:{h3:"h3",a:"h3 a",grid:".item-grid"},async setup({el:e,refs:s,manyRefs:i,data:r}){let a=t.registerField(e,!1,!1,`recovery_${r.index}`);r.isCurrent?(e.open=!0,s.a?.remove(),s.h3&&(s.h3.textContent="From this page:")):s.a&&(s.a.href=r.src,s.a.title="Navigate to page and restore",s.a.textContent=r.src);let o=[...new Set(r.uploads.map((e=>e.group??"preview")))];for(let e of o){let i="preview"===e||t.stores.groups.get(e);if(!i)continue;let o=await t.createGroupElement(e,a),l=o.querySelector(".item-grid"),d=r.uploads.filter((t=>t.group===("preview"===e)?null:e));for(const[e,t]of Object.entries(i.fields??{})){let s=o.querySelector(`input[name*="${e}"]`);s&&(s.value=t)}for(let e of d){let s=await t.createUpload(e.id,t.formatFile(e),a);l.append(s)}s.grid.append(o)}}})}initStores(){const{uploads:e,groups:t}=window.jvbStore.register("uploads",[{storeName:"uploads",keyPath:"id",indexes:[{name:"field",keyPath:"field"},{name:"status",keyPath:"status"},{name:"group",keyPath:"group"},{name:"src",keyPath:"src"}]},{storeName:"groups",keyPath:"id",indexes:[{name:"field",keyPath:"field"},{name:"src",keyPath:"src"}]}]);this.stores={uploads:e,groups:t,ready:[]},this.stores.uploads.subscribe(this.handleStores.bind(this,"uploads")),this.stores.groups.subscribe(this.handleStores.bind(this,"groups")),this.queue.subscribe(((e,t)=>{if(("operation-status"===e||"cancel-operation"===e)&&["image_upload","video_upload","document_upload"].includes(t.type)){let s=(t.data instanceof FormData?this.stores.uploads.formDataToObject(t.data):t.data).upload_ids;if(!s||0===s.length)return;if("cancel-operation"===e)return this.handleOperationCancelled(s);this.setBulkUpload(s,"status",t.status).then((()=>{})),"completed"===t.status&&s.forEach((e=>{this.removeUpload(e).then((()=>{}))}))}}))}storesReady(){return 2===this.stores.ready.length}handleStores(e,t){"data-ready"===t&&(this.stores.ready.push(e),this.storesReady()&&this.checkRecovery().then((()=>{})))}initWorker(){this.worker=null,this.workerState={worker:null,tasks:new Map,restart:{count:0,max:3},settings:{timeout:3e3,maxConcurrent:3,restartAfterTimeout:!0}}}initElements(){this.selectors={fields:{field:"[data-upload-field]",input:'input[type="file"]',dropZone:".file-upload-container",preview:".preview-wrap",grid:".item-grid.preview",progress:{progress:".file-upload-container .progress",fill:".file-upload-container .progress .fill",details:".file-upload-container .progress .details",icon:".file-upload-container .progress .icon"},selectAll:"[data-select-all]",actions:".selection-actions",count:".selected .info",hidden:'input[type="hidden"]'},groups:{container:".group-display",grid:".item-grid.groups",empty:".empty-group",header:".sidebar .header"},group:{item:".upload-group",actions:".selection-actions",selectAll:'[name="select-all-group"]',count:".group-header .info",fields:"details .fields",grid:".item-grid.group",total:".group-content .group-count"},items:{item:".item.upload",checkbox:'[name="select-item"]',featured:'[name="featured"]',image:"img",details:"details",progress:{progress:".progress",fill:".fill",details:".details",icon:".icon"}}}}initListeners(){this.clickHandler=this.handleClick.bind(this),this.changeHandler=this.handleChange.bind(this),this.dragEnterHandler=this.handleDragEnter.bind(this),this.dragLeaveHandler=this.handleDragLeave.bind(this),this.dragOverHandler=this.handleDragOver.bind(this),this.dropHandler=this.handleDrop.bind(this),document.addEventListener("click",this.clickHandler),document.addEventListener("change",this.changeHandler),document.addEventListener("dragenter",this.dragEnterHandler),document.addEventListener("dragleave",this.dragLeaveHandler),document.addEventListener("dragover",this.dragOverHandler),document.addEventListener("drop",this.dropHandler),window.addEventListener("beforeunload",(()=>{this.cleanupAllPreviewUrls()}))}async setUpload(e,t){const s={...{id:e,attachment:null,group:null,field:null,src:window.location.href,blob:null,status:"local_processing",operationId:null,fields:{}},...t};return Object.preventExtensions(s),await this.stores.uploads.save(s),s}createPreviewUrl(e){const t=URL.createObjectURL(e);return this.previewUrls.add(t),t}revokePreviewUrl(e){e?.startsWith("blob:")&&(URL.revokeObjectURL(e),this.previewUrls.delete(e))}formatFile(e){return e.blob?new File([e.blob],e.fields.originalName||"file",{type:e.fields.type||e.blob.type,lastModified:e.fields.lastModified||Date.now()}):null}handleClick(e){let t=window.targetCheck(e,this.selectors.fields.dropZone);t&&!e.target.matches("input, button, a")&&t.querySelector(this.selectors.fields.input)?.click();const s=window.targetCheck(e,"[data-action]");s&&this.handleAction(s)}handleAction(e){const t=e.dataset.action,s=this.getFieldIdFromElement(e);switch(t){case"add-to-group":this.handleAddToGroup(s).then((()=>{}));break;case"delete-group":this.handleDeleteGroup(e);break;case"delete-upload":case"remove-from-group":this.handleRemoveItem(e).then((()=>{}));break;case"upload":this.queueUploads("uploads/groups",s).then((()=>{}));break;case"restore":this.handleRestoreSelected().then((()=>{}));break;case"restore-all":this.handleRestoreAll().then((()=>{}));break;case"clear-cache":this.handleClearCache().then((()=>{}))}}handleChange(e){let t=this.getFieldIdFromElement(e.target);if(t)if(e.target.matches(this.selectors.fields.input)){const s=Array.from(e.target.files);s.length>0&&this.processFiles(t,s).then((()=>{}))}else e.target.matches(this.selectors.items.checkbox)||e.target.matches(this.selectors.items.featured)||e.target.matches('[name*="select-"]')||("post_group"===this.fields.get(t).config.destination?this.handleGroupMetaChange(e.target):this.queueUploadMeta(e));else{e.target.closest("[data-upload-id], [data-attachment-id]")&&this.queueUploadMeta(e)}}handleGroupMetaChange(e){const t=e.dataset.groupId;if(!t)return;const s=e.name;if(!s)return;const i=e.value,r=s.replace(`${t}[`,"").replace(`${t}_`,"").replace("]","");window.debouncer.schedule(`group-meta-${t}-${r}`,(async()=>{const e=this.stores.groups.get(t);e&&(e.fields||(e.fields={}),e.fields[r]=i,await this.setGroup(t,e))}),300)}handleDragEnter(e){if(!e.dataTransfer.types.includes("Files"))return;const t=e.target.closest(this.selectors.fields.dropZone);t&&(e.preventDefault(),t.classList.add("dragover"))}handleDragLeave(e){const t=e.target.closest(this.selectors.fields.dropZone);t&&!t.contains(e.relatedTarget)&&t.classList.remove("dragover")}handleDragOver(e){if(!e.dataTransfer.types.includes("Files"))return;e.target.closest(this.selectors.fields.dropZone)&&(e.preventDefault(),e.dataTransfer.dropEffect="copy")}handleDrop(e){const t=e.target.closest(this.selectors.fields.dropZone);if(!t)return;e.preventDefault(),t.classList.remove("dragover"),t.classList.add("uploading");const s=Array.from(e.dataTransfer.files);if(0===s.length)return;const i=this.getFieldIdFromElement(t);i&&(this.processFiles(i,s).then((()=>{this.updateHandlerItems(i)})),this.a11y.announce(`${s.length} file(s) dropped for upload`))}async queueUploads(e,t){let s=new FormData;const i=this.fields.get(t);if(!i)return;let r=this.stores.uploads.filterByIndex({field:t});if(0===r.length)return;const[a,o]=["uploads"===e,"uploads/groups"===e];let l,d,n,u,p;s.append("fieldId",i.id),s.append("content",i.config.content),a&&(s.append("mode",i.config.mode),s.append("field_name",i.config.name),s.append("fieldId",i.id),s.append("field_type",i.config.type),s.append("subtype",i.config.subtype),s.append("item_id",i.config.itemID),s.append("destination",i.config.destination)),o?({posts:l,uploadMap:d,files:n}=this.collectGroups(t)):a&&({uploadMap:d,files:n}=this.collectUploads(t)),o&&s.append("posts",JSON.stringify(l)),n.forEach((e=>{s.append("files[]",e)})),s.append("upload_ids",JSON.stringify(d)),a?(u=`Uploading ${r.length} file${r.length>1?"s":""} to server...`,p=`Uploading ${r.length} file${r.length>1?"s":""}...`):o&&(u=`Creating ${l.length} ${i.config.content}${l.length>1?"s":""} from uploads...`,p=`Creating ${l.length} post${l.length>1?"s":""}...`),await this.setBulkUpload(r,"status","queued");let c=this.sendToQueue(e,s,u,p);if("uploads/groups"===e){let e=i.element.closest("details");e&&(e.open=!1)}return c?(i.operationId=c,await this.setBulkUpload(r,"operationId",c),await this.setBulkUpload(r,"status","uploading"),await this.setBulkGroup(t,"operationId",c),this.fields.set(i.id,i),this.notify("sent-to-queue",{field:i,operation:c})):await this.setBulkUpload(r,"status","failed"),c}async sendToQueue(e,t,s="",i="",r=!1){""===i&&(i=s);const a={endpoint:e,method:"POST",data:t,title:s,popup:i,canMerge:r,sendNow:"uploads/groups"===e,headers:{action_nonce:window.auth.getNonce("dash")},append:"_upload"};try{return await this.queue.addToQueue(a)}catch(e){return this.error.log(e,{component:"UploadManager",action:"sentToQueue"}),!1}}collectGroups(e){let t=this.stores.uploads.filterByIndex({field:e}),s=[],i=[],r=[];const a=this.stores.groups.filterByIndex({field:e}).filter((e=>{const t=this.getGroupUploadsInOrder(e);return t.length>0&&t.some((e=>this.formatFile(e)))}));for(const e of a){const t=this.groups.get(e.id)?.element,a={images:[],fields:this.collectGroupFieldsFromDOM(t,e.id)},o=this.getGroupUploadsInOrder(e);for(const t of o){const s=this.formatFile(t);if(s){r.push(s);const o={upload_id:t.id,index:i.length},l=this.uploads.get(t.id),d=l?.element?.querySelector(`input[name="${e.id}_featured"]`);d?.checked&&(a.fields.featured=t.id),a.images.push(o),i.push(t.id)}}a.images.length>0&&s.push(a)}const o=t.filter((e=>!e.group));for(const e of o){const t={images:[],fields:{}},a=this.formatFile(e);if(a){r.push(a);const s={upload_id:e.id,index:i.length};t.images.push(s),i.push(e.id)}t.images.length>0&&s.push(t)}return{posts:s,uploadMap:i,files:r}}getGroupUploadsInOrder(e){return e.uploads&&0!==e.uploads.length?e.uploads.map((e=>this.stores.uploads.get(e))).filter(Boolean):[]}collectGroupFieldsFromDOM(e,t){if(!e)return{};const s={};return e.querySelectorAll("input, textarea, select").forEach((e=>{const i=e.name.replace(`${t}[`,"").replace(`${t}_`,"").replace("]","");["featured","select-all"].some((e=>i.includes(e)))||e.value&&(s[i]=e.value)})),s}collectUploads(e){let t=this.stores.uploads.filterByIndex({field:e});if(0===t.length)return;let s=[],i=[];for(const e of t){const t=this.formatFile(e);t&&(i.push(t),s.push(e.id))}return{uploadMap:s,files:i}}queueUploadMeta(e){let t=e.target.closest("[data-attachment-id]")?.dataset.attachmentId,s=!1;if(!t&&(t=e.target.closest("[data-upload-id]")?.dataset.uploadId,s=!0,!t))return;if(!this.changes.has(t)){let e={};s?e.uploadId=t:e.attachmentId=t,this.changes.set(t,e)}let i=e.target.closest("[data-field]").dataset.field;this.changes.get(t)[i]=e.target.value,this.scheduleSave()}scheduleSave(){window.debouncer.schedule("upload-meta",(async()=>{if(this.changes.size>0){let e={};for(let[t,s]of this.changes.entries())console.log(t,s),e[t]=s;let t={user:window.auth.getUser(),items:e};await this.sendToQueue("uploads/meta",t,"Uploading Meta","Uploading Meta",!0),this.changes.clear()}}),2e3)}scanFields(e,t=!0,s=!0){e.querySelectorAll(this.selectors.fields.field).forEach((e=>this.registerField(e,t,s)))}registerField(e,t=!0,s=!0,i=null){const r={element:e,id:i||this.determineFieldId(e),config:this.extractFieldConfig(e,t,s),uploads:new Set,operationId:null,groups:[],ui:window.uiFromSelectors(this.selectors.fields,e),groupUI:window.uiFromSelectors(this.selectors.groups,e)};return this.fields.set(r.id,r),e.dataset.uploader=r.id,this.getSelectionHandler(r.id),"single"!==r.config.type&&this.initSortable(r.id),r.id}extractFieldConfig(e,t,s){return{autoUpload:t,showMeta:s,destination:e.dataset.destination||"meta",content:this.extractFieldContent(e),mode:e.dataset.mode||"direct",type:e.dataset.type||"single",name:e.dataset.field,itemID:this.extractFieldItemId(e)??0,maxFiles:parseInt(e.dataset.maxFiles)??25,subType:e.dataset.subtype??"image"}}extractFieldContent(e){return e.dataset.content||e.closest("dialog")?.dataset.content||e.closest("form")?.dataset.save||null}extractFieldItemId(e){return e.dataset.itemId||e.closest("dialog")?.dataset.itemId||null}determineFieldId(e){let t=this.extractFieldContent(e);t=null===t?"":t+"_";let s=this.extractFieldItemId(e);s=null===s?"":s+"_";return`${t}${s}${e.dataset.field||""}`}getFieldIdFromElement(e){const t=e.closest(this.selectors.fields.field);return t?.dataset.uploader||null}updateFieldProgress(e,t,s,i){const r=this.fields.get(e);r&&window.showProgress(r.ui.progress,t,s,i)}getWorker(){return this.workerState.worker||"undefined"==typeof OffscreenCanvas||(this.workerState.worker=new Worker("worker.js"),this.workerState.worker.onmessage=e=>this.handleWorkerMessage(e),this.workerState.worker.onerror=e=>this.handleWorkerError(e)),this.workerState.worker}handleWorkerMessage(e){const{id:t,blob:s}=e.data,i=this.workerState.tasks.get(t);i&&(clearTimeout(i.timeoutId),i.resolve(s),this.workerState.tasks.delete(t))}handleWorkerError(e){this.workerState.tasks.forEach((t=>{clearTimeout(t.timeoutId),t.reject(e)})),this.workerState.tasks.clear(),this.restartWorker()}restartWorker(){this.workerState.worker&&(this.workerState.worker.terminate(),this.workerState.worker=null),this.workerState.restart.count++}async processImages(e,t=2200,s=2200){const i=[],r=[...e],a=this.workerState.settings.maxConcurrent,o=async()=>{for(;r.length>0;){const e=r.shift(),a=await this.processImage(e.file,t,s);i.push({uploadId:e.uploadId,blob:a})}};return await Promise.all(Array.from({length:Math.min(a,e.length)},(()=>o()))),i}async processImage(e,t=2200,s=2200,i=3e3){if("undefined"==typeof OffscreenCanvas)return this.resizeImage(e,t,s);try{return await this.withTimeout(this.workerImage(e,t,s),i)}catch(i){return this.resizeImage(e,t,s)}}withTimeout(e,t){return Promise.race([e,new Promise(((e,s)=>setTimeout((()=>s(new Error("Timeout"))),t)))])}async workerImage(e,t=2200,s=2200){const{settings:i,restart:r}=this.workerState;if(r.count>=r.max)throw new Error("Worker max restarts exceeded");const a=await createImageBitmap(e);let{width:o,height:l}=a;if(o>t||l>s){const e=Math.min(t/o,s/l);o=Math.round(o*e),l=Math.round(l*e)}const d=this.getWorker(),n=crypto.randomUUID();return new Promise(((t,s)=>{const r=setTimeout((()=>{this.workerState.tasks.delete(n),i.restartAfterTimeout&&this.restartWorker(),s(new Error("Timeout"))}),i.timeout);this.workerState.tasks.set(n,{resolve:t,reject:s,timeoutId:r}),d.postMessage({id:n,imageBitmap:a,width:o,height:l,type:e.type,quality:.9},[a])}))}resizeImage(e,t,s){return new Promise((i=>{const r=new Image;r.onload=()=>{URL.revokeObjectURL(r.src);let{width:a,height:o}=r;if(a>t||o>s){const e=Math.min(t/a,s/o);a=Math.round(a*e),o=Math.round(o*e)}const l=document.createElement("canvas");l.width=a,l.height=o,l.getContext("2d").drawImage(r,0,0,a,o),l.toBlob(i,e.type,.9)},r.src=URL.createObjectURL(e)}))}async processFiles(e,t){let s=this.fields.get(e);if(!s)return;s.groupUI.container&&(s.groupUI.container.hidden=!1);const i=t.length;let r=0;this.updateFieldProgress(e,0,i,"Processing files...");const a=await Promise.all(t.map((async t=>{const s=window.generateID("upload"),i=await this.setUpload(s,{id:s,field:e,status:"local_processing",fields:{originalName:t.name,originalSize:t.size,type:t.type,lastModified:t.lastModified}}),r=await this.createUpload(s,t,e);return this.uploads.set(s,{element:r,ui:window.uiFromSelectors(this.selectors.items,r)}),await this.addToGroup(s,null),{uploadId:s,upload:i,file:t}}))),o=a.filter((e=>e.file.type.startsWith("image/"))),l=a.filter((e=>!e.file.type.startsWith("image/"))),d=await this.processImages(o.map((e=>({file:e.file,uploadId:e.uploadId}))));for(const{uploadId:t,blob:s}of d){const a=o.find((e=>e.uploadId===t));a&&(a.upload.blob=s,a.upload.fields.size=s.size,a.upload.status="queued",await this.setUpload(t,a.upload),r++,this.updateFieldProgress(e,r,i,"Processing files..."))}for(const{uploadId:t,upload:s,file:a}of l)s.blob=a,s.status="queued",await this.setUpload(t,s),r++,this.updateFieldProgress(e,r,i,"Processing files...");this.maybeLockUploads(e),s.config.autoUpload&&"post_group"!==s.config.destination&&await this.queueUploads("uploads",e)}async checkRecovery(){const e=this.stores.uploads.filterByIndex({status:["local_processing","queued","uploading"]}),t=Array.from(this.stores.groups.data.values());for(const e of t){this.stores.uploads.filterByIndex({group:e.id}).length>0||await this.stores.groups.delete(e.id)}if(0===e.length)return;const s=new Map;e.forEach((e=>{const t=e.src||"unknown";s.has(t)||s.set(t,[]),s.get(t).push(e)}));let i={bySource:s,pendingUploads:e};document.body.append(this.templates.create("restoreNotification",i));let r=document.querySelector("dialog.restore-uploads");this.restoreModal=new window.jvbModal(r),this.restoreSelection=new window.jvbHandleSelection(r,{wrapper:{wrapper:".restore-field",id:"selection"},items:".item-grid.restore",selectAll:{bulkControls:".selection-actions",checkbox:"#select-all-restore",count:".selection-count"}}),this.restoreModal.handleOpen()}async handleRestoreSelected(){if(!this.restoreSelection)return;let e=Array.from(this.restoreSelection.selectedItems);0!==e.length&&await this.restoreSelectedUploads(e)}async handleRestoreAll(){if(!this.restoreModal)return;const e=Array.from(this.restoreModal.modal.querySelectorAll(".item.upload")).map((e=>e.dataset.uploadId));await this.restoreSelectedUploads(e)}async restoreSelectedUploads(e){let t=window.location.href,s=Array.from(this.stores.uploads.data.values()).filter((s=>e.includes(s.id)&&s.src===t)),i=[...new Set(s.map((e=>e.group)))].filter(Boolean),r=s[0].field;if(!document.querySelector(`[data-uploader="${r}"]`))return void console.log("No field found for "+r);let a=this.fields.get(r);a.groupUI.container&&(a.groupUI.container.hidden=!1);let o=[];for(let e of i){let t=this.stores.groups.get(e);await this.createGroup(r,e);let i=this.groups.get(e),a=s.filter((t=>t.group===e));if(t&&this.groups.has(e)){let e=t.fields;for(const[t,s]of Object.entries(e)){let e=i.element.querySelector(`input[name*="${t}"]`);e&&(e.value=s)}}else e=null;for(let t of a){let s=await this.createUpload(t.id,this.formatFile(t),r);this.uploads.set(t.id,{element:s,ui:window.uiFromSelectors(this.selectors.items,s)}),await this.addToGroup(t.id,e),o.push(t.id)}}let l=s.filter((e=>!o.includes(e.id)));for(let e of l){let t=await this.createUpload(e.id,this.formatFile(e),r);this.uploads.set(e.id,{element:t,ui:window.uiFromSelectors(this.selectors.items,t)}),await this.addToGroup(e.id,null)}this.cleanupRestore()}cleanupRestore(){this.restoreModal.handleClose(),this.restoreSelection.destroy(),this.restoreSelection=null,this.restoreModal.destroy(),this.restoreModal.modal.remove(),this.restoreModal=null}getStatusText(e){return{received:"Image Received",local_processing:"Processing Image...",queued:"Waiting to upload...",uploading:"Uploading to Server",pending:"Successfully sent to server. In line for further processing.",processing:"Processing on server...",completed:"Upload complete!",failed:"Upload failed (will retry)",failed_permanent:"Upload failed permanently"}[e]||e}getStatusProgress(e){return{local_processing:28,queued:50,uploading:66,pending:75,processing:89,completed:100}[e]??0}async createUpload(e,t,s){let i=this.fields.get(s);if(!i)return null;let r={uploadId:e,file:t,field:i};return this.templates.create("uploadItem",r)}getSubtypeFromURL(e){const t=e.split("?")[0].toLowerCase();return[".webp",".jpg",".jpeg",".png",".gif",".svg"].some((e=>t.endsWith(e)))?"image":[".mp4",".ogg",".mov",".webm",".avi"].some((e=>t.endsWith(e)))?"video":"document"}getSubtypeFromMime(e){return e.startsWith("image/")?"image":e.startsWith("video/")?"video":"document"}async handleRemoveItem(e){const t=e.closest(this.selectors.items.item);if(!t)return;const s=t.dataset.uploadId;confirm("Remove this item?")&&(await this.removeUpload(s),this.a11y.announce("Item removed"))}async setBulkUpload(e,t,s){const i=Array.from(e).map((async e=>{if("string"==typeof e&&(e=await this.stores.uploads.get(e)),e)return"status"===t&&await this.setUploadStatus(e,s),e[t]=s,this.stores.uploads.save(e)}));await Promise.all(i)}async setUploadStatus(e,t){"string"==typeof e&&(e=await this.stores.uploads.get(e)),e&&e.progress&&window.showProgress(e.progress,this.getStatusProgress(t),100,this.getStatusText(t),this.queue.icons[t]??"")}async removeUpload(e){let t=this.stores.uploads.get(e);if(!t)return;if(t.group){let s=this.stores.groups.get(t.group);s.uploads=s.uploads.filter((t=>t!==e)),0===s.uploads.length?await this.removeGroup(s.id,!1):await this.stores.groups.save(s)}await this.clearUpload(e),this.maybeLockUploads(t.field);let s=this.selectionHandlers.get(t.field);s&&s.deselect(e),this.a11y.announce("Upload removed")}async clearUpload(e){const t=this.uploads.get(e);if(t&&(this.revokePreviewUrl(t.preview),t.element)){const e=t.element.dataset.previewUrl;this.revokePreviewUrl(e),t.element.remove()}this.uploads.delete(e),await this.stores.uploads.delete(e)}async handleAddToGroup(e){const t=this.selected.get(e);if(!t||0===t.size)return;let s=await this.createGroup(e);s&&(await Promise.all(Array.from(t).map((e=>this.addToGroup(e,s)))),this.selectionHandlers.get(e)?.clearSelection(),this.a11y.announce(`Created group with ${t.size} items`))}async createGroup(e,t=null){let s=this.fields.get(e);if(!s)return;t||(t=window.generateID("group"));const i=this.createGroupElement(t,e);if(!i)return null;const r=s.groupUI.empty;r?.nextSibling?s.groupUI.grid.insertBefore(i,r.nextSibling):s.groupUI.grid.append(i);const a=i.querySelector(".item-grid");a&&(a.dataset.groupId=t,this.createSortable(e,a,t));let o=this.stores.groups.data.has(t)?this.stores.groups.data.get(t):{};return await this.setGroup(t,{...o,id:t,field:e}),t}createGroupElement(e,t=null){let s={groupId:e,fieldId:t},i=this.templates.create("imageGroup",s);return this.groups.set(e,{element:i,ui:window.uiFromSelectors(this.selectors.group,i)}),this.getSelectionHandler(t)?.addWrapper(i),i}async setGroup(e,t){const s={...{id:e,src:window.location.href,uploads:[],operationId:null,field:null,fields:{}},...t};Object.preventExtensions(s),await this.stores.groups.save(s)}async setBulkGroup(e,t,s){let i=this.stores.groups.filterByIndex({field:e});if(0===i.length)return;let r=i.map((e=>{e[t]=s,this.stores.groups.save(e)}));await Promise.all(r)}async addToGroup(e,t=null){const s=this.stores.uploads.get(e),i=this.uploads.get(e);if(!s||!i)return;const r=this.fields.get(s.field);if(!r)return;if(null!==i.element?.parentElement&&(!t&&null===s.group||t===s.group))return void this.handleReorder(s.field,t);if(s.group){const t=this.stores.groups.get(s.group);t&&(t.uploads=t.uploads.filter((t=>t!==e)),0===t.uploads.length?await this.removeGroup(t.id,!1):await this.stores.groups.save(t))}i.ui.checkbox&&(i.ui.checkbox.checked=!1);const a=this.selectionHandlers.get(s.field);if(a&&a.isSelected(e)&&a.deselect(e),this.selected.get(s.field)?.has(e)&&this.selected.get(s.field).delete(e),i.ui.featured&&(i.ui.featured.hidden=!t),t){i.ui.featured&&(i.ui.featured.name=`${t}_featured`);let r=this.stores.groups.get(t);r&&(r.uploads.push(e),s.group=t,await this.stores.groups.save(r))}else s.group=null;let o=t?this.groups.get(t)?.ui.grid:r.ui.grid;o&&(o.append(i.element),t&&await this.handleReorder(s.field,t)),await this.stores.uploads.save(s)}handleDeleteGroup(e){const t=e.closest(this.selectors.group.item);if(!t)return;let s=t.dataset.groupId;if(!confirm("Delete this group? Items will be moved back to the upload area."))return;let i=this.stores.uploads.filterByIndex({group:s});Promise.all(i.map((e=>this.addToGroup(e.id,null)))).then((()=>{this.removeGroup(s,!1).then((()=>{})),this.a11y.announce("Group deleted. Items returned to upload area")}))}async removeGroup(e,t=!0){let s=this.groups.get(e),i=this.stores.groups.get(e);if(!i)return;let r=!0;t&&i.uploads.length>0&&(r=window.confirm("Keep uploads in this group?")),await Promise.all(i.uploads.map((e=>r?this.addToGroup(e,null):this.removeUpload(e))));if(this.fields.get(i.field)){const t=this.getGroupKey(i.field,e),r=this.selectionHandlers.get(t);r?.destroy&&r.destroy(),this.selectionHandlers.get(i.field)?.removeWrapper(s.element);const a=this.sortables.get(t);a?.destroy&&a.destroy(),this.sortables.delete(t)}s?.element&&s.element.remove(),this.groups.delete(e),await this.stores.groups.delete(e),this.a11y.announce("Group removed")}maybeLockUploads(e){let t=this.fields.get(e);if(!t||!t.ui.dropZone)return;let s=this.stores.uploads.filterByIndex({field:e}).length,i=t.config.maxFiles??25;t.ui.dropZone.hidden=s>=i}async handleOperationCancelled(e){0!==e.length&&e.forEach((e=>{this.removeUpload(e)}))}getGroupKey(e,t=null){return t?`${e}_${t}`:`${e}`}getSelectionHandler(e){let t=this.getGroupKey(e);if(!this.selectionHandlers.has(t)){let s=this.fields.get(e);if(!s)return;if("post_group"!==s.config.destination)return;let i=new window.jvbHandleSelection(s.element,{selectAll:{checkbox:this.selectors.fields.selectAll,count:this.selectors.fields.count,bulkControls:this.selectors.fields.actions},item:{item:this.selectors.items.item,checkbox:this.selectors.items.checkbox,idAttribute:"uploadId"},wrapper:{wrapper:".preview-wrap, .upload-group",id:"groupId"}});i.subscribe(((t,s)=>{this.selected.set(e,s.selectedItems)})),this.selectionHandlers.set(t,i)}return this.selectionHandlers.get(t)}updateHandlerItems(e){let t=this.getSelectionHandler(e);t&&t.collectItems()}initSortable(e){if(!window.Sortable)return;const t=this.fields.get(e);t&&(!Sortable._multiDragMounted&&Sortable.MultiDrag&&(Sortable.mount(new Sortable.MultiDrag),Sortable._multiDragMounted=!0),this.createSortable(e,t.ui.grid,null),this.initEmptyGroupDropZone(e))}createSortable(e,t,s){if(!t)return null;const i=this.getGroupKey(e,s);if(this.sortables.has(i))return this.sortables.get(i);const r=new Sortable(t,{animation:150,draggable:".item",multiDrag:!0,selectedClass:"selected",avoidImplicitDeselect:!0,group:{name:e,pull:!0,put:!0},dragClass:"dragging",ignore:".empty-group",onStart:t=>{const s=t.item,i=s?.dataset.uploadId,r=this.selected.get(e);if(i&&(!r||!r.has(i))){const t=this.selectionHandlers.get(e);t&&t.select(i)}},onEnd:t=>this.sortableDrop(t,e)});return this.sortables.set(i,r),r}initEmptyGroupDropZone(e){const t=this.fields.get(e),s=t?.groupUI?.empty;s&&(s.addEventListener("dragover",(e=>{e.preventDefault(),e.stopPropagation(),e.dataTransfer.dropEffect="move",s.classList.add("drag-over")})),s.addEventListener("dragleave",(e=>{s.contains(e.relatedTarget)||s.classList.remove("drag-over")})),s.addEventListener("drop",(async t=>{t.preventDefault(),t.stopPropagation(),s.classList.remove("drag-over");const i=this.selected.get(e);if(!i||0===i.size)return;const r=await this.createGroup(e);r&&(await Promise.all(Array.from(i).map((e=>this.addToGroup(e,r)))),this.selectionHandlers.get(e)?.clearSelection())})))}async sortableDrop(e,t){const s=e.to,i=(e.items?.length>0?Array.from(e.items):[e.item]).map((e=>e.dataset.uploadId)).filter(Boolean);if(0===i.length)return;const r=s.dataset.groupId||null;for(const e of i)await this.addToGroup(e,r);await this.handleReorder(t,r),this.selectionHandlers.get(t)?.clearSelection()}handleReorder(e,t=null){let s=t?this.groups.get(t)?.ui.grid:this.fields.get(e)?.ui.grid;if(!s)return void console.log("Couldn't Reorder items...");let i=Array.from(s.children).filter((e=>e.matches(this.selectors.items.item)&&!e.classList.contains("ghost"))).map((e=>e.dataset.uploadId)).filter((e=>e));if(t){let e=this.stores.groups.get(t);e&&(e.uploads=i,this.stores.groups.save(e).then((()=>{})))}else{let t=this.fields.get(e)?.ui.hidden;t&&(t.value=i.join(","))}this.a11y.announce("Items reordered")}subscribe(e){return this.subscribers.add(e),()=>this.subscribers.delete(e)}notify(e,t={}){this.subscribers.forEach((s=>{try{s(e,t)}catch(e){console.error("Subscriber error:",e)}}))}destroy(){this.subscribers.clear(),this.previewUrls.forEach((e=>{this.revokePreviewUrl(e)})),this.previewUrls.clear()}cleanupAllPreviewUrls(){this.previewUrls.forEach((e=>this.revokePreviewUrl(e))),this.previewUrls.clear()}async handleClearCache(){const e=window.location.href,t=this.stores.uploads.filterByIndex({src:e}),s=this.stores.groups.filterByIndex({src:e});await Promise.all([...t.map((e=>this.clearUpload(e.id))),...s.map((e=>(this.groups.get(e.id)?.element?.remove(),this.groups.delete(e.id),this.stores.groups.delete(e.id))))]),this.restoreModal&&this.cleanupRestore(),this.a11y.announce("Cache cleared for this page")}async getFilesForForm(e){const t=e.querySelectorAll(this.selectors.fields.field),s=[];for(const e of t){const t=this.determineFieldId(e),i=e.dataset.field,r=this.stores.uploads.filterByIndex({field:t});for(const e of r){const t=this.formatFile(e);t&&s.push({file:t,fieldName:i,uploadId:e.id,meta:e.fields||{}})}}return s}async clearFieldFromStores(e){const t=this.stores.uploads.filterByIndex({field:e}),s=this.stores.groups.filterByIndex({field:e});await Promise.all(t.map((e=>this.clearUpload(e.id)))),await Promise.all(s.map((e=>(this.groups.get(e.id)?.element?.remove(),this.groups.delete(e.id),this.stores.groups.delete(e.id)))))}}document.addEventListener("DOMContentLoaded",(async function(){window.auth.subscribe((t=>{"auth-loaded"===t&&(window.jvbUploads=new e)}))}))})();
(()=>{class e{constructor(){this.a11y=window.jvbA11y,this.queue=window.jvbQueue,this.error=window.jvbError,this.templates=window.jvbTemplates,this.subscribers=new Set,this.initStores(),this.initWorker(),this.fields=new Map,this.uploads=new Map,this.groups=new Map,this.selected=new Map,this.selectionHandlers=new Map,this.sortables=new Map,this.changes=new Map,this.previewUrls=new Set,this.initElements(),this.initListeners(),this.defineTemplates()}defineTemplates(){const e=this.templates,t=this;e.define("uploadItem",{refs:{select:'[name="select-item"]',featured:'[name="featured"]',img:"img",video:"video",file:"label > span",details:"details",alt:'[name="image-alt-text"]',title:'[name="image-title"]',description:'[name="image-caption"]'},manyRefs:{inputs:"input, select, textarea"},setup({el:e,refs:s,manyRefs:i,data:r}){let a,o,l,d=!1;switch(Object.hasOwn(r,"file")?(e.dataset.uploadId=r.uploadId,a=t.getSubtypeFromMime(r.file.type)||"image",o="document"!==a&&t.createPreviewUrl(r.file),d=o,l=r.file.name||""):(e.dataset.id=r.id,a=t.getSubtypeFromURL(r.medium??r.src),o=r.medium??r.src,l=r["image-alt-text"]??""),e.dataset.subtype=a,s.featured&&(s.featured.value=r.uploadId),a){case"image":s.img&&(s.img.src=o,s.img.alt=l,d&&(s.img.dataset.previewUrl=d)),s.video&&s.video.remove(),s.file&&s.file.remove();break;case"video":s.video&&(s.video.src=o,s.video.alt=l,d&&(s.video.dataset.previewUrl=d)),s.img&&s.img.remove(),s.file&&s.file.remove();break;case"document":if(s.preview){let e=r.file.name.split(".").pop()?.toLowerCase()??"",t={pdf:"file-pdf",csv:"file-csv",doc:"file-doc",docx:"file-doc",txt:"file-txt",xls:"file-xls",xlsx:"file-xls"},i=window.getIcon(t[e]??"file");s.preview.innerText=r.file.name??r.title,s.preview.prepend(i)}s.img&&s.img.remove(),s.video&&s.video.remove()}if(s.details&&(Object.hasOwn(r,"field")&&Object.hasOwn(r.field,"config")&&Object.hasOwn(r.field.config,"showMeta")&&!r.field.config.showMeta?s.details.remove():(Object.hasOwn(r,"id")?s.details.dataset.attachmentId=r.id:Object.hasOwn(r,"uploadId")&&(s.details.dataset.uploadId=r.uploadId),s.details.setAttribute("data-ignore",""),"image"!==a&&s.alt?s.alt.closest(".field")?.remove():Object.hasOwn(r,"image-alt-text")&&s.alt&&(s.alt.value=r["image-alt-text"]),(Object.hasOwn(r,"title")||Object.hasOwn(r,"file"))&&s.title&&(s.title.value=r.title||r.file.name),Object.hasOwn(r,"image-caption")&&s.description&&(s.description.value=r["image-caption"]))),e.draggable="single"!==e.dataset.mode,i.inputs)for(let t of i.inputs){let s=t.closest("[data-field]")??e;window.prefixInput(t,`${r.id??r.uploadId}-`,s)}}}),e.define("imageGroup",{refs:{selectAll:"[data-select-all]",fields:".fields",details:"details",grid:".item-grid"},setup({el:t,refs:s,manyRefs:i,data:r}){if(t.dataset.groupId=r.groupId,s.selectAll){let e=s.selectAll.closest(".field");window.prefixInput(s.selectAll,`select-all-${r.groupId}`,e,!0)}let a=e.create("groupMetadata",{groupId:r.groupId});a?s.fields.append(a):s.details.remove(),s.grid&&(s.grid.dataset.groupId=r.groupId)}}),e.define("groupMetadata",{manyRefs:{inputs:"input,textarea,select"},setup({el:e,refs:t,manyRefs:s,data:i}){t.inputs&&t.inputs.forEach((e=>{let t=e.closest("[data-field]");window.prefixInput(e,`${i.groupId}-`,t)}))}}),e.define("restoreNotification",{refs:{details:".details",wrap:".wrap"},setup({el:t,refs:s,manyRefs:i,data:r}){if(s.details){let e=r.bySource.size>1?` across ${r.bySource.size} pages`:"",t=r.pendingUploads.length>1?"uploads":"upload";s.details.textContent=`${r.pendingUploads.length} ${t} can be recovered${e}`}if(!s.wrap)return void console.warn("No wrap element in template");let a=1;for(const[t,i]of r.bySource){let r={index:a,isCurrent:t===window.location.href,src:t,uploads:i};s.wrap.append(e.create("restoreField",r)),a++}}}),e.define("restoreField",{refs:{h3:"h3",a:"h3 a",grid:".item-grid"},async setup({el:e,refs:s,manyRefs:i,data:r}){let a=t.registerField(e,!1,!1,`recovery_${r.index}`);r.isCurrent?(e.open=!0,s.a?.remove(),s.h3&&(s.h3.textContent="From this page:")):s.a&&(s.a.href=r.src,s.a.title="Navigate to page and restore",s.a.textContent=r.src);let o=[...new Set(r.uploads.map((e=>e.group??"preview")))];for(let e of o){let i="preview"===e||t.stores.groups.get(e);if(!i)continue;let o=await t.createGroupElement(e,a),l=o.querySelector(".item-grid"),d=r.uploads.filter((t=>t.group===("preview"===e)?null:e));for(const[e,t]of Object.entries(i.fields??{})){let s=o.querySelector(`input[name*="${e}"]`);s&&(s.value=t)}for(let e of d){let s=await t.createUpload(e.id,t.formatFile(e),a);l.append(s)}s.grid.append(o)}}})}initStores(){const{uploads:e,groups:t}=window.jvbStore.register("uploads",[{storeName:"uploads",keyPath:"id",indexes:[{name:"field",keyPath:"field"},{name:"status",keyPath:"status"},{name:"group",keyPath:"group"},{name:"src",keyPath:"src"}]},{storeName:"groups",keyPath:"id",indexes:[{name:"field",keyPath:"field"},{name:"src",keyPath:"src"}]}]);this.stores={uploads:e,groups:t,ready:[]},this.stores.uploads.subscribe(this.handleStores.bind(this,"uploads")),this.stores.groups.subscribe(this.handleStores.bind(this,"groups")),this.queue.subscribe(((e,t)=>{if(("operation-status"===e||"cancel-operation"===e)&&["image_upload","video_upload","document_upload"].includes(t.type)){let s=[];if(t.data)if(t.data instanceof FormData){const e=this.stores.uploads.formDataToObject(t.data);s=e.upload_ids||[]}else s=t.data.upload_ids||[];if(0===s.length&&t.result&&t.result.upload_ids&&(s=t.result.upload_ids),!s||0===s.length)return void console.warn("[UploadManager] No upload_ids found for operation:",{id:t.id,type:t.type,status:t.status,hasData:!!t.data,hasResult:!!t.result});if("cancel-operation"===e)return this.handleOperationCancelled(s);this.setBulkUpload(s,"status",t.status).then((()=>{console.log(`[UploadManager] Updated ${s.length} uploads to status: ${t.status}`)})),"completed"===t.status&&("process_upload_groups"===t.type?(s.forEach((e=>{this.setBulkUpload([e],"serverProcessed",!0).then((()=>{}))})),t.result&&t.result.created_posts&&console.log("[UploadManager] Created posts:",t.result.created_posts),setTimeout((()=>{s.forEach((e=>{this.removeUpload(e).then((()=>{}))}))}),2e3)):s.forEach((e=>{this.removeUpload(e).then((()=>{}))}))),"failed"!==t.status&&"failed_permanent"!==t.status||console.error("[UploadManager] Operation failed:",{id:t.id,type:t.type,uploadIds:s,error:t.error_message})}}))}storesReady(){return 2===this.stores.ready.length}handleStores(e,t){"data-ready"===t&&(this.stores.ready.push(e),this.storesReady()&&this.checkRecovery().then((()=>{})))}initWorker(){this.worker=null,this.workerState={worker:null,tasks:new Map,restart:{count:0,max:3},settings:{timeout:3e3,maxConcurrent:3,restartAfterTimeout:!0}}}initElements(){this.selectors={fields:{field:"[data-upload-field]",input:'input[type="file"]',dropZone:".file-upload-wrapper",preview:".preview-wrap",grid:".item-grid.preview",progress:{progress:".file-upload-container .progress",fill:".file-upload-container .progress .fill",details:".file-upload-container .progress .details",icon:".file-upload-container .progress .icon"},selectAll:"[data-select-all]",actions:".selection-actions",count:".selected .info",hidden:'input[type="hidden"]'},groups:{container:".group-display",grid:".item-grid.groups",empty:".empty-group",header:".sidebar .header"},group:{item:".upload-group",actions:".selection-actions",selectAll:'[name="select-all-group"]',count:".group-header .info",fields:"details .fields",grid:".item-grid.group",total:".group-content .group-count"},items:{item:".item.upload",checkbox:'[name="select-item"]',featured:'[name="featured"]',image:"img",details:"details",progress:{progress:".progress",fill:".fill",details:".details",icon:".icon"}}}}initListeners(){this.clickHandler=this.handleClick.bind(this),this.changeHandler=this.handleChange.bind(this),this.dragEnterHandler=this.handleDragEnter.bind(this),this.dragLeaveHandler=this.handleDragLeave.bind(this),this.dragOverHandler=this.handleDragOver.bind(this),this.dropHandler=this.handleDrop.bind(this),document.addEventListener("click",this.clickHandler),document.addEventListener("change",this.changeHandler),document.addEventListener("dragenter",this.dragEnterHandler),document.addEventListener("dragleave",this.dragLeaveHandler),document.addEventListener("dragover",this.dragOverHandler),document.addEventListener("drop",this.dropHandler),window.addEventListener("beforeunload",(()=>{this.cleanupAllPreviewUrls()}))}async setUpload(e,t){const s={...{id:e,attachment:null,group:null,field:null,src:window.location.href,blob:null,status:"local_processing",operationId:null,fields:{}},...t};return Object.preventExtensions(s),await this.stores.uploads.save(s),s}createPreviewUrl(e){const t=URL.createObjectURL(e);return this.previewUrls.add(t),t}revokePreviewUrl(e){e?.startsWith("blob:")&&(URL.revokeObjectURL(e),this.previewUrls.delete(e))}formatFile(e){return e.blob?new File([e.blob],e.fields.originalName||"file",{type:e.fields.type||e.blob.type,lastModified:e.fields.lastModified||Date.now()}):null}handleClick(e){let t=window.targetCheck(e,this.selectors.fields.dropZone);t&&!e.target.matches("input, button, a")&&t.querySelector(this.selectors.fields.input)?.click();const s=window.targetCheck(e,"[data-action]");s&&this.handleAction(s)}handleAction(e){const t=e.dataset.action,s=this.getFieldIdFromElement(e);switch(t){case"add-to-group":this.handleAddToGroup(s).then((()=>{}));break;case"delete-group":this.handleDeleteGroup(e);break;case"delete-upload":case"remove-from-group":this.handleRemoveItem(e).then((()=>{}));break;case"upload":this.queueUploads("uploads/groups",s).then((()=>{}));break;case"restore":this.handleRestoreSelected().then((()=>{}));break;case"restore-all":this.handleRestoreAll().then((()=>{}));break;case"clear-cache":this.handleClearCache().then((()=>{}))}}handleChange(e){let t=this.getFieldIdFromElement(e.target);if(t)if(e.target.matches(this.selectors.fields.input)){const s=Array.from(e.target.files);s.length>0&&this.processFiles(t,s).then((()=>{}))}else e.target.matches(this.selectors.items.checkbox)||e.target.matches(this.selectors.items.featured)||e.target.matches('[name*="select-"]')||("post_group"===this.fields.get(t).config.destination?this.handleGroupMetaChange(e.target):this.queueUploadMeta(e));else{e.target.closest("[data-upload-id], [data-attachment-id]")&&this.queueUploadMeta(e)}}handleGroupMetaChange(e){const t=e.dataset.groupId;if(!t)return;const s=e.name;if(!s)return;const i=e.value,r=s.replace(`${t}[`,"").replace(`${t}_`,"").replace("]","");window.debouncer.schedule(`group-meta-${t}-${r}`,(async()=>{const e=this.stores.groups.get(t);e&&(e.fields||(e.fields={}),e.fields[r]=i,await this.setGroup(t,e))}),300)}handleDragEnter(e){if(!e.dataTransfer.types.includes("Files"))return;const t=e.target.closest(this.selectors.fields.dropZone);t&&(e.preventDefault(),t.classList.add("dragover"))}handleDragLeave(e){const t=e.target.closest(this.selectors.fields.dropZone);t&&!t.contains(e.relatedTarget)&&t.classList.remove("dragover")}handleDragOver(e){if(!e.dataTransfer.types.includes("Files"))return;e.target.closest(this.selectors.fields.dropZone)&&(e.preventDefault(),e.dataTransfer.dropEffect="copy")}handleDrop(e){const t=e.target.closest(this.selectors.fields.dropZone);if(!t)return;e.preventDefault(),t.classList.remove("dragover"),t.classList.add("uploading");const s=Array.from(e.dataTransfer.files);if(0===s.length)return;const i=this.getFieldIdFromElement(t);i&&(this.processFiles(i,s).then((()=>{this.updateHandlerItems(i)})),this.a11y.announce(`${s.length} file(s) dropped for upload`))}async queueUploads(e,t){let s=new FormData;const i=this.fields.get(t);if(!i)return;let r=this.stores.uploads.filterByIndex({field:t});if(0===r.length)return;const[a,o]=["uploads"===e,"uploads/groups"===e];let l,d,n,u,p;s.append("fieldId",i.id),s.append("content",i.config.content),a&&(s.append("mode",i.config.mode),s.append("field_name",i.config.name),s.append("fieldId",i.id),s.append("field_type",i.config.type),s.append("subtype",i.config.subtype),s.append("item_id",i.config.itemID),s.append("destination",i.config.destination)),o?({posts:l,uploadMap:d,files:n}=this.collectGroups(t)):a&&({uploadMap:d,files:n}=this.collectUploads(t)),o&&s.append("posts",JSON.stringify(l)),n.forEach((e=>{s.append("files[]",e)})),s.append("upload_ids",JSON.stringify(d)),a?(u=`Uploading ${r.length} file${r.length>1?"s":""} to server...`,p=`Uploading ${r.length} file${r.length>1?"s":""}...`):o&&(u=`Creating ${l.length} ${i.config.content}${l.length>1?"s":""} from uploads...`,p=`Creating ${l.length} post${l.length>1?"s":""}...`),await this.setBulkUpload(r,"status","queued");let c=this.sendToQueue(e,s,u,p);if("uploads/groups"===e){let e=i.element.closest("details");e&&(e.open=!1),this.notify("groups_uploaded",{fieldId:t,posts:l,content:i.config.content})}return c?(i.operationId=c,await this.setBulkUpload(r,"operationId",c),await this.setBulkUpload(r,"status","uploading"),await this.setBulkGroup(t,"operationId",c),this.fields.set(i.id,i),this.notify("sent-to-queue",{field:i,operation:c})):await this.setBulkUpload(r,"status","failed"),c}async sendToQueue(e,t,s="",i="",r=!1){""===i&&(i=s);const a={endpoint:e,method:"POST",data:t,title:s,popup:i,canMerge:r,sendNow:"uploads/groups"===e,headers:{"X-Action-Nonce":window.auth.getNonce("dash")},append:"_upload"};try{return await this.queue.addToQueue(a)}catch(e){return this.error.log(e,{component:"UploadManager",action:"sentToQueue"}),!1}}collectGroups(e){let t=this.stores.uploads.filterByIndex({field:e}),s=[],i=[],r=[];const a=this.stores.groups.filterByIndex({field:e}).filter((e=>{const t=this.getGroupUploadsInOrder(e);return t.length>0&&t.some((e=>this.formatFile(e)))}));for(const e of a){const t=this.groups.get(e.id)?.element,a=this.collectGroupFieldsFromDOM(t,e.id),o={groupId:e.id,images:[],fields:a},l=this.getGroupUploadsInOrder(e);for(const t of l){const s=this.formatFile(t);if(s){r.push(s);const a={upload_id:t.id,index:i.length},l=this.uploads.get(t.id),d=l?.element?.querySelector(`input[name="${e.id}_featured"]`);d?.checked&&(o.fields.featured=t.id),o.images.push(a),i.push(t.id)}}o.images.length>0&&s.push(o)}const o=t.filter((e=>!e.group));for(const e of o){const t={groupId:window.generateID("group"),images:[],fields:{}},a=this.formatFile(e);if(a){r.push(a);const s={upload_id:e.id,index:i.length};t.images.push(s),i.push(e.id)}t.images.length>0&&s.push(t)}return{posts:s,uploadMap:i,files:r}}getGroupUploadsInOrder(e){return e.uploads&&0!==e.uploads.length?e.uploads.map((e=>this.stores.uploads.get(e))).filter(Boolean):[]}collectGroupFieldsFromDOM(e,t){if(!e)return{};const s={};return e.querySelectorAll("input, textarea, select").forEach((e=>{const i=e.name.replace(`${t}[`,"").replace(`${t}_`,"").replace("]","");["featured","select-all"].some((e=>i.includes(e)))||e.value&&(s[i]=e.value)})),s}collectUploads(e){let t=this.stores.uploads.filterByIndex({field:e});if(0===t.length)return;let s=[],i=[];for(const e of t){const t=this.formatFile(e);t&&(i.push(t),s.push(e.id))}return{uploadMap:s,files:i}}queueUploadMeta(e){let t=e.target.closest("[data-attachment-id]")?.dataset.attachmentId,s=!1;if(!t&&(t=e.target.closest("[data-upload-id]")?.dataset.uploadId,s=!0,!t))return;if(!this.changes.has(t)){let e={};s?e.uploadId=t:e.attachmentId=t,this.changes.set(t,e)}let i=e.target.closest("[data-field]").dataset.field;this.changes.get(t)[i]=e.target.value,this.scheduleSave()}scheduleSave(){window.debouncer.schedule("upload-meta",(async()=>{if(this.changes.size>0){let e={};for(let[t,s]of this.changes.entries())console.log(t,s),e[t]=s;let t={user:window.auth.getUser(),items:e};await this.sendToQueue("uploads/meta",t,"Uploading Meta","Uploading Meta",!0),this.changes.clear()}}),2e3)}scanFields(e,t=!0,s=!0){e.querySelectorAll(this.selectors.fields.field).forEach((e=>this.registerField(e,t,s)))}registerField(e,t=!0,s=!0,i=null){const r={element:e,id:i||this.determineFieldId(e),config:this.extractFieldConfig(e,t,s),uploads:new Set,operationId:null,groups:[],ui:window.uiFromSelectors(this.selectors.fields,e),groupUI:window.uiFromSelectors(this.selectors.groups,e)};return this.fields.set(r.id,r),e.dataset.uploader=r.id,this.getSelectionHandler(r.id),"single"!==r.config.type&&this.initSortable(r.id),r.id}extractFieldConfig(e,t,s){return{autoUpload:t,showMeta:s,destination:e.dataset.destination||"meta",content:this.extractFieldContent(e),mode:e.dataset.mode||"direct",type:e.dataset.type||"single",name:e.dataset.field,itemID:this.extractFieldItemId(e)??0,maxFiles:parseInt(e.dataset.maxFiles)??25,subType:e.dataset.subtype??"image"}}extractFieldContent(e){return e.dataset.content||e.closest("dialog")?.dataset.content||e.closest("form")?.dataset.save||null}extractFieldItemId(e){return e.dataset.itemId||e.closest("dialog")?.dataset.itemId||null}determineFieldId(e){let t=this.extractFieldContent(e);t=null===t?"":t+"_";let s=this.extractFieldItemId(e);s=null===s?"":s+"_";return`${t}${s}${e.dataset.field||""}`}getFieldIdFromElement(e){const t=e.closest(this.selectors.fields.field);return t?.dataset.uploader||null}updateFieldProgress(e,t,s,i){const r=this.fields.get(e);r&&window.showProgress(r.ui.progress,t,s,i)}getWorker(){return this.workerState.worker||"undefined"==typeof OffscreenCanvas||(this.workerState.worker=new Worker("worker.js"),this.workerState.worker.onmessage=e=>this.handleWorkerMessage(e),this.workerState.worker.onerror=e=>this.handleWorkerError(e)),this.workerState.worker}handleWorkerMessage(e){const{id:t,blob:s}=e.data,i=this.workerState.tasks.get(t);i&&(clearTimeout(i.timeoutId),i.resolve(s),this.workerState.tasks.delete(t))}handleWorkerError(e){this.workerState.tasks.forEach((t=>{clearTimeout(t.timeoutId),t.reject(e)})),this.workerState.tasks.clear(),this.restartWorker()}restartWorker(){this.workerState.worker&&(this.workerState.worker.terminate(),this.workerState.worker=null),this.workerState.restart.count++}async processImages(e,t=2200,s=2200){const i=[],r=[...e],a=this.workerState.settings.maxConcurrent,o=async()=>{for(;r.length>0;){const e=r.shift(),a=await this.processImage(e.file,t,s);i.push({uploadId:e.uploadId,blob:a})}};return await Promise.all(Array.from({length:Math.min(a,e.length)},(()=>o()))),i}async processImage(e,t=2200,s=2200,i=3e3){if("undefined"==typeof OffscreenCanvas)return this.resizeImage(e,t,s);try{return await this.withTimeout(this.workerImage(e,t,s),i)}catch(i){return this.resizeImage(e,t,s)}}withTimeout(e,t){return Promise.race([e,new Promise(((e,s)=>setTimeout((()=>s(new Error("Timeout"))),t)))])}async workerImage(e,t=2200,s=2200){const{settings:i,restart:r}=this.workerState;if(r.count>=r.max)throw new Error("Worker max restarts exceeded");const a=await createImageBitmap(e);let{width:o,height:l}=a;if(o>t||l>s){const e=Math.min(t/o,s/l);o=Math.round(o*e),l=Math.round(l*e)}const d=this.getWorker(),n=crypto.randomUUID();return new Promise(((t,s)=>{const r=setTimeout((()=>{this.workerState.tasks.delete(n),i.restartAfterTimeout&&this.restartWorker(),s(new Error("Timeout"))}),i.timeout);this.workerState.tasks.set(n,{resolve:t,reject:s,timeoutId:r}),d.postMessage({id:n,imageBitmap:a,width:o,height:l,type:e.type,quality:.9},[a])}))}resizeImage(e,t,s){return new Promise((i=>{const r=new Image;r.onload=()=>{URL.revokeObjectURL(r.src);let{width:a,height:o}=r;if(a>t||o>s){const e=Math.min(t/a,s/o);a=Math.round(a*e),o=Math.round(o*e)}const l=document.createElement("canvas");l.width=a,l.height=o,l.getContext("2d").drawImage(r,0,0,a,o),l.toBlob(i,e.type,.9)},r.src=URL.createObjectURL(e)}))}async processFiles(e,t){let s=this.fields.get(e);if(!s)return;s.groupUI.container&&(s.groupUI.container.hidden=!1);const i=t.length;let r=0;this.updateFieldProgress(e,0,i,"Processing files...");const a=await Promise.all(t.map((async t=>{const s=window.generateID("upload"),i=await this.setUpload(s,{id:s,field:e,status:"local_processing",fields:{originalName:t.name,originalSize:t.size,type:t.type,lastModified:t.lastModified}}),r=await this.createUpload(s,t,e);return this.uploads.set(s,{element:r,ui:window.uiFromSelectors(this.selectors.items,r)}),await this.addToGroup(s,null),{uploadId:s,upload:i,file:t}}))),o=a.filter((e=>e.file.type.startsWith("image/"))),l=a.filter((e=>!e.file.type.startsWith("image/"))),d=await this.processImages(o.map((e=>({file:e.file,uploadId:e.uploadId}))));for(const{uploadId:t,blob:s}of d){const a=o.find((e=>e.uploadId===t));a&&(a.upload.blob=s,a.upload.fields.size=s.size,a.upload.status="queued",await this.setUpload(t,a.upload),r++,this.updateFieldProgress(e,r,i,"Processing files..."))}for(const{uploadId:t,upload:s,file:a}of l)s.blob=a,s.status="queued",await this.setUpload(t,s),r++,this.updateFieldProgress(e,r,i,"Processing files...");this.maybeLockUploads(e),s.config.autoUpload&&"post_group"!==s.config.destination&&await this.queueUploads("uploads",e)}async checkRecovery(){const e=this.stores.uploads.filterByIndex({status:["local_processing","queued","uploading"]}),t=Array.from(this.stores.groups.data.values());for(const e of t){this.stores.uploads.filterByIndex({group:e.id}).length>0||await this.stores.groups.delete(e.id)}if(0===e.length)return;const s=new Map;e.forEach((e=>{const t=e.src||"unknown";s.has(t)||s.set(t,[]),s.get(t).push(e)}));let i={bySource:s,pendingUploads:e};document.body.append(this.templates.create("restoreNotification",i));let r=document.querySelector("dialog.restore-uploads");this.restoreModal=new window.jvbModal(r),this.restoreSelection=new window.jvbHandleSelection(r,{wrapper:{wrapper:".restore-field",id:"selection"},items:".item-grid.restore",selectAll:{bulkControls:".selection-actions",checkbox:"#select-all-restore",count:".selection-count"}}),this.restoreModal.handleOpen()}async handleRestoreSelected(){if(!this.restoreSelection)return;let e=Array.from(this.restoreSelection.selectedItems);0!==e.length&&await this.restoreSelectedUploads(e)}async handleRestoreAll(){if(!this.restoreModal)return;const e=Array.from(this.restoreModal.modal.querySelectorAll(".item.upload")).map((e=>e.dataset.uploadId));await this.restoreSelectedUploads(e)}async restoreSelectedUploads(e){let t=window.location.href,s=Array.from(this.stores.uploads.data.values()).filter((s=>e.includes(s.id)&&s.src===t)),i=[...new Set(s.map((e=>e.group)))].filter(Boolean),r=s[0].field;if(!document.querySelector(`[data-uploader="${r}"]`))return void console.log("No field found for "+r);let a=this.fields.get(r);a.groupUI.container&&(a.groupUI.container.hidden=!1);let o=[];for(let e of i){let t=this.stores.groups.get(e);await this.createGroup(r,e);let i=this.groups.get(e),a=s.filter((t=>t.group===e));if(t&&this.groups.has(e)){let e=t.fields;for(const[t,s]of Object.entries(e)){let e=i.element.querySelector(`input[name*="${t}"]`);e&&(e.value=s)}}else e=null;for(let t of a){let s=await this.createUpload(t.id,this.formatFile(t),r);this.uploads.set(t.id,{element:s,ui:window.uiFromSelectors(this.selectors.items,s)}),await this.addToGroup(t.id,e),o.push(t.id)}}let l=s.filter((e=>!o.includes(e.id)));for(let e of l){let t=await this.createUpload(e.id,this.formatFile(e),r);this.uploads.set(e.id,{element:t,ui:window.uiFromSelectors(this.selectors.items,t)}),await this.addToGroup(e.id,null)}this.cleanupRestore()}cleanupRestore(){this.restoreModal.handleClose(),this.restoreSelection.destroy(),this.restoreSelection=null,this.restoreModal.destroy(),this.restoreModal.modal.remove(),this.restoreModal=null}getStatusText(e){return{received:"Image Received",local_processing:"Processing Image...",queued:"Waiting to upload...",uploading:"Uploading to Server",pending:"Successfully sent to server. In line for further processing.",processing:"Processing on server...",completed:"Upload complete!",failed:"Upload failed (will retry)",failed_permanent:"Upload failed permanently"}[e]||e}getStatusProgress(e){return{local_processing:28,queued:50,uploading:66,pending:75,processing:89,completed:100}[e]??0}async createUpload(e,t,s){let i=this.fields.get(s);if(!i)return null;let r={uploadId:e,file:t,field:i};return this.templates.create("uploadItem",r)}getSubtypeFromURL(e){const t=e.split("?")[0].toLowerCase();return[".webp",".jpg",".jpeg",".png",".gif",".svg"].some((e=>t.endsWith(e)))?"image":[".mp4",".ogg",".mov",".webm",".avi"].some((e=>t.endsWith(e)))?"video":"document"}getSubtypeFromMime(e){return e.startsWith("image/")?"image":e.startsWith("video/")?"video":"document"}async handleRemoveItem(e){const t=e.closest(this.selectors.items.item);if(!t)return;const s=t.dataset.uploadId;confirm("Remove this item?")&&(await this.removeUpload(s),this.a11y.announce("Item removed"))}async setBulkUpload(e,t,s){const i=Array.from(e).map((async e=>{if("string"==typeof e&&(e=await this.stores.uploads.get(e)),e)return"status"===t&&await this.setUploadStatus(e,s),e[t]=s,this.stores.uploads.save(e)}));await Promise.all(i)}async setUploadStatus(e,t){"string"==typeof e&&(e=await this.stores.uploads.get(e)),e&&e.progress&&window.showProgress(e.progress,this.getStatusProgress(t),100,this.getStatusText(t),this.queue.icons[t]??"")}async removeUpload(e){let t=this.stores.uploads.get(e);if(!t)return;if(t.group){let s=this.stores.groups.get(t.group);s.uploads=s.uploads.filter((t=>t!==e)),0===s.uploads.length?await this.removeGroup(s.id,!1):await this.stores.groups.save(s)}await this.clearUpload(e),this.maybeLockUploads(t.field);let s=this.selectionHandlers.get(t.field);s&&s.deselect(e),this.a11y.announce("Upload removed")}async clearUpload(e){const t=this.uploads.get(e);if(t&&(this.revokePreviewUrl(t.preview),t.element)){const e=t.element.dataset.previewUrl;this.revokePreviewUrl(e),t.element.remove()}this.uploads.delete(e),await this.stores.uploads.delete(e)}async handleAddToGroup(e){const t=this.selected.get(e);if(!t||0===t.size)return;let s=await this.createGroup(e);s&&(await Promise.all(Array.from(t).map((e=>this.addToGroup(e,s)))),this.selectionHandlers.get(e)?.clearSelection(),this.a11y.announce(`Created group with ${t.size} items`))}async createGroup(e,t=null){let s=this.fields.get(e);if(!s)return;t||(t=window.generateID("group"));const i=this.createGroupElement(t,e);if(!i)return null;const r=s.groupUI.empty;r?.nextSibling?s.groupUI.grid.insertBefore(i,r.nextSibling):s.groupUI.grid.append(i);const a=i.querySelector(".item-grid");a&&(a.dataset.groupId=t,this.createSortable(e,a,t));let o=this.stores.groups.data.has(t)?this.stores.groups.data.get(t):{};return await this.setGroup(t,{...o,id:t,field:e}),t}createGroupElement(e,t=null){let s={groupId:e,fieldId:t},i=this.templates.create("imageGroup",s);return this.groups.set(e,{element:i,ui:window.uiFromSelectors(this.selectors.group,i)}),this.getSelectionHandler(t)?.addWrapper(i),i}async setGroup(e,t){const s={...{id:e,src:window.location.href,uploads:[],operationId:null,field:null,fields:{}},...t};Object.preventExtensions(s),await this.stores.groups.save(s)}async setBulkGroup(e,t,s){let i=this.stores.groups.filterByIndex({field:e});if(0===i.length)return;let r=i.map((e=>{e[t]=s,this.stores.groups.save(e)}));await Promise.all(r)}async addToGroup(e,t=null){const s=this.stores.uploads.get(e),i=this.uploads.get(e);if(!s||!i)return;const r=this.fields.get(s.field);if(!r)return;if(null!==i.element?.parentElement&&(!t&&null===s.group||t===s.group))return void this.handleReorder(s.field,t);if(s.group){const t=this.stores.groups.get(s.group);t&&(t.uploads=t.uploads.filter((t=>t!==e)),0===t.uploads.length?await this.removeGroup(t.id,!1):await this.stores.groups.save(t))}i.ui.checkbox&&(i.ui.checkbox.checked=!1);const a=this.selectionHandlers.get(s.field);if(a&&a.isSelected(e)&&a.deselect(e),this.selected.get(s.field)?.has(e)&&this.selected.get(s.field).delete(e),i.ui.featured&&(i.ui.featured.hidden=!t),t){i.ui.featured&&(i.ui.featured.name=`${t}_featured`);let r=this.stores.groups.get(t);r&&(r.uploads.push(e),s.group=t,await this.stores.groups.save(r))}else s.group=null;let o=t?this.groups.get(t)?.ui.grid:r.ui.grid;o&&(o.append(i.element),t&&await this.handleReorder(s.field,t)),await this.stores.uploads.save(s)}handleDeleteGroup(e){const t=e.closest(this.selectors.group.item);if(!t)return;let s=t.dataset.groupId;if(!confirm("Delete this group? Items will be moved back to the upload area."))return;let i=this.stores.uploads.filterByIndex({group:s});Promise.all(i.map((e=>this.addToGroup(e.id,null)))).then((()=>{this.removeGroup(s,!1).then((()=>{})),this.a11y.announce("Group deleted. Items returned to upload area")}))}async removeGroup(e,t=!0){let s=this.groups.get(e),i=this.stores.groups.get(e);if(!i)return;let r=!0;t&&i.uploads.length>0&&(r=window.confirm("Keep uploads in this group?")),await Promise.all(i.uploads.map((e=>r?this.addToGroup(e,null):this.removeUpload(e))));if(this.fields.get(i.field)){const t=this.getGroupKey(i.field,e),r=this.selectionHandlers.get(t);r?.destroy&&r.destroy(),this.selectionHandlers.get(i.field)?.removeWrapper(s.element);const a=this.sortables.get(t);a?.destroy&&a.destroy(),this.sortables.delete(t)}s?.element&&s.element.remove(),this.groups.delete(e),await this.stores.groups.delete(e),this.a11y.announce("Group removed")}maybeLockUploads(e){let t=this.fields.get(e);if(!t||!t.ui.dropZone)return;let s=this.stores.uploads.filterByIndex({field:e}).length,i=t.config.maxFiles??25;t.ui.dropZone.hidden=s>=i}async handleOperationCancelled(e){0!==e.length&&e.forEach((e=>{this.removeUpload(e)}))}getGroupKey(e,t=null){return t?`${e}_${t}`:`${e}`}getSelectionHandler(e){let t=this.getGroupKey(e);if(!this.selectionHandlers.has(t)){let s=this.fields.get(e);if(!s)return;if("post_group"!==s.config.destination)return;let i=new window.jvbHandleSelection(s.element,{selectAll:{checkbox:this.selectors.fields.selectAll,count:this.selectors.fields.count,bulkControls:this.selectors.fields.actions},item:{item:this.selectors.items.item,checkbox:this.selectors.items.checkbox,idAttribute:"uploadId"},wrapper:{wrapper:".preview-wrap, .upload-group",id:"groupId"}});i.subscribe(((t,s)=>{this.selected.set(e,s.selectedItems)})),this.selectionHandlers.set(t,i)}return this.selectionHandlers.get(t)}updateHandlerItems(e){let t=this.getSelectionHandler(e);t&&t.collectItems()}initSortable(e){if(!window.Sortable)return;const t=this.fields.get(e);t&&(!Sortable._multiDragMounted&&Sortable.MultiDrag&&(Sortable.mount(new Sortable.MultiDrag),Sortable._multiDragMounted=!0),this.createSortable(e,t.ui.grid,null),this.initEmptyGroupDropZone(e))}createSortable(e,t,s){if(!t)return null;const i=this.getGroupKey(e,s);if(this.sortables.has(i))return this.sortables.get(i);const r=new Sortable(t,{animation:150,draggable:".item",multiDrag:!0,selectedClass:"selected",avoidImplicitDeselect:!0,group:{name:e,pull:!0,put:!0},dragClass:"dragging",ignore:".empty-group",onStart:t=>{const s=t.item,i=s?.dataset.uploadId,r=this.selected.get(e);if(i&&(!r||!r.has(i))){const t=this.selectionHandlers.get(e);t&&t.select(i)}},onEnd:t=>this.sortableDrop(t,e)});return this.sortables.set(i,r),r}initEmptyGroupDropZone(e){const t=this.fields.get(e),s=t?.groupUI?.empty;s&&(s.addEventListener("dragover",(e=>{e.preventDefault(),e.stopPropagation(),e.dataTransfer.dropEffect="move",s.classList.add("drag-over")})),s.addEventListener("dragleave",(e=>{s.contains(e.relatedTarget)||s.classList.remove("drag-over")})),s.addEventListener("drop",(async t=>{t.preventDefault(),t.stopPropagation(),s.classList.remove("drag-over");const i=this.selected.get(e);if(!i||0===i.size)return;const r=await this.createGroup(e);r&&(await Promise.all(Array.from(i).map((e=>this.addToGroup(e,r)))),this.selectionHandlers.get(e)?.clearSelection())})))}async sortableDrop(e,t){const s=e.to,i=(e.items?.length>0?Array.from(e.items):[e.item]).map((e=>e.dataset.uploadId)).filter(Boolean);if(0===i.length)return;const r=s.dataset.groupId||null;for(const e of i)await this.addToGroup(e,r);await this.handleReorder(t,r),this.selectionHandlers.get(t)?.clearSelection()}handleReorder(e,t=null){let s=t?this.groups.get(t)?.ui.grid:this.fields.get(e)?.ui.grid;if(!s)return void console.log("Couldn't Reorder items...");let i=Array.from(s.children).filter((e=>e.matches(this.selectors.items.item)&&!e.classList.contains("ghost"))).map((e=>e.dataset.uploadId)).filter((e=>e));if(t){let e=this.stores.groups.get(t);e&&(e.uploads=i,this.stores.groups.save(e).then((()=>{})))}else{let t=this.fields.get(e)?.ui.hidden;t&&(t.value=i.join(","))}this.a11y.announce("Items reordered")}subscribe(e){return this.subscribers.add(e),()=>this.subscribers.delete(e)}notify(e,t={}){this.subscribers.forEach((s=>{try{s(e,t)}catch(e){console.error("Subscriber error:",e)}}))}destroy(){this.subscribers.clear(),this.previewUrls.forEach((e=>{this.revokePreviewUrl(e)})),this.previewUrls.clear()}cleanupAllPreviewUrls(){this.previewUrls.forEach((e=>this.revokePreviewUrl(e))),this.previewUrls.clear()}async handleClearCache(){const e=window.location.href,t=this.stores.uploads.filterByIndex({src:e}),s=this.stores.groups.filterByIndex({src:e});await Promise.all([...t.map((e=>this.clearUpload(e.id))),...s.map((e=>(this.groups.get(e.id)?.element?.remove(),this.groups.delete(e.id),this.stores.groups.delete(e.id))))]),this.restoreModal&&this.cleanupRestore(),this.a11y.announce("Cache cleared for this page")}async getFilesForForm(e){const t=e.querySelectorAll(this.selectors.fields.field),s=[];for(const e of t){const t=this.determineFieldId(e),i=e.dataset.field,r=this.stores.uploads.filterByIndex({field:t});for(const e of r){const t=this.formatFile(e);t&&s.push({file:t,fieldName:i,uploadId:e.id,meta:e.fields||{}})}}return s}async clearFieldFromStores(e){const t=this.stores.uploads.filterByIndex({field:e}),s=this.stores.groups.filterByIndex({field:e});await Promise.all(t.map((e=>this.clearUpload(e.id)))),await Promise.all(s.map((e=>(this.groups.get(e.id)?.element?.remove(),this.groups.delete(e.id),this.stores.groups.delete(e.id)))))}}document.addEventListener("DOMContentLoaded",(async function(){window.auth.subscribe((t=>{"auth-loaded"===t&&(window.jvbUploads=new e)}))}))})();
inc/blocks/FormBlock.php
@@ -37,7 +37,7 @@
        // Initialize forms from filter
        $this->forms = $this->registerForms();
        $this->form_contact = apply_filters('jvb_form_contact', '');
        // Hook into the CustomBlocks render system
        add_filter('jvb_render_block_jvb_forms', [$this, 'render'], 10, 2);
inc/helpers/renderFields.php
@@ -423,7 +423,7 @@
            </div>
        </template>
        <template class="uploadMeta">
            <?= jvbImageMeta() ?>
            <?= Form::renderImagePreview() ?>
        </template>
        <template class="imageGroup">
            <div class="upload-group">
@@ -594,37 +594,6 @@
    }
}
function jvbImageMeta(int|null $ID = null, string $title = '', string $alt = '', string $caption = '', array $fields = []):string
{
    $dataID = ($ID) ? ['image-id' => $ID] : false;
    $addID = ($ID) ? '-'.$ID : '';
    $config = [
        'image-title'.$addID => [
            'type'  => 'text',
            'label' => 'Image Title',
            'value' => $title,
            'data'  => $dataID
        ],
        'image-alt-text'.$addID => [
            'type'  => 'text',
            'label' => 'Alt Text',
            'value' => $alt,
            'hint'  => 'Alt text helps the visually impaired, as well as some benefits for SEO.',
            'data'  => $dataID
        ],
        'image-caption'.$addID => [
            'type'  => 'textarea',
            'value' => $caption,
            'label' => 'Image Caption',
            'data'  => $dataID
        ]
    ];
    return Form::render('image_data', null, $config, false, true);
}
function jvbLocationLinks(array $location): string {
    if (empty($location['address'])) {
inc/managers/CRUDManager.php
@@ -63,8 +63,6 @@
            );
        // Initialize meta
        $this->skeleton->initMeta($this->type, $this->content);
        $this->skeleton->addSearch();
        // Timeline if applicable
inc/managers/DashboardManager.php
@@ -31,6 +31,7 @@
        if (!$this->isRegistered()) {
            add_action('init', [$this, 'buildDashboard']);
        }
        $this->cache->flush();
        $this->user = wp_get_current_user();
        $this->role = jvbUserRole($this->user->ID);
        $this->userLink = (int)get_user_meta($this->user->ID, BASE.'link', true);
@@ -538,28 +539,28 @@
                    break;
                case 'admin':
                case 'dash':
                    if (current_user_can('manage_options') && apply_filters('jvbAdminDashboard', '') === '') {
                        wp_enqueue_script(
                        'jvb-admin',
                        JVB_URL . 'assets/js/min/admin.min.js',
                        [
                            'jvb-queue',
                            'jvb-loading'
                        ],
                        [
                            'strategy' => 'defer',
                            'in_footer' => true
                        ]
                    );
//                    if (current_user_can('manage_options') && apply_filters('jvbAdminDashboard', '') === '') {
//                        wp_enqueue_script(
//                        'jvb-admin',
//                        JVB_URL . 'assets/js/min/admin.min.js',
//                        [
//                            'jvb-queue',
////                            'jvb-loading'
//                        ],
//                        [
//                            'strategy' => 'defer',
//                            'in_footer' => true
//                        ]
//                    );
                    wp_localize_script(
                        'jvb-admin',
                        'jvbAdmin',
                        [
                            'nonce' => wp_create_nonce('itsme')
                        ]
                    );
                    }
//                    wp_localize_script(
//                        'jvb-admin',
//                        'jvbAdmin',
//                        [
//                            'nonce' => wp_create_nonce('itsme')
//                        ]
//                    );
//                    }
                    break;
                case 'seo':
                    wp_enqueue_script('jvb-schema');
inc/managers/queue/Progress.php
@@ -19,6 +19,7 @@
            $this->operation->processedItems + $count,
            $this->operation->totalItems
        );
        JVB()->queue()->storage()->saveProgress($this->operation);
    }
    public function failItem(mixed $item, string $reason): void
@@ -27,5 +28,6 @@
            'item' => $item,
            'reason' => $reason,
        ];
        JVB()->queue()->storage()->saveProgress($this->operation);
    }
}
inc/managers/queue/Queue.php
@@ -19,7 +19,7 @@
        $this->registry = new TypeRegistry();
        $this->locker = new Locker();
        $executor = new FilteredExecutor($this->storage);
        $executor = new FilteredExecutor();
        $this->processor = new Processor($this->storage, $executor, $this->registry);
        add_action('jvb_process_queue', [$this, 'checkQueue']);
inc/managers/queue/Storage.php
@@ -138,6 +138,9 @@
            return false;
        }
        Cache::invalidateGroup('queue');
//      $this->invalidateUser($op->userId);
        return true;
    }
@@ -172,7 +175,6 @@
        ];
        $updated = $wpdb->update($table, $data, $where);
        $this->invalidateQueueCache();
        if ($updated === 0) {
            return true;
@@ -182,6 +184,9 @@
            error_log('[Storage::saveFinal] DB error: ' . $wpdb->last_error);
            return false;
        }
        Cache::invalidateGroup('queue');
//      $this->invalidateQueueCache();
//      $this->invalidateUser($op->userId);
        return true;
    }
@@ -214,7 +219,8 @@
        ]);
        if ($result) {
            $this->invalidateUser($op->userId);
//          $this->invalidateUser($op->userId);
            Cache::invalidateGroup('queue');
        }
        return $result !== false;
@@ -239,6 +245,8 @@
            $type, $userId
        ));
        $this->invalidateUser($userId);
        return $row ? $this->rowToOperation($row) : null;
    }
@@ -443,6 +451,7 @@
    private function invalidateUser(int $userId): void
    {
        $this->cache->forget($userId);
        Cache::touch($userId);
    }
    public function getLastError(): string
    {
inc/managers/queue/_setup.php
@@ -28,6 +28,7 @@
require_once JVB_DIR . '/inc/managers/queue/Processor.php';
require_once JVB_DIR . '/inc/managers/queue/executors/UploadExecutor.php';
require_once JVB_DIR . '/inc/managers/queue/executors/ContentExecutor.php';
require_once JVB_DIR . '/inc/managers/queue/executors/ContentTermExecutor.php';
require_once JVB_DIR . '/inc/managers/queue/executors/InvitationExecutor.php';
// Facade
inc/managers/queue/executors/ContentExecutor.php
@@ -76,9 +76,11 @@
        $results = [];
        $errors = [];
        $success = [];
        $timelineParents = [];
        $timelineStatus = [];
        $timelineSharedFields = [];
        $newPostsMap = [];
        foreach ($posts as $id => $postData) {
            try {
@@ -94,16 +96,14 @@
                    ]);
                    if (!$newId || is_wp_error($newId)) {
                        $errors[$id] = 'Could not create post';
                        $progress->failItem($id, 'Could not create post');
                        continue;
                    }
                    $newPostsMap[$id] = $newId;
                    $this->savePostFields($newId, $postData);
                    $results[$id] = [
                        'success' => true,
                        'new_id' => $newId,
                        'processed_fields' => array_keys($postData)
                    ];
                    $success[$newId] = array_keys($postData);
                    $progress->advance();
                    continue;
                }
@@ -117,7 +117,6 @@
                $this->savePostFields((int)$id, $postData);
                if (Features::forContent($content)->has('is_timeline')) {
                    $post = get_post((int)$id);
                    $parentId = $post->post_parent > 0 ? $post->post_parent : $post->ID;
@@ -142,19 +141,12 @@
                        }
                    }
                }
                $results[$id] = [
                    'success' => true,
                    'processed_fields' => array_keys($postData)
                ];
                $progress->advance();
                $success[$id] = array_keys($postData);
                $progress->advance();
            } catch (Exception $e) {
                $progress->failItem($id, $e->getMessage());
                $errors[$id] = $e->getMessage();
                $results[$id] = [
                    'success' => false,
                    'error' => $e->getMessage()
                ];
            }
        }
@@ -188,15 +180,13 @@
            $outcome = count($errors) === count($posts) ? 'failed' : 'partial';
        }
        return new Result(
            outcome: $outcome,
            result: [
                'posts' => $results,
                'posts' => $success,
                'errors' => $errors,
                'updated_count' => count(array_filter($results, fn($r) => $r['success'] ?? false)),
                'new_posts' => $newPostsMap,
                'updated_count' => count($success),
                'failed_count' => count($errors)
            ]
        );
@@ -215,9 +205,9 @@
            return true;
        }
        $meta = Meta::forPost($postId);
        $meta->setAll($allowedFields);
        return true;
        return Meta::forPost($postId)
            ->setAll($allowedFields)
            ->save();
    }
    // â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
@@ -318,7 +308,7 @@
        foreach ($posts as $index => $post) {
            $meta = Meta::forPost($post->ID);
            if ($index === 0) {
                $meta->set('timeline', '', false);
                $meta->set('timeline', '');
                $previousPost = $post;
                continue; // Parent has no timeline
            }
@@ -329,7 +319,7 @@
                if ($timeline) {
                    $termId = $this->getOrCreateTerm($timeline, 'timeline');
                    if ($termId) {
                        $success = $meta->set('timeline', $termId, false);
                        $success = $meta->set('timeline', $termId);
                    }
                }
            }
@@ -337,7 +327,7 @@
            if ($lastKey === $index) {
                $latestTimestamp = strtotime($post->post_date);
            }
            $meta->save();
            $previousPost = $post;
        }
inc/managers/queue/executors/ContentTermExecutor.php
New file
@@ -0,0 +1,338 @@
<?php
namespace JVBase\managers\queue\executors;
use JVBase\managers\CustomTable;
use JVBase\managers\queue\Executor;
use JVBase\managers\queue\Operation;
use JVBase\managers\queue\Progress;
use JVBase\managers\queue\Result;
use JVBase\managers\RoleManager;
use JVBase\meta\Meta;
use JVBase\utility\Features;
use Exception;
if (!defined('ABSPATH')) {
    exit;
}
/**
 * Executor for content taxonomy operations (shops, studios, etc.)
 *
 * Handles:
 * - Term metadata updates
 * - Member additions (via history table)
 * - Member removals (via history table)
 */
class ContentTermExecutor implements Executor
{
    protected RoleManager $roleManager;
    public function __construct()
    {
        $this->roleManager = new RoleManager();
    }
    public function execute(Operation $operation, Progress $progress): Result
    {
        // Extract taxonomy from operation type (e.g., "shop_update" -> "shop")
        $parts = explode('_', $operation->type);
        $taxonomy = $parts[0] ?? '';
        if (!$taxonomy || !isset(JVB_TAXONOMY[$taxonomy])) {
            return Result::fail("Invalid taxonomy: {$taxonomy}");
        }
        return match(true) {
            str_ends_with($operation->type, '_update') => $this->processUpdate($operation, $taxonomy),
            str_ends_with($operation->type, '_member_add') => $this->processMemberAdd($operation, $taxonomy),
            str_ends_with($operation->type, '_member_remove') => $this->processMemberRemove($operation, $taxonomy),
            default => Result::fail("Unknown operation type: {$operation->type}")
        };
    }
    /**
     * Process term metadata updates
     */
    protected function processUpdate(Operation $operation, string $taxonomy): Result
    {
        $termID = $operation->requestData['term_id'] ?? 0;
        $userID = $operation->userId;
        if (!$termID || !term_exists($termID, jvbCheckBase($taxonomy))) {
            return Result::fail('Invalid term ID');
        }
        // Verify permissions using RoleManager
        if (!user_can($userID, 'manage_options') && !$this->roleManager->isManager($userID, $termID)) {
            return Result::fail('User does not have permission to manage this ' . $taxonomy);
        }
        try {
            $meta = Meta::forTerm($termID);
            $data = $operation->requestData;
            unset($data['term_id']);
            // Filter to only allowed fields
            $allowed = jvbGetFields($taxonomy, 'term');
            $setData = array_filter(
                $data,
                fn($key) => array_key_exists($key, $allowed),
                ARRAY_FILTER_USE_KEY
            );
            if (empty($setData)) {
                return Result::fail('No valid fields to update');
            }
            // Update metadata
            $results = $meta->setAll($setData);
            if ($results) {
                // Clear cache
                JVB()->cache()->for($taxonomy)->delete("term_{$termID}_meta");
                // Trigger any post-update actions (e.g., thumbnail generation)
                do_action(BASE . "{$taxonomy}_updated", $termID, $userID, $setData);
                return Result::success([
                    'updated_fields' => array_keys($setData),
                    'term_id' => $termID
                ]);
            }
            return Result::fail('Failed to update term metadata');
        } catch (Exception $e) {
            return Result::fail('Update error: ' . $e->getMessage());
        }
    }
    /**
     * Add member to term (via history table)
     */
    protected function processMemberAdd(Operation $operation, string $taxonomy): Result
    {
        $termID = $operation->requestData['term_id'] ?? 0;
        $targetUserID = $operation->requestData['target_user'] ?? 0;
        $userID = $operation->userId;
        if (!$termID || !term_exists($termID, jvbCheckBase($taxonomy))) {
            return Result::fail('Invalid term ID');
        }
        if (!get_userdata($targetUserID)) {
            return Result::fail('Invalid target user');
        }
        // Verify permissions
        if (!user_can($userID, 'manage_options') && !$this->roleManager->isManager($userID, $termID)) {
            return Result::fail('User does not have permission to manage this ' . $taxonomy);
        }
        // Check if tracking enabled
        if (!Features::forTaxonomy($taxonomy)->has('track_changes')) {
            return Result::fail('Member tracking not enabled for ' . $taxonomy);
        }
        try {
            return $this->addMember($targetUserID, $termID, $taxonomy);
        } catch (Exception $e) {
            return Result::fail('Add member error: ' . $e->getMessage());
        }
    }
    /**
     * Remove member from term (via history table)
     */
    protected function processMemberRemove(Operation $operation, string $taxonomy): Result
    {
        $termID = $operation->requestData['term_id'] ?? 0;
        $targetUserID = $operation->requestData['target_user'] ?? 0;
        $userID = $operation->userId;
        if (!$termID || !term_exists($termID, jvbCheckBase($taxonomy))) {
            return Result::fail('Invalid term ID');
        }
        if (!get_userdata($targetUserID)) {
            return Result::fail('Invalid target user');
        }
        // Verify permissions
        if (!user_can($userID, 'manage_options') && !$this->roleManager->isManager($userID, $termID)) {
            return Result::fail('User does not have permission to manage this ' . $taxonomy);
        }
        try {
            return $this->removeMember($targetUserID, $termID, $taxonomy);
        } catch (Exception $e) {
            return Result::fail('Remove member error: ' . $e->getMessage());
        }
    }
    /**
     * Add member to term with transaction support
     */
    protected function addMember(int $userID, int $termID, string $taxonomy): Result
    {
        $config = JVB_TAXONOMY[$taxonomy] ?? [];
        $content = $config['for_content'] ?? [];
        if (empty($content)) {
            return Result::fail('No content types configured for ' . $taxonomy);
        }
        // Get table name (e.g., "history_artist_shop")
        $contentType = $content[0]; // Use first content type
        $tableName = "history_{$contentType}_{$taxonomy}";
        $table = CustomTable::for($tableName);
        return $table->transaction(function($table) use ($userID, $termID, $taxonomy, $contentType) {
            // Check if already a member
            $existing = $table
                ->where([
                    'user_id' => $userID,
                    'term_id' => $termID,
                    'end_date' => null
                ])
                ->first();
            if ($existing) {
                return Result::success([
                    'message' => 'User is already a member',
                    'existing' => true
                ]);
            }
            // Get user's content post ID
            $contentID = get_user_meta($userID, BASE . 'link', true);
            if (!$contentID) {
                throw new Exception('User profile not found');
            }
            // Verify content post exists
            $post = get_post($contentID);
            if (!$post || $post->post_type !== BASE . $contentType) {
                throw new Exception('Content post not found or invalid type');
            }
            // Insert new membership
            $result = $table->create([
                'user_id' => $userID,
                'content_id' => $contentID,
                'term_id' => $termID,
                'role' => 'member',
                'is_primary' => 1,
                'start_date' => current_time('mysql')
            ]);
            if (!$result) {
                throw new Exception('Failed to create membership record');
            }
            // Add taxonomy term to content post
            $termResult = wp_set_object_terms($contentID, [$termID], jvbCheckBase($taxonomy), true);
            if (is_wp_error($termResult)) {
                throw new Exception('Failed to set taxonomy term: ' . $termResult->get_error_message());
            }
            // Clear cache
            JVB()->cache()->for($taxonomy)->delete("{$taxonomy}_{$termID}_members");
            // Notify term managers
            $this->notifyTermManagers($termID, $userID, $taxonomy, 'member_added');
            return Result::success([
                'message' => 'Member added successfully',
                'user_id' => $userID,
                'term_id' => $termID
            ]);
        });
    }
    /**
     * Remove member from term with transaction support
     */
    protected function removeMember(int $userID, int $termID, string $taxonomy): Result
    {
        $config = JVB_TAXONOMY[$taxonomy] ?? [];
        $content = $config['for_content'] ?? [];
        if (empty($content)) {
            return Result::fail('No content types configured for ' . $taxonomy);
        }
        // Get table name
        $contentType = $content[0];
        $tableName = "history_{$contentType}_{$taxonomy}";
        $table = CustomTable::for($tableName);
        return $table->transaction(function($table) use ($userID, $termID, $taxonomy, $contentType) {
            // Get user's content post ID
            $contentID = get_user_meta($userID, BASE . 'link', true);
            if (!$contentID) {
                throw new Exception('User profile not found');
            }
            // Update membership record - set end_date
            $updated = $table
                ->where([
                    'user_id' => $userID,
                    'term_id' => $termID,
                    'end_date' => null
                ])
                ->updateResults([
                    'end_date' => current_time('mysql'),
                    'is_primary' => 0
                ]);
            if (!$updated) {
                throw new Exception('No active membership found');
            }
            // Remove taxonomy term from content post
            $termResult = wp_remove_object_terms($contentID, [$termID], jvbCheckBase($taxonomy));
            if (is_wp_error($termResult)) {
                throw new Exception('Failed to remove taxonomy term: ' . $termResult->get_error_message());
            }
            // Clear cache
            JVB()->cache()->for($taxonomy)->delete("{$taxonomy}_{$termID}_members");
            // Notify term managers
            $this->notifyTermManagers($termID, $userID, $taxonomy, 'member_removed');
            return Result::success([
                'message' => 'Member removed successfully',
                'user_id' => $userID,
                'term_id' => $termID
            ]);
        });
    }
    /**
     * Notify term owners/managers about member changes
     */
    protected function notifyTermManagers(int $termID, int $userID, string $taxonomy, string $notificationType): void
    {
        $managers = $this->roleManager->getManagedTerms($userID, $taxonomy);
        $term = get_term($termID, jvbCheckBase($taxonomy));
        $user = get_userdata($userID);
        foreach ($managers as $managerID) {
            JVB()->notification()->addNotification(
                $managerID,
                $notificationType,
                [
                    'user_id' => $userID,
                    'user_name' => $user ? $user->display_name : 'Unknown',
                    'term_id' => $termID,
                    'term_name' => $term && !is_wp_error($term) ? $term->name : 'Unknown',
                    'taxonomy' => $taxonomy
                ]
            );
        }
    }
}
inc/managers/queue/executors/UploadExecutor.php
@@ -74,10 +74,12 @@
        $uploader = new UploadManager();
        $processedResults = [];
        $errors = [];
        $uploadIds = [];
        $securedFiles = $data['secured_files'] ?? [];
        foreach ($securedFiles as $securedFile) {
            $uploadIds[] = $securedFile['upload_id'];
            try {
                $result = $uploader->processUpload(
                    $securedFile['temp_path'],
@@ -92,32 +94,26 @@
                    ]
                );
                if (!is_wp_error($result)) {
                    $standardized = [
                        'attachment_id' => $result['attachment_id'],
                        'url'           => $result['url'],
                        'file'          => $result['file'],
                        'upload_id'     => $securedFile['upload_id'] ?? null,
                    ];
                    if ($standardized['upload_id']) {
                        $processedResults[$standardized['upload_id']] = $standardized;
                    } else {
                        $processedResults[] = $standardized;
                    }
                    // Apply frontend metadata if provided
                    if (!empty($securedFile['metadata'])) {
                        $this->applyMeta($standardized['attachment_id'], $securedFile['metadata']);
                    }
                    $progress->advance(1);
                } else {
                    $progress->failItem($securedFile, $result->get_error_message());
                if (is_wp_error($result)) {
                    $progress->failItem($securedFile['upload_id'], $result->get_error_message());
                    $errors[] = $result->get_error_message();
                    continue;
                }
                $processedResults[$securedFile['upload_id']] = [
                    'upload_id' => $securedFile['upload_id'],
                    'attachment_id' => $result['attachment_id'] ?? 0,
                    'url' => $result['url'] ?? '',
                    'sizes' => $result['sizes'] ?? [],
                ];
                if (!empty($securedFile['metadata'])) {
                    $this->applyMeta($securedFile['attachment_id'], $securedFile['metadata']);
                }
                $progress->advance();
            } catch (Exception $e) {
                $progress->failItem($securedFile, $e->getMessage());
                $progress->failItem($securedFile['upload_id'], $e->getMessage());
                $errors[] = $e->getMessage();
            }
        }
@@ -128,14 +124,20 @@
        // Cleanup temp files
        $this->cleanupTempFiles($securedFiles, $operation->userId);
        $outcome = 'success';
        if (!empty($operation->failedItems)) {
            $outcome = count($operation->failedItems) === count($securedFiles) ? 'failed' : 'partial';
        $outcome = count($processedResults) > 0 ? 'success' : 'failed';
        if (count($processedResults) > 0 && !empty($errors)) {
            $outcome = 'partial';
        }
        return new Result(
            outcome: $outcome,
            result: $processedResults
            result: [
                'upload_ids'    => $uploadIds,
                'uploads'       => $processedResults,
                'processed_count'=> count($processedResults),
                'total_count'   => count($uploadIds),
                'errors'        => $errors
            ]
        );
    }
@@ -281,17 +283,27 @@
        }
        $uploads = [];
        $uploadIds = [];
        foreach ($dependencies as $dependency) {
            $res = JVB()->queue()->getOperationValue($dependency, 'result');
            if (empty($res)) {
                continue;
            }
            // Results are stored at root level, keyed by upload_id
            // Check if dependency result has upload_ids
            if (isset($res['upload_ids'])) {
                $uploadIds = array_merge($uploadIds, $res['upload_ids']);
            }
            // Results are stored in 'uploads', keyed by upload_id
            // Filter to only include actual upload results (arrays with attachment_id)
            foreach ($res as $key => $value) {
            foreach ($res['uploads'] as $key => $value) {
                if (is_array($value) && isset($value['attachment_id'])) {
                    $uploads[$key] = $value;
                    // If we didn't get upload_ids from result, track them from keys
                    if (!isset($res['upload_ids']) && !in_array($key, $uploadIds)) {
                        $uploadIds[] = $key;
                    }
                }
            }
        }
@@ -316,72 +328,126 @@
            return $this->processTimelineUploads($operation, $data, $progress, $all_uploads);
        }
        $user = (int)$operation->userId;
        $user = $operation->userId;
        $createdPosts = [];
        $errors = [];
        $groupMappings = [];
        $usedUploads = [];
        foreach($data['posts'] as $index => $post) {
            $progress->advance();
            $post_title = array_key_exists('post_title', $post['fields'])
                ? sanitize_text_field($post['fields']['post_title'])
                : 'New '. JVB_CONTENT[$data['content']]['singular'].' '.($index + 1);
            try {
                $groupId = $post['groupId'] ?? null;
                // Create post for this group
                $created = $this->createPostFromGroup($post, $index+1, $content, $uploads, $operation);
            $post_excerpt = array_key_exists('post_excerpt', $post['fields'])
                ? sanitize_textarea_field($post['fields']['post_excerpt'])
                : '';
                if ($created) {
                    $postId = $created['ID'];
                    $createdPosts[] = [
                        'post_id' => $postId,
                        'group_id' => $groupId,
                    ];
            $args = [
                'post_type'     => $content,
                'post_author'   => $user,
                'post_status'   => 'draft',
                'post_title'    => $post_title,
                'post_excerpt'  => $post_excerpt
            ];
            $newPostID = wp_insert_post($args);
            if ($newPostID && !is_wp_error($newPostID)) {
                $createdPosts[] = $newPostID;
                $featured_upload_id = $post['fields']['featured']??null;
                $featured_attachment_id = null;
                $gallery_attachment_ids = [];
                foreach ($post['images'] as $img) {
                    $uploadId = $img['upload_id'];
                    $usedUploads[] = $uploadId;
                    if (array_key_exists($uploadId, $all_uploads)) {
                        $attachmentId = $all_uploads[$uploadId]['attachment_id'];
                        if ($uploadId === $featured_upload_id) {
                            $featured_attachment_id = $attachmentId;
                        } else {
                            $gallery_attachment_ids[] = $attachmentId;
                        }
                    if ($groupId) {
                        $groupMappings[$groupId] = $postId;
                    }
                }
                if ($featured_attachment_id) {
                    set_post_thumbnail($newPostID, $featured_attachment_id);
                } elseif (!empty($gallery_attachment_ids)) {
                    set_post_thumbnail($newPostID, $gallery_attachment_ids[0]);
                    array_shift($gallery_attachment_ids);
                }
                if (!empty($gallery_attachment_ids)) {
                    $meta = Meta::forPost($newPostID);
                    $fields = jvbGetFields($content, 'post');
                    foreach($fields as $name => $config) {
                        if ($config['type'] === 'gallery') {
                            $meta->set($name, implode(',', $gallery_attachment_ids));
                            break;
                        }
                    }
                    $usedUploads = array_merge($usedUploads, $created['usedUploads']);
                    $progress->advance(1);
                }
            } catch (Exception $e) {
                $errors[] = $e->getMessage();
                $progress->failItem($index ?? 'unknown', $e->getMessage());
            }
        }
        $outcome = !empty($createdPosts) ? 'success' : 'failed';
        if (!empty($createdPosts) && !empty($errors)) {
            $outcome = 'partial';
        }
        return new Result(
            outcome: $outcome,
            result: [
                'upload_ids' => $usedUploads,
                'created_posts' => $createdPosts,
                'group_mappings' => $groupMappings,
                'post_count' => count($createdPosts),
                'processed_uploads' => count($uploads),
                'errors' => $errors,
            ]
        );
    }
    protected function createPostFromGroup(array $post, int $index, string $content, array $uploads, Operation $op):array|false
    {
        $config = JVB_CONTENT[jvbNoBase($content)]??false;
        if (!$config) {
            throw new Exception('No config found for content: '.$content.'.');
        }
        $post_title = array_key_exists('post_title', $post['fields'])
            ? sanitize_text_field($post['fields']['post_title'])
            : 'New '. $config['singular'].' '.($index + 1);
        $post_excerpt = array_key_exists('post_excerpt', $post['fields'])
            ? sanitize_textarea_field($post['fields']['post_excerpt'])
            : '';
        $ID = wp_insert_post([
            'post_type'     => $content,
            'post_author'   => $op->userId,
            'post_status'   => 'draft',
            'post_title'    => $post_title,
            'post_excerpt'  => $post_excerpt,
        ]);
        if (!$ID || is_wp_error($ID)) {
            throw new Exception('Could not create post: '.$ID?->get_error_message());
        }
        $uploadIds = [];
        $featured_upload_id = $post['fields']['featured']??null;
        $featured_attachment_id = null;
        $gallery = [];
        foreach ($post['images'] as $img) {
            $uploadId = $img['upload_id'];
            if (array_key_exists($uploadId, $uploads)){
                $imgID = $uploads[$uploadId]['attachment_id'];
                if ($uploadId === $featured_upload_id) {
                    $featured_attachment_id = $imgID;
                } else {
                    $gallery[] = $imgID;
                }
                $uploadIds[] = $uploadId;
            }
        }
        return new Result(
            outcome: !empty($createdPosts) ? 'success' : 'failed',
            result: ['posts' => $createdPosts]
        );
        if ($featured_attachment_id) {
            set_post_thumbnail($ID, $featured_attachment_id);
        } elseif (!empty($gallery)) {
            set_post_thumbnail($ID, $gallery[0]);
            array_shift($gallery);
        }
        if (!empty($gallery)) {
            $meta = Meta::forPost($ID);
            $fields = jvbGetFields($content, 'post');
            //add images to first found gallery field
            $found = false;
            foreach ($fields as $name =>$config) {
                if ($config['type'] === 'upload' && (array_key_exists('multiple', $config) && $config['multiple'] === true)) {
                    $found = true;
                    $meta->set($name, implode(',', $gallery));
                    break;
                }
            }
            if (!$found) {
                error_log('Could not find a gallery upload field for post '.$ID);
            }
        }
        return [
            'ID'    => $ID,
            'usedUploads' => $uploadIds
        ];
    }
    private function processTimelineUploads(Operation $operation, array $data, Progress $progress, array $uploads):Result
@@ -389,33 +455,37 @@
        $user = $operation->userId;
        $createdPosts = [];
        $usedUploads = [];
        $errors = [];
        $content = jvbCheckBase($data['content']);
        $config = Features::getConfig($content);
        $defaultTitle = 'New '.$config['singular']. ' ';
        foreach($data['posts'] as $index => $post) {
            $progress->advance();
            $title = array_key_exists('post_title', $post['fields'])
                ? sanitize_text_field($post['fields']['post_title'])
                : $defaultTitle . ($index + 1);
            try {
                $title = array_key_exists('post_title', $post['fields'])
                    ? sanitize_text_field($post['fields']['post_title'])
                    : $defaultTitle . ($index + 1);
            $excerpt = array_key_exists('post_excerpt', $post['fields'])
                ? sanitize_textarea_field($post['fields']['post_excerpt'])
                : '';
                $excerpt = array_key_exists('post_excerpt', $post['fields'])
                    ? sanitize_textarea_field($post['fields']['post_excerpt'])
                    : '';
            $args = [
                'post_type' => $content,
                'post_author'   => $user,
                'post_status'   => 'draft',
                'post_title'    => $title,
                'post_slug'     => sanitize_title($title),
                'post_excerpt'  => $excerpt
            ];
                $args = [
                    'post_type' => $content,
                    'post_author'   => $user,
                    'post_status'   => 'draft',
                    'post_title'    => $title,
                    'post_slug'     => sanitize_title($title),
                    'post_excerpt'  => $excerpt
                ];
            $parent = wp_insert_post($args);
            $progress->advance();
            if ($parent && !is_wp_error($parent)) {
                $parent = wp_insert_post($args);
                if (!$parent || is_wp_error($parent)) {
                    throw new Exception('Could not create post: '.$parent->get_error_message());
                }
                $childPosts = [];
                $featured = $post['fields']['featured']??null;
@@ -423,7 +493,6 @@
                foreach ($post['images'] as $img) {
                    $uploadId = $img['upload_id'];
                    $usedUploads[] = $uploadId;
                    if (array_key_exists($uploadId, $uploads)) {
                        $attachmentId = (int)$uploads[$uploadId]['attachment_id'];
@@ -434,33 +503,58 @@
                        }
                    }
                }
                if ($featuredID) {
                    $usedUploads[] = $featuredID;
                    set_post_thumbnail($parent, $featuredID);
                } elseif (!empty($childPosts)) {
                    set_post_thumbnail($parent, (int)$childPosts[0]);
                    $usedUploads[] = (int)$childPosts[0];
                    array_shift($childPosts);
                }
                $createdChildren = [];
                if (!empty($childPosts)) {
                    $args['post_parent'] = $parent;
                    $args['post_excerpt'] = '';
                    $createdPosts[$parent] = [];
                    foreach($childPosts as $i => $imgID) {
                        $treatment = $i + 1;
                        $args ['post_title'] = $title.' - Treatment #'.$treatment;
                        $child = wp_insert_post($args);
                        if ($child && !is_wp_error($child)) {
                            $createdPosts[$parent][] = $child;
                            $createdChildren = $child;
                            $usedUploads[] = $imgID;
                            set_post_thumbnail($child, $imgID);
                        }
                    }
                }
                $createdPosts[] = [
                    'parent'    => $parent,
                    'children'  => $createdChildren
                ];
                $this->updateTimelineMetadata($parent);
                $progress->advance();
            } catch (Exception $e) {
                $errors[] = $e->getMessage();
                $progress->failItem($index ?? 'unknown', $e->getMessage());
            }
        }
        $outcome = !empty($createdPosts) ? 'success' : 'failed';
        if (!empty($createdPosts) && !empty($errors)) {
            $outcome = 'partial';
        }
        return new Result(
            outcome: !empty($createdPosts) ? 'success' : 'failed',
            result: ['posts' => $createdPosts]
            outcome: $outcome,
            result: [
                'upload_ids'    => $usedUploads,
                'created_posts' => $createdPosts,
                'post_count'    => count($createdPosts),
                'processed_uploads' => count($uploads),
                'errors'        => $errors
            ]
        );
    }
inc/meta/Form.php
@@ -141,10 +141,10 @@
            protected static function handleCustomDatasets($config):string
            {
                if (array_key_exists('data', $config) && !empty($config['data'])) {
                    $datasets = array_map(function ($key) use ($config) {
                    $datasets = array_map(function ($value, $key) use ($config) {
                        $name = str_replace('_', '-', sanitize_title($key));
                        return ' data-'.$name.'="'.$config['data'][$key].'"';
                    }, $config['data']);
                        return ' data-'.$name.'="'.$value.'"';
                    }, array_values($config['data']), array_keys($config['data']));
                    return implode($datasets);
                }
@@ -629,8 +629,22 @@
        }
        $config['data']['upload-field'] = '';
        $config['data']['type'] = $config['multiple'] ? 'gallery' : 'single';
        $plural = 'images';
        $singular = 'image';
        if (!empty($config['content'])) {
            $config['data']['content'] = $config['content'];
            $content = $config['content'];
            $config['data']['content'] = $content;
            $plural =
                JVB_CONTENT[$content]['plural']
                ?? JVB_TAXONOMY[$content]['plural']
                ?? JVB_USER[$content]['plural']
                ?? str_replace('_', ' ', $content) . 's';
            $singular = JVB_CONTENT[$content]['singular']
                ?? JVB_TAXONOMY[$content]['singular']
                ?? JVB_USER[$content]['singular']
                ?? str_replace('_', ' ',$config['content']);
        }
        if ($config['limit'] > 0) {
            $config['data']['limit'] = $config['limit'];
@@ -644,7 +658,8 @@
                <input type="file"
                    %s
                    accept="%s"
                    data-max-size="%s">
                    data-max-size="%s"
                    data-ignore>
                <h2>%s</h2>
                %s
                <p class="file-upload-text">
@@ -660,8 +675,8 @@
            esc_html(static::getAcceptedTypesLabel($config)),
            esc_html(static::formatFileSize(static::getMaxFileSize($config)))
        );
        $plural = (array_key_exists($config['content'], JVB_CONTENT)) ? JVB_CONTENT[$config['content']]['plural'] : (array_key_exists($config['content'], JVB_TAXONOMY) ? JVB_TAXONOMY[$config['content']]['plural'] : str_replace('_', ' ',$config['content']).'s');
        $singular = (array_key_exists($config['content'], JVB_CONTENT)) ? JVB_CONTENT[$config['content']]['singular'] : (array_key_exists($config['content'], JVB_TAXONOMY) ? JVB_TAXONOMY[$config['content']]['singular'] : str_replace('_', ' ',$config['content']));
        if ($config['destination'] === 'post_group') {
            $input .= sprintf(
                '<p class="hint">You can group images to create separate %s.</p>
@@ -852,7 +867,7 @@
                        ]
                    ];
                    $out .= static::render('image_data', $fields);
                    $out .= static::render('image_data', '', $fields);
                    $out .= static::renderUploadItemMetaEnd();
                    if ($additionalFields) {
@@ -920,7 +935,7 @@
                        ]
                    ];
                    $out .= static::render('image_data', $fields);
                    $out .= static::render('image_data', '', $fields);
                    $out .= static::renderUploadItemMetaEnd();
                    if ($additionalFields) {
@@ -982,7 +997,7 @@
                        ]
                    ];
                    $out .= static::render('image_data', $fields);
                    $out .= static::render('image_data', '', $fields);
                    $out .= static::renderUploadItemMetaEnd();
                    if ($additionalFields) {
@@ -1577,7 +1592,6 @@
    protected static function renderGroup(string $name, mixed $value, array $config): string
    {
        $fields = $config['fields'] ?? [];
        error_log('Group fields: '.print_r($fields, true));
        $values = is_array($value) ? $value : [];
        $wrapper = (array_key_exists('wrap', $config)) ? 'details' : 'fieldset';
inc/meta/Storage.php
@@ -429,6 +429,11 @@
            return set_post_thumbnail($item->id, $value) !== false;
        }
        // Special handling for post_status (trash/delete require specific functions)
        if ($item->objectType === 'post' && $name === 'post_status') {
            return $this->updatePostStatus($item->id, $value);
        }
        return match ($item->objectType) {
            'post' => wp_update_post(['ID' => $item->id, $name => $value]) !== 0,
            'term' => !is_wp_error(wp_update_term($item->id, $item->wpObject->taxonomy, [
@@ -440,6 +445,55 @@
        };
    }
    /**
     * Update post status with proper WordPress functions
     *
     * WordPress doesn't handle trash/delete via wp_update_post():
     * - wp_trash_post() required for trashing
     * - wp_delete_post() required for deletion
     * - 'delete' is not even a valid post_status value
     *
     * @param int $postId Post ID
     * @param string $status New status (trash, delete, publish, draft, etc.)
     * @return bool Success
     */
    protected function updatePostStatus(int $postId, string $status): bool
    {
        // Handle trash status
        if ($status === 'trash') {
            $result = wp_trash_post($postId);
            if ($result === false || $result === null) {
                error_log("[Storage] Failed to trash post {$postId}");
                return false;
            }
            return true;
        }
        // Handle permanent deletion
        if ($status === 'delete') {
            $result = wp_delete_post($postId, true); // true = force delete, bypass trash
            if ($result === false || $result === null) {
                error_log("[Storage] Failed to delete post {$postId}");
                return false;
            }
            return true;
        }
        // Handle all other statuses (publish, draft, pending, private, future)
        $result = wp_update_post([
            'ID' => $postId,
            'post_status' => $status
        ]);
        if ($result === 0 || is_wp_error($result)) {
            $error = is_wp_error($result) ? $result->get_error_message() : 'Unknown error';
            error_log("[Storage] Failed to update post {$postId} status to {$status}: {$error}");
            return false;
        }
        return true;
    }
    protected function saveTaxonomyField(Item $item, Field $field): bool
    {
        $taxonomy = jvbCheckBase($field->config['taxonomy']);
inc/rest/PermissionHandler.php
@@ -343,9 +343,13 @@
    /**
     * Verify action-specific nonce (e.g., 'dash-{user_id}')
     */
    public static function verifyActionNonce(WP_REST_Request $request, string $actionPrefix, string $header = 'action_nonce'): bool|WP_Error
    public static function verifyActionNonce(WP_REST_Request $request, string $actionPrefix, string $header = 'X-Action-Nonce'): bool|WP_Error
    {
        $userId = $request->get_param('user') ?: get_current_user_id();
        $userId = absint($request->get_param('user'));
        if ($userId === 0) {
            return false;
        }
        $action = $actionPrefix . $userId;
        return self::verifyNonce($request, $action, $header);
inc/rest/_setup.php
@@ -39,6 +39,7 @@
if (Features::forSite()->has('dashboard')) {
//  require(JVB_DIR . '/inc/rest/routes/AdminRoutes.php');
    require(JVB_DIR . '/inc/rest/routes/ContentRoutes.php');
    require(JVB_DIR . '/inc/rest/routes/ContentTermsRoutes.php');
//  require(JVB_DIR . '/inc/rest/routes/BioRoutes.php');
//  require(JVB_DIR . '/inc/rest/routes/ShopRoutes.php');
}
inc/rest/routes/ContentRoutes.php
@@ -76,10 +76,27 @@
        Route::for('content')
            ->get([$this, 'getContent'])
            ->auth(PermissionHandler::combine(['user', 'nonce', ['actionNonce'=>'dash-']]))
            ->args([
                'content' => 'string|required',
                'status' => 'string|default:all',
                'page' => 'integer|default:1|min:1',
                'per_page' => 'integer|default:30|min:1|max:100',
                'orderby' => 'string|enum:date,alphabetical|default:date',
                'order' => 'string|enum:asc,desc|default:desc',
                'search' => 'string',
                'date-filter' => 'string',
                'dateFrom' => 'string',
                'dateTo' => 'string',
            ])
            ->rateLimit(20)
            ->post([$this, 'postContent'])
            ->auth(PermissionHandler::combine(['user', 'nonce', ['actionNonce'=>'dash-']]))
            ->rateLimit(30);
            ->rateLimit(30)
            ->args([
                'user'  => 'int|required',
                'posts' => 'required',
                'content' => 'string',
            ]);
    }
    protected function initTimelineFields(string $content): void
@@ -151,6 +168,7 @@
            return Response::success(['message'=>'No posts found in request']);
        }
        $count = count($data['posts']);
        $operationId = $data['id'];
        unset($data['user']);
inc/rest/routes/ContentTermsRoutes.php
@@ -1,17 +1,566 @@
<?php
namespace JVBase\rest\routes;
use JVBase\managers\queue\executors\ContentTermExecutor;
use JVBase\managers\queue\TypeConfig;
use JVBase\rest\Rest;
use JVBase\rest\Route;
use JVBase\rest\PermissionHandler;
use JVBase\utility\Features;
use JVBase\managers\CustomTable;
use WP_REST_Request;
use WP_REST_Response;
use Exception;
if (!defined('ABSPATH')) {
    exit;
}
/**
 * Generic routes for managing content taxonomies (shops, studios, etc.)
 *
 * Responsibilities:
 * - Update term settings/metadata
 * - Manage members (add/remove via history table)
 * - Manage ownership/management permissions
 * - Process membership requests (if verify_entry enabled)
 *
 * Note: Invitations handled by InvitationRoutes
 * Note: Approvals handled by ApprovalRoutes
 */
class ContentTermsRoutes extends Rest
{
    protected string $taxonomy;
    protected array $config;
    protected ?CustomTable $historyTable = null;
    protected ?CustomTable $requestsTable = null;
    public function __construct(string $taxonomy = '')
    {
        $this->taxonomy = jvbNoBase($taxonomy);
        if ($taxonomy && isset(JVB_TAXONOMY[$this->taxonomy])) {
            $this->config = JVB_TAXONOMY[$this->taxonomy];
            $this->cacheName = $this->taxonomy;
            parent::__construct();
            $this->setupTables();
            $this->setupCacheConnections();
        }
        add_action('init', [$this, 'registerContentTermsExecutors'], 5);
    }
    public function registerContentTermsExecutors():void
    {
        $registry = JVB()->queue()->registry();
        $executor = new ContentTermExecutor();
        $taxonomies = Features::getTypesWithFeature('is_content', 'taxonomy');
        foreach($taxonomies as $taxonomy) {
            $registry->register("{$taxonomy}_update", new TypeConfig(
                executor: $executor,
            ));
            if (Features::forTaxonomy($taxonomy)->has('track_changes')) {
                $registry->register("{$taxonomy}_member_add", new TypeConfig(
                    executor: $executor
                ));
                $registry->register("{$taxonomy}_member_remove", new TypeConfig(
                    executor: $executor
                ));
            }
        }
    }
    protected function setupTables(): void
    {
        $content = $this->config['for_content'] ?? [];
        if (Features::forTaxonomy($this->taxonomy)->has('track_changes') && !empty($content)) {
            foreach ($content as $contentType) {
                $tableName = "history_{$contentType}_{$this->taxonomy}";
                $this->historyTable = CustomTable::for($tableName);
                break; // Only need one table
            }
        }
        if (Features::forTaxonomy($this->taxonomy)->has('verify_entry') && !empty($content)) {
            foreach ($content as $contentType) {
                $tableName = "{$contentType}_{$this->taxonomy}_requests";
                $this->requestsTable = CustomTable::for($tableName);
                break;
            }
        }
    }
    protected function setupCacheConnections(): void
    {
        $this->cache
            ->connect('taxonomy')
            ->connect('user');
    }
    public function registerRoutes(): void
    {
        // TODO: Implement registerRoutes() method.
        if (!Features::forTaxonomy($this->taxonomy)->has('is_content')) {
            return;
        }
        $base = $this->taxonomy;
        // Update term settings
        Route::for("{$base}/:term_id/settings")
            ->post([$this, 'updateSettings'])
            ->args([
                'user' => 'int|required',
                'term_id' => 'int|required'
            ])
            ->auth(PermissionHandler::custom([$this, 'checkTermPermission']))
            ->rateLimit(10);
        // Member management (if track_changes enabled)
        if (Features::forTaxonomy($this->taxonomy)->has('track_changes')) {
            Route::for("{$base}/:term_id/members")
                ->get([$this, 'getMembers'])
                ->args([
                    'term_id' => 'int|required',
                    'status' => 'string|enum:active,inactive,all|default:active',
                    'page' => 'int|default:1|min:1'
                ])
                ->auth('logged_in')
                ->rateLimit(30)
                ->post([$this, 'manageMember'])
                ->args([
                    'user' => 'int|required',
                    'term_id' => 'int|required',
                    'target_user' => 'int|required',
                    'action' => 'string|enum:add,remove|required'
                ])
                ->auth(PermissionHandler::custom([$this, 'checkTermPermission']))
                ->rateLimit(5);
        }
        // Membership requests (if verify_entry enabled)
        if (Features::forTaxonomy($this->taxonomy)->has('verify_entry')) {
            Route::for("{$base}/:term_id/requests")
                ->get([$this, 'getRequests'])
                ->args([
                    'user' => 'int|required',
                    'term_id' => 'int|required',
                    'status' => 'string|enum:requested,accepted,rejected,all|default:requested',
                    'page' => 'int|default:1|min:1'
                ])
                ->auth(PermissionHandler::custom([$this, 'checkTermPermission']))
                ->rateLimit(20);
            Route::for("{$base}/request")
                ->post([$this, 'handleRequest'])
                ->args([
                    'user' => 'int|required',
                    'term_id' => 'int|required',
                    'action' => 'string|enum:create,accept,reject|required',
                    'request_id' => 'int',
                    'notes' => 'string'
                ])
                ->auth('verified')
                ->rateLimit(5);
        }
        // Ownership/management (if is_ownable enabled)
        if (Features::forTaxonomy($this->taxonomy)->has('is_ownable')) {
            Route::for("{$base}/:term_id/permissions")
                ->post([$this, 'updatePermissions'])
                ->args([
                    'user' => 'int|required',
                    'term_id' => 'int|required',
                    'target_user' => 'int|required',
                    'role' => 'string|enum:owner,manager|required',
                    'grant' => 'bool|required'
                ])
                ->auth(PermissionHandler::custom([$this, 'checkOwnerPermission']))
                ->rateLimit(5);
        }
    }
    /**
     * Permission Callbacks
     */
    public function checkTermPermission(WP_REST_Request $request): bool
    {
        $userID = $request->get_param('user') ?? get_current_user_id();
        $termID = (int)$request->get_param('term_id');
        if (!$this->checkUser($userID) || !term_exists($termID, jvbCheckBase($this->taxonomy))) {
            return false;
        }
        return user_can($userID, 'manage_options') ||
            JVB()->roles()->isManager($userID, $termID);
    }
    public function checkOwnerPermission(WP_REST_Request $request): bool
    {
        $userID = $request->get_param('user') ?? get_current_user_id();
        $termID = (int)$request->get_param('term_id');
        if (!$this->checkUser($userID) || !term_exists($termID, jvbCheckBase($this->taxonomy))) {
            return false;
        }
        return user_can($userID, 'manage_options') ||
            JVB()->roles()->isOwner($userID, $termID);
    }
    /**
     * Update term settings
     */
    public function updateSettings(WP_REST_Request $request): WP_REST_Response
    {
        $termID = (int)$request->get_param('term_id');
        $userID = $request->get_param('user');
        $data = $request->get_params();
        unset($data['user'], $data['term_id']);
        // Queue the update
        $op = JVB()->queue()->add(
            "{$this->taxonomy}_update",
            $userID,
            array_merge(['term_id' => $termID], $data),
            [
                'priority' => 'high',
                'notification' => true
            ]
        );
        return $this->queued($op['operation_id']);
    }
    /**
     * Get term members
     */
    public function getMembers(WP_REST_Request $request): WP_REST_Response
    {
        $termID = (int)$request->get_param('term_id');
        $status = $request->get_param('status');
        $page = (int)$request->get_param('page');
        $cacheKey = $this->cache->generateKey(compact('termID', 'status', 'page'));
        return $this->cache->remember($cacheKey, function() use ($termID, $status, $page) {
            $perPage = 20;
            $offset = ($page - 1) * $perPage;
            $query = $this->historyTable->where(['term_id' => $termID]);
            if ($status === 'active') {
                $query = $query->where(['end_date' => null]);
            } elseif ($status === 'inactive') {
                $query = $query->whereNotNull('end_date');
            }
            $total = $query->countResults();
            $members = $query
                ->orderBy('is_primary', 'DESC')
                ->orderBy('created_at', 'ASC')
                ->limit($perPage, $offset)
                ->getResults(ARRAY_A);
            // Enrich with user data
            $members = array_map(function($member) {
                $user = get_userdata($member['user_id']);
                $member['display_name'] = $user ? $user->display_name : 'Unknown';
                $member['user_email'] = $user ? $user->user_email : '';
                return $member;
            }, $members);
            return $this->success([
                'members' => $members,
                'total' => $total,
                'pages' => ceil($total / $perPage),
                'page' => $page,
                'per_page' => $perPage
            ]);
        });
    }
    /**
     * Manage member (add/remove)
     */
    public function manageMember(WP_REST_Request $request): WP_REST_Response
    {
        $action = $request->get_param('action');
        $userID = $request->get_param('user');
        $termID = (int)$request->get_param('term_id');
        $targetUserID = (int)$request->get_param('target_user');
        if (!$this->checkUser($targetUserID)) {
            return $this->error('Invalid target user');
        }
        // Queue the operation
        $op = JVB()->queue()->add(
            "{$this->taxonomy}_member_{$action}",
            $userID,
            [
                'term_id' => $termID,
                'target_user' => $targetUserID
            ],
            ['priority' => 'high']
        );
        return $this->queued($op['operation_id']);
    }
    /**
     * Get membership requests
     */
    public function getRequests(WP_REST_Request $request): WP_REST_Response
    {
        $termID = (int)$request->get_param('term_id');
        $status = $request->get_param('status');
        $page = (int)$request->get_param('page');
        $cacheKey = $this->cache->generateKey(compact('termID', 'status', 'page'));
        return $this->cache->remember($cacheKey, function() use ($termID, $status, $page) {
            $perPage = 20;
            $offset = ($page - 1) * $perPage;
            $query = $this->requestsTable->where(['term_id' => $termID]);
            if ($status !== 'all') {
                $query = $query->where(['status' => $status]);
            }
            $total = $query->countResults();
            $requests = $query
                ->orderBy('created_date', 'DESC')
                ->limit($perPage, $offset)
                ->getResults(ARRAY_A);
            return $this->success([
                'requests' => $requests,
                'total' => $total,
                'pages' => ceil($total / $perPage),
                'page' => $page,
                'per_page' => $perPage
            ]);
        });
    }
    /**
     * Handle membership request (create/accept/reject)
     */
    public function handleRequest(WP_REST_Request $request): WP_REST_Response
    {
        $action = $request->get_param('action');
        $userID = $request->get_param('user');
        $termID = (int)$request->get_param('term_id');
        return match($action) {
            'create' => $this->createRequest($userID, $termID),
            'accept', 'reject' => $this->processRequest($request),
            default => $this->error('Invalid action')
        };
    }
    protected function createRequest(int $userID, int $termID): WP_REST_Response
    {
        if (!term_exists($termID, jvbCheckBase($this->taxonomy))) {
            return $this->error('Invalid ' . $this->taxonomy);
        }
        // Check if request already exists
        $existing = $this->requestsTable
            ->where([
                'user_id' => $userID,
                'term_id' => $termID
            ])
            ->first();
        if ($existing) {
            return $this->error('Request already exists');
        }
        $contentID = get_user_meta($userID, BASE . 'link', true);
        if (!$contentID) {
            return $this->error('User profile not found');
        }
        try {
            $requestID = $this->requestsTable->create([
                'user_id' => $userID,
                'content_id' => $contentID,
                'term_id' => $termID,
                'status' => 'requested',
                'created_date' => current_time('mysql')
            ]);
            if ($requestID) {
                $this->notifyTermManagers($termID, $userID, 'membership_request');
                $this->cache->flush();
                return $this->success([
                    'message' => 'Request submitted successfully',
                    'request_id' => $requestID
                ]);
            }
            return $this->error('Failed to create request');
        } catch (Exception $e) {
            $this->logError('createRequest', [
                'error' => $e->getMessage(),
                'user_id' => $userID,
                'term_id' => $termID
            ]);
            return $this->error('An error occurred');
        }
    }
    protected function processRequest(WP_REST_Request $request): WP_REST_Response
    {
        $action = $request->get_param('action');
        $requestID = (int)$request->get_param('request_id');
        $notes = $request->get_param('notes') ?? '';
        if (!$requestID) {
            return $this->error('Request ID required');
        }
        $requestData = $this->requestsTable
            ->where(['id' => $requestID])
            ->first(ARRAY_A);
        if (!$requestData) {
            return $this->error('Request not found');
        }
        $status = $action === 'accept' ? 'accepted' : 'rejected';
        try {
            $this->requestsTable->transaction(function($table) use ($requestID, $status, $notes, $requestData) {
                // Update request
                $table->where(['id' => $requestID])->updateResults([
                    'status' => $status,
                    'notes' => $notes,
                    'updated_date' => current_time('mysql')
                ]);
                // If accepted, add member via action
                if ($status === 'accepted') {
                    do_action(
                        BASE . 'add_user_to_term',
                        $requestData['user_id'],
                        $requestData['term_id'],
                        $this->taxonomy,
                        'member'
                    );
                }
            });
            // Notify user
            JVB()->notification()->addNotification(
                $requestData['user_id'],
                'request_' . $status,
                [
                    'term_id' => $requestData['term_id'],
                    'taxonomy' => $this->taxonomy,
                    'notes' => $notes
                ]
            );
            $this->cache->flush();
            return $this->success(['message' => 'Request ' . $status]);
        } catch (Exception $e) {
            $this->logError('processRequest', [
                'error' => $e->getMessage(),
                'request_id' => $requestID,
                'action' => $action
            ]);
            return $this->error('Failed to process request');
        }
    }
    /**
     * Update ownership/management permissions
     */
    public function updatePermissions(WP_REST_Request $request): WP_REST_Response
    {
        $termID = (int)$request->get_param('term_id');
        $targetUserID = (int)$request->get_param('target_user');
        $role = $request->get_param('role');
        $grant = (bool)$request->get_param('grant');
        if (!$this->checkUser($targetUserID)) {
            return $this->error('Invalid target user');
        }
        $roleManager = JVB()->roles();
        try {
            $result = match($role) {
                'owner' => $grant
                    ? $roleManager->grantOwnership($targetUserID, $termID, $this->taxonomy)
                    : $roleManager->revokeOwnership($targetUserID, $termID, $this->taxonomy),
                'manager' => $grant
                    ? $roleManager->grantManagement($targetUserID, $termID, $this->taxonomy)
                    : $roleManager->revokeManagement($targetUserID, $termID, $this->taxonomy),
                default => false
            };
            if ($result) {
                $this->cache->flush();
                return $this->success([
                    'message' => ucfirst($role) . ' permissions ' . ($grant ? 'granted' : 'revoked')
                ]);
            }
            return $this->error('Failed to update permissions');
        } catch (Exception $e) {
            $this->logError('updatePermissions', [
                'error' => $e->getMessage(),
                'term_id' => $termID,
                'target_user' => $targetUserID,
                'role' => $role
            ]);
            return $this->error('An error occurred');
        }
    }
    /**
     * Notify term managers
     */
    protected function notifyTermManagers(int $termID, int $actingUserID, string $notificationType): void
    {
        $roleManager = JVB()->roles();
        $managers = $roleManager->getManagedTerms($actingUserID, $this->taxonomy);
        $term = get_term($termID, jvbCheckBase($this->taxonomy));
        foreach ($managers as $managerID) {
            JVB()->notification()->addNotification(
                $managerID,
                $notificationType,
                [
                    'user_id' => $actingUserID,
                    'term_id' => $termID,
                    'term_name' => $term->name ?? 'Unknown',
                    'taxonomy' => $this->taxonomy
                ]
            );
        }
    }
}
inc/rest/routes/LoginRoutes.php
@@ -112,36 +112,14 @@
     */
    public function getAuthStatus(WP_REST_Request $request): WP_REST_Response
    {
        $user = wp_get_current_user();
        $authenticated = $user->exists();
        $data = $this->buildAuth();
        $response = $this->success($data);
        $response = [
            'authenticated' => $authenticated,
            'user' => false,
            'nonces' => [
                'wp_rest' => wp_create_nonce('wp_rest'),
            ],
            'session_id' => session_id() ?: wp_generate_password(32, false),
        ];
        // Add caching headers
        $response->header('Cache-Control', 'private, max-age=300'); // 5 minutes
        $response->header('Vary', 'Cookie'); // Important for nginx
        if ($authenticated) {
            // Validate session fingerprint
            if (!$this->validateSessionFingerprint($user->ID, $request)) {
                wp_logout();
                $response['authenticated'] = false;
                $response['session_invalid'] = true;
            } else {
                $response['user'] = [
                    'id' => $user->ID,
                    'name' => $user->display_name,
                    'email' => $user->user_email,
                    'roles' => $user->roles,
                    'link' => get_user_meta($user->ID, BASE . 'link', true),
                ];
            }
        }
        return $this->success($response);
        return $response;
    }
    /**
@@ -201,20 +179,7 @@
        return $this->success([
            'message' => 'Login successful',
            'redirect' => $redirect,
            'auth' => [
                'authenticated' => true,
                'user' => [
                    'id' => $user->ID,
                    'name' => $user->display_name,
                    'email' => $user->user_email,
                    'roles' => $user->roles,
                    'link' => get_user_meta($user->ID, BASE . 'link', true),
                ],
                'nonces' => [
                    'wp_rest' => wp_create_nonce('wp_rest'),
                ],
                'session_id' => session_id() ?: wp_generate_password(32, false),
            ]
            'auth' => $this->buildAuth($user->ID)
        ]);
    }
@@ -757,4 +722,44 @@
        return wp_mail($user->user_email, $subject, $message);
    }
    protected function buildAuth(?int $user = null): array
    {
        if (is_user_logged_in()) {
            $user = ($user) ?: get_current_user_id();
            return [
                'authenticated' => true,
                'user' => $user,
                'nonces' => $this->getUserNonces($user)
            ];
        }
        return [
            'authenticated' => false,
            'user' => false,
            'nonces' => [
                'wp_rest' => wp_create_nonce('wp_rest')
            ]
        ];
    }
    protected function getUserNonces(int $userID):array {
        $nonces = [
            'wp_rest'   => wp_create_nonce('wp_rest'),
        ];
        if (Features::forSite()->has('dashboard')) {
            $nonces['dash'] = wp_create_nonce('dash-'.$userID);
        }
        if (Features::forSite()->has('favourites')) {
            $nonces['favourites'] = wp_create_nonce('favourites-'.$userID);
        }
        if (Features::anyContentHas('karma') ||
            Features::anyTaxonomyHas('karma') ||
            Features::anyUserHas('karma')) {
            $nonces['votes'] = wp_create_nonce('votes-'.$userID);
        }
        if (Features::forSite()->has('notifications')) {
            $nonces['notifications'] = wp_create_nonce('notifications-'.$userID);
        }
        return $nonces;
    }
}
inc/rest/routes/QueueRoutes.php
@@ -85,7 +85,9 @@
        $params = $request->get_params();
        $user_id = absint($params['user']);
        $status = sanitize_text_field($params['status']);
        $ids = array_map('trim', array_map('sanitize_text_field', explode(',', $params['ids'])));
        $ids = !empty($params['ids'])
            ? array_map('trim', array_map('sanitize_text_field', explode(',', $params['ids'])))
            : [];
        $limit = absint($params['limit']);
        $cacheKey = $this->cache->generateKey(compact('user_id', 'status', 'ids', 'limit'));
@@ -93,7 +95,7 @@
            return $cached;
        }
        $data = $this->cache->remember($cacheKey, function() use ($user_id, $params) {
        $data = $this->cache->tag("user:{$user_id}")->remember($cacheKey, function() use ($user_id, $params) {
            $filters = $this->buildFilters($params);
            $operations = JVB()->queue()->getUserOperations($user_id, $filters);
@@ -136,7 +138,10 @@
    public function handleAction(WP_REST_Request $request): WP_REST_Response
    {
        $data = $request->get_params();
        $ids = array_map('trim', array_map('sanitize_text_field', explode(',', $data['ids'])));
        $ids = is_array($data['ids'])
            ? array_map('trim', array_map('sanitize_text_field', $data['ids']))
            : array_map('trim', array_map('sanitize_text_field', explode(',', $data['ids'])));
        $action = sanitize_text_field($data['action'] ?? '');
        $user_id = absint($data['user']);
@@ -192,6 +197,7 @@
        $items = array_map(fn($op) => [
            'id' => $op->id,
            'status' => $this->mapStateToStatus($op->state, $op->outcome),
            'progress_percentage'   => $this->formatPercentage($op),
            'progress_count' => $op->processedItems,
            'count' => $op->totalItems,
            'updated_at' => $this->formatTimestamp($op->completedAt ?? $op->startedAt ?? $op->scheduledAt),
@@ -255,21 +261,22 @@
        );
    }
    private function formatOperation(Operation $op, bool $full = false): array
    private function formatOperation(Operation $op, bool $full = true): array
    {
        $formatted = [
            'id' => $op->id,
            'type' => $op->type,
            'status' => $this->mapStateToStatus($op->state, $op->outcome),
            'progress_count' => $op->processedItems,
            'progress_percentage'   => $this->formatPercentage($op),
            'count' => $op->totalItems,
            'title' => $this->getOperationTitle($op->type, $op->requestData),
            'created_at' => $this->formatTimestamp($op->scheduledAt),
            'updated_at' => $this->formatTimestamp($op->completedAt ?? $op->startedAt ?? $op->scheduledAt),
        ];
        if ($op->processedItems > 0 && $op->totalItems > 0) {
            $formatted['progress_percentage'] = round(($op->processedItems / $op->totalItems) * 100);
        if (!empty($op->result['merged_into'])) {
            $formatted['merged_into'] = $op->result['merged_into'];
        }
        if ($op->errorMessage) {
@@ -292,6 +299,18 @@
        return $formatted;
    }
    protected function formatPercentage(Operation $op):int
    {
        if ($op->totalItems > 0){
            return round(($op->processedItems / $op->totalItems) * 100);
        } else {
            return match($op->state) {
                'pending','scheduled=' => 0,
                'processing' => 45,
                'completed' => 100
            };
        }
    }
    /**
     * Map backend state/outcome to frontend status
     * Backend uses: state (pending, scheduled, processing, completed) + outcome (pending, success, partial, failed, failed_permanent)
inc/rest/routes/UploadRoutes.php
@@ -75,7 +75,9 @@
        // Process upload groups into posts
        $registry->register('process_upload_groups', new TypeConfig(
            executor: $executor
            executor: $executor,
            chunkKey: 'posts',
            chunkSize: 5
        ));
    }
@@ -1137,7 +1139,8 @@
                [
                    'operation_id' => $args['id'],
                    'depends_on' => [$args['upload']],
                    'priority' => 'high'
                    'priority' => 'high',
                    'chunk_key' => 'posts'
                ]
            );
inc/ui/CRUDSkeleton.php
@@ -573,7 +573,7 @@
            <?php
            echo Form::render(
                'new_' . $this->dataType,
                null,
                '',
                $this->uploaderConfig
            );
            ?>
@@ -1249,7 +1249,7 @@
                                $config['autocomplete'] = true;
                            }
                            echo Form::render($name, null, $config);
                            echo Form::render($name, '', $config);
                            echo $makeThisDetailed ? '</details>' : '';
                        } else {
                            echo '<p></p>';
@@ -1332,7 +1332,7 @@
                            $config['autocomplete'] = true;
                        }
                        ?>
                        <?= Form::render($name, null, $config); ?>
                        <?= Form::render($name, '', $config); ?>
                        <?= $makeThisDetailed ? '</details>' : '' ?>
                    </td>
                    <?php
@@ -1360,7 +1360,7 @@
                    ?>
                    <td class="field show-<?= esc_attr($name) ?>" data-field="<?= esc_attr($name) ?>" data-field-type="<?=$config['type']?>"<?=(in_array($name, $this->stuck)) ? ' data-stuck':''?>>
                        <?= $makeThisDetailed ? '<details><summary class="row btw">See Value</summary>' : '' ?>
                        <?= Form::render($name, null, $config); ?>
                        <?= Form::render($name, '', $config); ?>
                        <?= $makeThisDetailed ? '</details>' : '' ?>
                    </td>
                    <?php
@@ -1584,9 +1584,9 @@
                    foreach ($first as $f) {
                        if (array_key_exists($f, $fields)) {
                            if ($tabs) {
                                $tabs['basic']['content'] .= Form::render($f, null, $fields[$f]);
                                $tabs['basic']['content'] .= Form::render($f, '', $fields[$f]);
                            } else {
                                Form::render($f, null, $fields[$f]);
                                Form::render($f, '', $fields[$f]);
                            }
                            unset($fields[$f]);
@@ -1599,7 +1599,7 @@
                        return in_array($field, $this->timelineUniqueFields);
                    }, ARRAY_FILTER_USE_KEY);
                    $config = [
                        'type'      => 'gallery',
                        'type'      => 'upload',
                        'subtype'   => 'timeline',
                        'data'      => 'timeline',
                        'label'     => 'Progression',
@@ -1611,12 +1611,12 @@
                            if (in_array($field['type'], ['taxonomy', 'selector'])) {
                                $field = array_merge($field, $this->taxConfig($field['taxonomy'], $field['label']));
                            }
                            $content .= Form::render($slug, null, $field);
                            $content .= Form::render($slug, '', $field);
                        }
                    }
                    $content .= Form::render('timeline', null, $config);
                    $content .= Form::render('timeline', '', $config);
                    $tabs['progression']['content'] = $content;
                    $fields = $this->nonTimelineFields;
@@ -1624,12 +1624,12 @@
                foreach ($fields as $n => $config) {
                    if ($tabs) {
                        $section = (array_key_exists('section', $config)) ? $config['section'] : 'basic';
                        $tabs[$section]['content'] .= Form::render($n,null, $config);
                        $tabs[$section]['content'] .= Form::render($n,'', $config);
                    } else {
                        if (in_array($config['type'], ['taxonomy', 'selector'])) {
                            $config = array_merge($config, $this->taxConfig($config['taxonomy'], $config['label']));
                        }
                        Form::render($n, null, $config);
                        Form::render($n, '', $config);
                    }
                }
@@ -1686,7 +1686,7 @@
                    return array_key_exists('bulkEdit', $field);
                });
                foreach ($fields as $fieldName => $config) {
                    echo Form::render($fieldName, null, $config);
                    echo Form::render($fieldName, '', $config);
                }
                ?>
            </div>