From 0837f4f5298c4bf947c231e1211ad9bf6e0f60bc Mon Sep 17 00:00:00 2001 From: Mark Rapson Date: Mon, 13 Jul 2026 21:44:49 +0100 Subject: [PATCH] Initial Product release --- .dockerignore | 6 + .env.example | 21 + .gitignore | 7 + Dockerfile | 14 + docker-compose.yml | 79 +- package-lock.json | 1604 ++++++++++++++ package.json | 25 + src/auth.js | 43 + src/common.js | 26 + src/data/admin.js | 38 + src/data/canvas-sizes.js | 33 + src/data/index.js | 26 + src/data/playlists.js | 8 + src/data/screens.js | 44 + src/data/slides.js | 158 ++ src/data/templates.js | 169 ++ src/data/utils.js | 28 + src/db.js | 233 ++ src/player-render.js | 1236 +++++++++++ src/player.js | 487 +++++ src/player/public/css/player.css | 253 +++ src/server.js | 1 + src/webui.js | 1704 +++++++++++++++ src/webui/public/css/admin-editor.css | 356 ++++ src/webui/public/css/admin.css | 1887 +++++++++++++++++ src/webui/public/css/template-designer.css | 245 +++ src/webui/public/js/admin-page.js | 942 ++++++++ .../public/js/compact/editor.header.min.js | 15 + .../public/js/compact/editor.list.min.js | 8 + .../public/js/compact/editor.marker.min.js | 8 + src/webui/public/js/compact/editor.min.js | 8 + .../public/js/compact/editor.table.min.js | 8 + src/webui/public/js/playlist-schedule.js | 527 +++++ src/webui/public/js/slide-form.js | 602 ++++++ src/webui/public/js/template-designer.js | 467 ++++ src/webui/routes/account/password.js | 12 + src/webui/routes/auth/login.js | 10 + src/webui/routes/canvas-sizes/add.js | 21 + src/webui/routes/canvas-sizes/edit.js | 5 + src/webui/routes/canvas-sizes/list.js | 19 + src/webui/routes/clients/list.js | 11 + src/webui/routes/common.js | 56 + src/webui/routes/dashboard/index.js | 15 + src/webui/routes/index.js | 24 + src/webui/routes/playlists/add.js | 14 + src/webui/routes/playlists/edit.js | 116 + src/webui/routes/playlists/list.js | 21 + src/webui/routes/playlists/slide-config.js | 65 + src/webui/routes/screens/add.js | 12 + src/webui/routes/screens/edit.js | 13 + src/webui/routes/screens/list.js | 12 + src/webui/routes/slides/form.js | 27 + src/webui/routes/slides/list.js | 12 + src/webui/routes/templates/add.js | 65 + src/webui/routes/templates/edit.js | 5 + src/webui/routes/templates/list.js | 19 + src/webui/routes/users/add.js | 10 + src/webui/routes/users/edit.js | 11 + src/webui/routes/users/list.js | 11 + src/webui/view.js | 68 + src/webui/views/account/password.hbs | 39 + src/webui/views/canvas-sizes/add.hbs | 24 + src/webui/views/canvas-sizes/edit.hbs | 24 + src/webui/views/canvas-sizes/list.hbs | 35 + src/webui/views/clients/list.hbs | 98 + src/webui/views/dashboard/index.hbs | 55 + src/webui/views/frame-layout.hbs | 65 + src/webui/views/layout.hbs | 127 ++ src/webui/views/login.hbs | 28 + src/webui/views/playlists/add.hbs | 24 + src/webui/views/playlists/edit.hbs | 119 ++ src/webui/views/playlists/list.hbs | 34 + src/webui/views/playlists/slide-config.hbs | 48 + src/webui/views/screens/add.hbs | 24 + src/webui/views/screens/edit.hbs | 37 + src/webui/views/screens/list.hbs | 42 + src/webui/views/slides/form.hbs | 50 + src/webui/views/slides/list.hbs | 34 + src/webui/views/templates/add.hbs | 72 + src/webui/views/templates/edit.hbs | 72 + src/webui/views/templates/list.hbs | 35 + src/webui/views/users/add.hbs | 43 + src/webui/views/users/edit.hbs | 49 + src/webui/views/users/list.hbs | 62 + 84 files changed, 13174 insertions(+), 36 deletions(-) create mode 100644 .dockerignore create mode 100644 .env.example create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 src/auth.js create mode 100644 src/common.js create mode 100644 src/data/admin.js create mode 100644 src/data/canvas-sizes.js create mode 100644 src/data/index.js create mode 100644 src/data/playlists.js create mode 100644 src/data/screens.js create mode 100644 src/data/slides.js create mode 100644 src/data/templates.js create mode 100644 src/data/utils.js create mode 100644 src/db.js create mode 100644 src/player-render.js create mode 100644 src/player.js create mode 100644 src/player/public/css/player.css create mode 100644 src/server.js create mode 100644 src/webui.js create mode 100644 src/webui/public/css/admin-editor.css create mode 100644 src/webui/public/css/admin.css create mode 100644 src/webui/public/css/template-designer.css create mode 100644 src/webui/public/js/admin-page.js create mode 100644 src/webui/public/js/compact/editor.header.min.js create mode 100644 src/webui/public/js/compact/editor.list.min.js create mode 100644 src/webui/public/js/compact/editor.marker.min.js create mode 100644 src/webui/public/js/compact/editor.min.js create mode 100644 src/webui/public/js/compact/editor.table.min.js create mode 100644 src/webui/public/js/playlist-schedule.js create mode 100644 src/webui/public/js/slide-form.js create mode 100644 src/webui/public/js/template-designer.js create mode 100644 src/webui/routes/account/password.js create mode 100644 src/webui/routes/auth/login.js create mode 100644 src/webui/routes/canvas-sizes/add.js create mode 100644 src/webui/routes/canvas-sizes/edit.js create mode 100644 src/webui/routes/canvas-sizes/list.js create mode 100644 src/webui/routes/clients/list.js create mode 100644 src/webui/routes/common.js create mode 100644 src/webui/routes/dashboard/index.js create mode 100644 src/webui/routes/index.js create mode 100644 src/webui/routes/playlists/add.js create mode 100644 src/webui/routes/playlists/edit.js create mode 100644 src/webui/routes/playlists/list.js create mode 100644 src/webui/routes/playlists/slide-config.js create mode 100644 src/webui/routes/screens/add.js create mode 100644 src/webui/routes/screens/edit.js create mode 100644 src/webui/routes/screens/list.js create mode 100644 src/webui/routes/slides/form.js create mode 100644 src/webui/routes/slides/list.js create mode 100644 src/webui/routes/templates/add.js create mode 100644 src/webui/routes/templates/edit.js create mode 100644 src/webui/routes/templates/list.js create mode 100644 src/webui/routes/users/add.js create mode 100644 src/webui/routes/users/edit.js create mode 100644 src/webui/routes/users/list.js create mode 100644 src/webui/view.js create mode 100644 src/webui/views/account/password.hbs create mode 100644 src/webui/views/canvas-sizes/add.hbs create mode 100644 src/webui/views/canvas-sizes/edit.hbs create mode 100644 src/webui/views/canvas-sizes/list.hbs create mode 100644 src/webui/views/clients/list.hbs create mode 100644 src/webui/views/dashboard/index.hbs create mode 100644 src/webui/views/frame-layout.hbs create mode 100644 src/webui/views/layout.hbs create mode 100644 src/webui/views/login.hbs create mode 100644 src/webui/views/playlists/add.hbs create mode 100644 src/webui/views/playlists/edit.hbs create mode 100644 src/webui/views/playlists/list.hbs create mode 100644 src/webui/views/playlists/slide-config.hbs create mode 100644 src/webui/views/screens/add.hbs create mode 100644 src/webui/views/screens/edit.hbs create mode 100644 src/webui/views/screens/list.hbs create mode 100644 src/webui/views/slides/form.hbs create mode 100644 src/webui/views/slides/list.hbs create mode 100644 src/webui/views/templates/add.hbs create mode 100644 src/webui/views/templates/edit.hbs create mode 100644 src/webui/views/templates/list.hbs create mode 100644 src/webui/views/users/add.hbs create mode 100644 src/webui/views/users/edit.hbs create mode 100644 src/webui/views/users/list.hbs diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..74ce70f --- /dev/null +++ b/.dockerignore @@ -0,0 +1,6 @@ +node_modules +npm-debug.log +uploads +.git +.gitignore +*.tmp diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..4ce5263 --- /dev/null +++ b/.env.example @@ -0,0 +1,21 @@ +NODE_ENV=production +WEB_PORT=3000 +PLAYER_PORT=3001 + +DB_HOST=mysql +DB_PORT=3306 +DB_NAME=signage +DB_USER=signage_user +DB_PASSWORD=signage_password +MYSQL_ROOT_PASSWORD=root_password + +PLAYER_INTERNAL_BASE_URL=http://player:3001 +PLAYER_PUBLIC_BASE_URL=http://localhost:3001 +UPLOAD_DIR=/app/uploads + +SESSION_MAX_AGE_DAYS=14 +DASHBOARD_REFRESH_INTERVAL_MS=2000 +DEFAULT_ADMIN_USERNAME=admin +DEFAULT_ADMIN_NAME=Admin +DEFAULT_ADMIN_PASSWORD=admin +PASSWORD_HASH_ITERATIONS=310000 \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b374877 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +node_modules/ +uploads/ +.env +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.DS_Store diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..1b5843e --- /dev/null +++ b/Dockerfile @@ -0,0 +1,14 @@ +FROM node:24-alpine + +WORKDIR /app + +COPY package*.json ./ +RUN npm ci --omit=dev + +COPY src ./src + +RUN mkdir -p /app/uploads + +EXPOSE 3000 + +CMD ["npm", "run", "start:webui"] diff --git a/docker-compose.yml b/docker-compose.yml index 12ce8f1..f46c7e6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,32 +1,56 @@ version: "3.9" services: - app: + webui: build: context: . dockerfile: Dockerfile - container_name: signage-app + container_name: signage-webui restart: unless-stopped ports: - "3000:3000" environment: - NODE_ENV: production - PORT: 3000 - - DB_HOST: mysql - DB_PORT: 3306 - DB_NAME: signage - DB_USER: signage_user - DB_PASSWORD: signage_password - - REDIS_URL: redis://redis:6379 - UPLOAD_DIR: /app/uploads + NODE_ENV: ${NODE_ENV:-production} + WEB_PORT: ${WEB_PORT:-3000} + DB_HOST: ${DB_HOST:-mysql} + DB_PORT: ${DB_PORT:-3306} + DB_NAME: ${DB_NAME:-signage} + DB_USER: ${DB_USER:-signage_user} + DB_PASSWORD: ${DB_PASSWORD:-signage_password} + PLAYER_INTERNAL_BASE_URL: ${PLAYER_INTERNAL_BASE_URL:-http://player:3001} + PLAYER_PUBLIC_BASE_URL: ${PLAYER_PUBLIC_BASE_URL:-http://localhost:3001} + UPLOAD_DIR: ${UPLOAD_DIR:-/app/uploads} volumes: - uploads:/app/uploads + command: ["npm", "run", "start:webui"] depends_on: mysql: condition: service_healthy - redis: + networks: + - signage-net + + player: + build: + context: . + dockerfile: Dockerfile + container_name: signage-player + restart: unless-stopped + ports: + - "3001:3001" + environment: + NODE_ENV: ${NODE_ENV:-production} + PLAYER_PORT: ${PLAYER_PORT:-3001} + DB_HOST: ${DB_HOST:-mysql} + DB_PORT: ${DB_PORT:-3306} + DB_NAME: ${DB_NAME:-signage} + DB_USER: ${DB_USER:-signage_user} + DB_PASSWORD: ${DB_PASSWORD:-signage_password} + UPLOAD_DIR: ${UPLOAD_DIR:-/app/uploads} + volumes: + - uploads:/app/uploads + command: ["npm", "run", "start:player"] + depends_on: + mysql: condition: service_healthy networks: - signage-net @@ -38,10 +62,10 @@ services: ports: - "3306:3306" environment: - MYSQL_DATABASE: signage - MYSQL_USER: signage_user - MYSQL_PASSWORD: signage_password - MYSQL_ROOT_PASSWORD: root_password + MYSQL_DATABASE: ${DB_NAME:-signage} + MYSQL_USER: ${DB_USER:-signage_user} + MYSQL_PASSWORD: ${DB_PASSWORD:-signage_password} + MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-root_password} command: - --default-authentication-plugin=mysql_native_password - --character-set-server=utf8mb4 @@ -49,23 +73,7 @@ services: volumes: - mysql_data:/var/lib/mysql healthcheck: - test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-uroot", "-proot_password"] - interval: 10s - timeout: 5s - retries: 10 - networks: - - signage-net - - redis: - image: redis:7-alpine - container_name: signage-redis - restart: unless-stopped - ports: - - "6379:6379" - volumes: - - redis_data:/data - healthcheck: - test: ["CMD", "redis-cli", "ping"] + test: ["CMD-SHELL", "mysqladmin ping -h localhost -uroot -p$$MYSQL_ROOT_PASSWORD"] interval: 10s timeout: 5s retries: 10 @@ -74,7 +82,6 @@ services: volumes: mysql_data: - redis_data: uploads: networks: diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..b78198c --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1604 @@ +{ + "name": "pulse-signage", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "pulse-signage", + "version": "1.0.0", + "dependencies": { + "dotenv": "^17.4.2", + "express": "^4.21.2", + "handlebars": "^4.7.8", + "multer": "^1.4.5-lts.1", + "mysql2": "^3.14.3", + "ws": "^8.21.0" + }, + "devDependencies": { + "nodemon": "^3.1.10" + } + }, + "node_modules/@types/node": { + "version": "26.1.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.1.1.tgz", + "integrity": "sha512-nxAkRSVkN1Y0JC1W8ky/fTfkGsMmcrRsbx+3XoZE+rMOX71kLYTV7fLXpqud1GpbpP5TuffXFqfX7fH2GgZREw==", + "license": "MIT", + "peer": true, + "dependencies": { + "undici-types": "~8.3.0" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/append-field": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", + "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==", + "license": "MIT" + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "license": "MIT" + }, + "node_modules/aws-ssl-profiles": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/aws-ssl-profiles/-/aws-ssl-profiles-1.1.2.tgz", + "integrity": "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==", + "license": "MIT", + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/body-parser": { + "version": "1.20.6", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.6.tgz", + "integrity": "sha512-p5tAzS57i5MV9fZFDj9LeIiTZEufbSe2eDozP+ElheSUq1m74CRq1jI4mYNDdVs9vQztXFLuk/Gd6BWTdwRJ5g==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.15.1", + "raw-body": "~2.5.3", + "type-is": "~1.6.18", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/brace-expansion": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz", + "integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "license": "MIT" + }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "engines": [ + "node >= 0.8" + ], + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", + "license": "MIT" + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/dotenv": { + "version": "17.4.2", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.4.2.tgz", + "integrity": "sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "4.22.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.2.tgz", + "integrity": "sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "~1.20.5", + "content-disposition": "~0.5.4", + "content-type": "~1.0.4", + "cookie": "~0.7.1", + "cookie-signature": "~1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.3.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "~0.1.12", + "proxy-addr": "~2.0.7", + "qs": "~6.15.1", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "~0.19.0", + "serve-static": "~1.16.2", + "setprototypeof": "1.2.0", + "statuses": "~2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", + "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "statuses": "~2.0.2", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/generate-function": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", + "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", + "license": "MIT", + "dependencies": { + "is-property": "^1.0.2" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/handlebars": { + "version": "4.7.9", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.9.tgz", + "integrity": "sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==", + "license": "MIT", + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", + "dev": true, + "license": "ISC" + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==", + "license": "MIT" + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "license": "Apache-2.0" + }, + "node_modules/lru.min": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/lru.min/-/lru.min-1.1.4.tgz", + "integrity": "sha512-DqC6n3QQ77zdFpCMASA1a3Jlb64Hv2N2DciFGkO/4L9+q/IpIAuRlKOvCXabtRW6cQf8usbmM6BE/TOPysCdIA==", + "license": "MIT", + "engines": { + "bun": ">=1.0.0", + "deno": ">=1.30.0", + "node": ">=8.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wellwelwel" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "license": "MIT", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/multer": { + "version": "1.4.5-lts.2", + "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.2.tgz", + "integrity": "sha512-VzGiVigcG9zUAoCNU+xShztrlr1auZOlurXynNvO9GiWD1/mTBbUljOKY+qMeazBqXgRnjzeEgJI/wyjJUHg9A==", + "deprecated": "Multer 1.x is impacted by a number of vulnerabilities, which have been patched in 2.x. You should upgrade to the latest 2.x version.", + "license": "MIT", + "dependencies": { + "append-field": "^1.0.0", + "busboy": "^1.0.0", + "concat-stream": "^1.5.2", + "mkdirp": "^0.5.4", + "object-assign": "^4.1.1", + "type-is": "^1.6.4", + "xtend": "^4.0.0" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/mysql2": { + "version": "3.22.6", + "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.22.6.tgz", + "integrity": "sha512-fPKmeDGUzvFP7bMD5SASlJ5zIgvCC4hbanTmhbUlEmhyrY1hR4Hi3xLOWgTd3luYjLVifx6uGvMNJ2m/LlqEpg==", + "license": "MIT", + "dependencies": { + "aws-ssl-profiles": "^1.1.2", + "denque": "^2.1.0", + "generate-function": "^2.3.1", + "iconv-lite": "^0.7.2", + "long": "^5.3.2", + "lru.min": "^1.1.4", + "named-placeholders": "^1.1.6", + "sql-escaper": "^1.4.0" + }, + "engines": { + "node": ">= 8.0" + }, + "peerDependencies": { + "@types/node": ">= 8" + } + }, + "node_modules/mysql2/node_modules/iconv-lite": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.3.tgz", + "integrity": "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/named-placeholders": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.6.tgz", + "integrity": "sha512-Tz09sEL2EEuv5fFowm419c1+a/jSMiBjI9gHxVLrVdbUkkNUUfjsVYs9pVZu5oCon/kmRh9TfLEObFtkVxmY0w==", + "license": "MIT", + "dependencies": { + "lru.min": "^1.1.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "license": "MIT" + }, + "node_modules/nodemon": { + "version": "3.1.14", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.14.tgz", + "integrity": "sha512-jakjZi93UtB3jHMWsXL68FXSAosbLfY0In5gtKq3niLSkrWznrVBzXFNOEMJUfc9+Ke7SHWoAZsiMkNP3vq6Jw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^4", + "ignore-by-default": "^1.0.1", + "minimatch": "^10.2.1", + "pstree.remy": "^1.1.8", + "semver": "^7.5.3", + "simple-update-notifier": "^2.0.0", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/nodemon/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/nodemon/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz", + "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==", + "license": "MIT" + }, + "node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true, + "license": "MIT" + }, + "node_modules/qs": { + "version": "6.15.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz", + "integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==", + "license": "BSD-3-Clause", + "dependencies": { + "es-define-property": "^1.0.1", + "side-channel": "^1.1.1" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", + "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.4.1", + "range-parser": "~1.2.1", + "statuses": "~2.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/serve-static": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", + "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "~0.19.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/side-channel": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", + "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4", + "side-channel-list": "^1.0.1", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sql-escaper": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/sql-escaper/-/sql-escaper-1.5.0.tgz", + "integrity": "sha512-JCY91/D1lVq7p6RzuHg9XFQH9vT9L22bljsk+uTdMWke4ZqPCCE+iIMNPmJPUUTywn6eW2nncaWc+MizKZgF4A==", + "license": "MIT", + "engines": { + "bun": ">=1.0.0", + "deno": ">=2.0.0", + "node": ">=12.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/mysqljs/sql-escaper?sponsor=1" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/touch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", + "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", + "dev": true, + "license": "ISC", + "bin": { + "nodetouch": "bin/nodetouch.js" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "license": "MIT" + }, + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "license": "BSD-2-Clause", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "dev": true, + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz", + "integrity": "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==", + "license": "MIT", + "peer": true + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "license": "MIT" + }, + "node_modules/ws": { + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz", + "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "license": "MIT", + "engines": { + "node": ">=0.4" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..640bfb9 --- /dev/null +++ b/package.json @@ -0,0 +1,25 @@ +{ + "name": "pulse-signage", + "version": "1.0.0", + "private": true, + "description": "Pulse Signage application with MySQL and media uploads", + "main": "src/server.js", + "scripts": { + "start": "node -r dotenv/config src/webui.js", + "start:webui": "node -r dotenv/config src/webui.js", + "start:player": "node -r dotenv/config src/player.js", + "dev:webui": "nodemon -r dotenv/config src/webui.js", + "dev:player": "nodemon -r dotenv/config src/player.js" + }, + "dependencies": { + "dotenv": "^17.4.2", + "express": "^4.21.2", + "handlebars": "^4.7.8", + "multer": "^1.4.5-lts.1", + "mysql2": "^3.14.3", + "ws": "^8.21.0" + }, + "devDependencies": { + "nodemon": "^3.1.10" + } +} diff --git a/src/auth.js b/src/auth.js new file mode 100644 index 0000000..1b53a10 --- /dev/null +++ b/src/auth.js @@ -0,0 +1,43 @@ +const crypto = require('crypto'); + +const PASSWORD_ITERATIONS = Number(process.env.PASSWORD_HASH_ITERATIONS || 310000); +const PASSWORD_KEY_LENGTH = 32; +const PASSWORD_DIGEST = 'sha256'; +const SESSION_BYTES = 32; + +function hashPassword(password, salt) { + const safePassword = String(password || ''); + const safeSalt = salt || crypto.randomBytes(16).toString('hex'); + const hash = crypto.pbkdf2Sync(safePassword, safeSalt, PASSWORD_ITERATIONS, PASSWORD_KEY_LENGTH, PASSWORD_DIGEST).toString('hex'); + return { + salt: safeSalt, + hash: hash, + iterations: PASSWORD_ITERATIONS + }; +} + +function verifyPassword(password, record) { + if (!record || !record.password_salt || !record.password_hash) { + return false; + } + const iterations = Number(record.password_iterations || PASSWORD_ITERATIONS); + const safePassword = String(password || ''); + const expected = crypto.pbkdf2Sync(safePassword, String(record.password_salt), iterations, PASSWORD_KEY_LENGTH, PASSWORD_DIGEST); + const actual = Buffer.from(String(record.password_hash), 'hex'); + return actual.length === expected.length && crypto.timingSafeEqual(actual, expected); +} + +function createSessionToken() { + return crypto.randomBytes(SESSION_BYTES).toString('hex'); +} + +function hashSessionToken(token) { + return crypto.createHash('sha256').update(String(token || '')).digest('hex'); +} + +module.exports = { + hashPassword, + verifyPassword, + createSessionToken, + hashSessionToken +}; \ No newline at end of file diff --git a/src/common.js b/src/common.js new file mode 100644 index 0000000..93e900a --- /dev/null +++ b/src/common.js @@ -0,0 +1,26 @@ +const db = require('./db'); +const data = require('./data'); +const player = require('./player-render'); + +module.exports = { + createPool: db.createPool, + ensureSchema: db.ensureSchema, + slugify: data.slugify, + uniqueScreenSlug: data.uniqueScreenSlug, + parseJsonSafe: data.parseJsonSafe, + fetchAdminData: data.fetchAdminData, + fetchPlaylistById: data.fetchPlaylistById, + fetchScreenById: data.fetchScreenById, + fetchScreenEditData: data.fetchScreenEditData, + fetchTemplateById: data.fetchTemplateById, + fetchSlideById: data.fetchSlideById, + fetchTemplatesData: data.fetchTemplatesData, + fetchCanvasSizesData: data.fetchCanvasSizesData, + fetchCanvasSizeById: data.fetchCanvasSizeById, + buildCanvasSizePayload: data.buildCanvasSizePayload, + buildSlidePayload: data.buildSlidePayload, + extractTemplateRegions: data.extractTemplateRegions, + buildTemplatePayload: data.buildTemplatePayload, + mediaKind: player.mediaKind, + renderPlayerPage: player.renderPlayerPage +}; diff --git a/src/data/admin.js b/src/data/admin.js new file mode 100644 index 0000000..9603a92 --- /dev/null +++ b/src/data/admin.js @@ -0,0 +1,38 @@ +async function fetchAdminData(pool) { + const [playlists] = await pool.query('SELECT * FROM playlists ORDER BY id DESC'); + const [canvasSizes] = await pool.query('SELECT * FROM canvas_sizes ORDER BY width ASC, height ASC, name ASC'); + const [templates] = await pool.query(` + SELECT st.id, st.name, st.canvas_size_id, st.background_image_path, st.created_at, st.modified_at, + cs.name AS canvas_size_name, cs.width AS canvas_size_width, cs.height AS canvas_size_height + FROM slide_templates st + LEFT JOIN canvas_sizes cs ON cs.id = st.canvas_size_id + ORDER BY st.id DESC + `); + const [templateRegions] = await pool.query('SELECT * FROM slide_template_regions ORDER BY template_id ASC, z_index ASC, id ASC'); + const [slides] = await pool.query(` + SELECT s.*, st.name AS template_name, cs.width AS canvas_width, cs.height AS canvas_height + FROM slides s + LEFT JOIN slide_templates st ON st.id = s.template_id + LEFT JOIN canvas_sizes cs ON cs.id = st.canvas_size_id + ORDER BY s.id DESC + `); + const [screens] = await pool.query(` + SELECT s.*, p.name AS playlist_name + FROM screens s + LEFT JOIN playlists p ON p.id = s.playlist_id + ORDER BY s.id DESC + `); + const [playlistSlides] = await pool.query(` + SELECT ps.id, ps.playlist_id, ps.position, ps.duration_seconds, ps.schedule_mode, ps.schedule_start_datetime, ps.schedule_end_datetime, ps.schedule_start_time, ps.schedule_end_time, ps.schedule_days_json, sl.id AS slide_id, sl.title, sl.media_path, sl.media_type, cs.width AS canvas_width, cs.height AS canvas_height + FROM playlist_slides ps + JOIN slides sl ON sl.id = ps.slide_id + LEFT JOIN slide_templates st ON st.id = sl.template_id + LEFT JOIN canvas_sizes cs ON cs.id = st.canvas_size_id + ORDER BY ps.playlist_id ASC, ps.position ASC, ps.id ASC + `); + return { playlists, canvasSizes, templates, templateRegions, slides, screens, playlistSlides }; +} + +module.exports = { + fetchAdminData +}; diff --git a/src/data/canvas-sizes.js b/src/data/canvas-sizes.js new file mode 100644 index 0000000..0d4a119 --- /dev/null +++ b/src/data/canvas-sizes.js @@ -0,0 +1,33 @@ +async function fetchCanvasSizesData(pool) { + const [canvasSizes] = await pool.query('SELECT * FROM canvas_sizes ORDER BY width ASC, height ASC, name ASC'); + return { canvasSizes }; +} + +async function fetchCanvasSizeById(pool, id) { + const [rows] = await pool.query('SELECT * FROM canvas_sizes WHERE id = ?', [id]); + return rows[0] || null; +} + +function buildCanvasSizePayload(req, existingCanvasSize) { + const name = String(req.body.name || '').trim(); + const width = Math.max(1, Number(req.body.width || (existingCanvasSize && existingCanvasSize.width) || 0)); + const height = Math.max(1, Number(req.body.height || (existingCanvasSize && existingCanvasSize.height) || 0)); + + if (!name) { + const error = new Error('Canvas size name is required.'); + error.statusCode = 400; + throw error; + } + + return { + name, + width, + height + }; +} + +module.exports = { + fetchCanvasSizesData, + fetchCanvasSizeById, + buildCanvasSizePayload +}; diff --git a/src/data/index.js b/src/data/index.js new file mode 100644 index 0000000..5ce0687 --- /dev/null +++ b/src/data/index.js @@ -0,0 +1,26 @@ +const { fetchAdminData } = require('./admin'); +const { fetchPlaylistById } = require('./playlists'); +const { slugify, uniqueScreenSlug, fetchScreenById, fetchScreenEditData } = require('./screens'); +const { fetchTemplateById, fetchTemplatesData, extractTemplateRegions, buildTemplatePayload } = require('./templates'); +const { fetchCanvasSizesData, fetchCanvasSizeById, buildCanvasSizePayload } = require('./canvas-sizes'); +const { fetchSlideById, buildSlidePayload } = require('./slides'); +const { parseJsonSafe } = require('./utils'); + +module.exports = { + slugify, + uniqueScreenSlug, + parseJsonSafe, + fetchAdminData, + fetchPlaylistById, + fetchScreenById, + fetchScreenEditData, + fetchTemplateById, + fetchSlideById, + fetchTemplatesData, + fetchCanvasSizesData, + fetchCanvasSizeById, + buildCanvasSizePayload, + buildSlidePayload, + extractTemplateRegions, + buildTemplatePayload +}; diff --git a/src/data/playlists.js b/src/data/playlists.js new file mode 100644 index 0000000..cb72f87 --- /dev/null +++ b/src/data/playlists.js @@ -0,0 +1,8 @@ +async function fetchPlaylistById(pool, id) { + const [rows] = await pool.query('SELECT * FROM playlists WHERE id = ?', [id]); + return rows[0] || null; +} + +module.exports = { + fetchPlaylistById +}; diff --git a/src/data/screens.js b/src/data/screens.js new file mode 100644 index 0000000..159b0b5 --- /dev/null +++ b/src/data/screens.js @@ -0,0 +1,44 @@ +function slugify(value) { + return String(value || '') + .toLowerCase() + .trim() + .replace(/[^a-z0-9]+/g, '-') + .replace(/^-+|-+$/g, '') + .replace(/-{2,}/g, '-'); +} + +async function uniqueScreenSlug(pool, baseSlug) { + const start = baseSlug || `screen-${Date.now()}`; + let candidate = start; + let counter = 2; + while (true) { + const [rows] = await pool.query('SELECT id FROM screens WHERE slug = ?', [candidate]); + if (!rows.length) { + return candidate; + } + candidate = `${start}-${counter}`; + counter += 1; + } +} + +async function fetchScreenById(pool, id) { + const [rows] = await pool.query(` + SELECT s.*, p.name AS playlist_name + FROM screens s + LEFT JOIN playlists p ON p.id = s.playlist_id + WHERE s.id = ? + `, [id]); + return rows[0] || null; +} + +async function fetchScreenEditData(pool) { + const [playlists] = await pool.query('SELECT * FROM playlists ORDER BY id DESC'); + return { playlists }; +} + +module.exports = { + slugify, + uniqueScreenSlug, + fetchScreenById, + fetchScreenEditData +}; diff --git a/src/data/slides.js b/src/data/slides.js new file mode 100644 index 0000000..3b59867 --- /dev/null +++ b/src/data/slides.js @@ -0,0 +1,158 @@ +const { fetchTemplateById } = require('./templates'); +const { parseJsonSafe } = require('./utils'); + +const ALLOWED_RICH_TEXT_TAGS = ['b', 'strong', 'i', 'em', 'u', 'br', 'p', 'div', 'ul', 'ol', 'li']; + +function sanitizeRichText(html) { + let output = String(html || ''); + output = output.replace(//gi, ''); + output = output.replace(//gi, ''); + return output.replace(/<[^>]+>/g, (tag) => { + const match = tag.match(/^<\s*(\/?)\s*([a-z0-9]+)(?:\s[^>]*)?>$/i); + if (!match) { + return ''; + } + const closing = Boolean(match[1]); + const name = String(match[2] || '').toLowerCase(); + if (!ALLOWED_RICH_TEXT_TAGS.includes(name)) { + return ''; + } + if (name === 'br') { + return '
'; + } + return closing ? `` : `<${name}>`; + }); +} + +async function fetchSlideById(pool, id) { + const [slides] = await pool.query(` + SELECT s.id, s.title, s.body, s.template_id, s.content_json, s.media_path, s.media_type, s.created_at, s.modified_at, + st.name AS template_name, cs.name AS canvas_size_name, cs.width AS canvas_width, cs.height AS canvas_height + FROM slides s + LEFT JOIN slide_templates st ON st.id = s.template_id + LEFT JOIN canvas_sizes cs ON cs.id = st.canvas_size_id + WHERE s.id = ? + `, [id]); + if (!slides.length) { + return null; + } + const slide = slides[0]; + slide.content = parseJsonSafe(slide.content_json) || {}; + if (slide.template_id) { + slide.template = await fetchTemplateById(pool, slide.template_id); + } + return slide; +} + +function getFilesByField(files) { + const map = {}; + (files || []).forEach((file) => { + map[file.fieldname] = file; + }); + return map; +} + +function sanitizeTextColor(value, fallback) { + const raw = String(value || '').trim(); + if (/^#[0-9a-fA-F]{6}$/.test(raw) || /^#[0-9a-fA-F]{3}$/.test(raw)) { + return raw; + } + return fallback || '#000000'; +} + +function getTextRegionStyle(body, region, existingContent) { + const existing = existingContent && existingContent[region.region_key] ? existingContent[region.region_key] : {}; + const fontFamily = String(body[`region_font_family_${region.id}`] || existing.font_family || region.font_family || 'Arial').trim() || 'Arial'; + const fontSize = Math.max(8, Number(body[`region_font_size_${region.id}`] || existing.font_size || 24)); + const fontColor = sanitizeTextColor(body[`region_font_color_${region.id}`] || existing.font_color || region.font_color || '#000000'); + return { + font_family: fontFamily, + font_size: fontSize, + font_color: fontColor + }; +} + +function buildTemplateContent(template, body, filesByField, existingContent) { + const content = {}; + template.regions.forEach((region) => { + if (region.region_type === 'image') { + const uploaded = filesByField[`region_image_${region.id}`]; + const existing = body[`existing_region_image_${region.id}`]; + content[region.region_key] = { + type: 'image', + value: uploaded ? `/uploads/${uploaded.filename}` : String(existing || '').trim() || ((existingContent && existingContent[region.region_key]) ? existingContent[region.region_key].value : '') + }; + } else if (region.region_type === 'webpage') { + const submitted = body[`region_webpage_${region.id}`]; + const current = existingContent && existingContent[region.region_key] ? existingContent[region.region_key].value : ''; + content[region.region_key] = { + type: 'webpage', + value: submitted === undefined ? current : String(submitted || '').trim() + }; + } else if (region.region_type === 'html') { + const submitted = body[`region_html_${region.id}`]; + const current = existingContent && existingContent[region.region_key] ? existingContent[region.region_key].value : ''; + content[region.region_key] = { + type: 'html', + value: submitted === undefined ? current : String(submitted || '') + }; + } else { + const submitted = body[`region_text_${region.id}`]; + const current = existingContent && existingContent[region.region_key] ? existingContent[region.region_key].value : ''; + const style = getTextRegionStyle(body, region, existingContent); + content[region.region_key] = { + type: 'text', + value: submitted === undefined ? current : String(submitted), + font_family: style.font_family, + font_size: style.font_size, + font_color: style.font_color + }; + } + }); + return content; +} + +async function buildSlidePayload(pool, req, existingSlide) { + const title = String(req.body.title || '').trim(); + const templateId = req.body.template_id ? Number(req.body.template_id) : null; + const filesByField = getFilesByField(req.files || []); + const template = templateId ? await fetchTemplateById(pool, templateId) : null; + const existingContent = existingSlide ? parseJsonSafe(existingSlide.content_json) || {} : {}; + + if (!title) { + const error = new Error('Slide title is required.'); + error.statusCode = 400; + throw error; + } + + if (templateId && !template) { + const error = new Error('Template not found.'); + error.statusCode = 400; + throw error; + } + + if (template) { + return { + title, + body: existingSlide ? existingSlide.body : null, + templateId: template.id, + contentJson: JSON.stringify(buildTemplateContent(template, req.body, filesByField, existingContent)), + mediaPath: null, + mediaType: null + }; + } + + return { + title, + body: existingSlide ? existingSlide.body : null, + templateId: null, + contentJson: existingSlide ? existingSlide.content_json : null, + mediaPath: existingSlide ? existingSlide.media_path : null, + mediaType: existingSlide ? existingSlide.media_type : null + }; +} + +module.exports = { + fetchSlideById, + buildSlidePayload +}; diff --git a/src/data/templates.js b/src/data/templates.js new file mode 100644 index 0000000..fce3203 --- /dev/null +++ b/src/data/templates.js @@ -0,0 +1,169 @@ +const { parseJsonSafe, readFormArray } = require('./utils'); + +const ALLOWED_TEMPLATE_REGION_TYPES = ['text', 'image', 'webpage', 'html']; + +function normalizeTemplateRegionType(value) { + const rawType = String(value || 'text').trim(); + return ALLOWED_TEMPLATE_REGION_TYPES.includes(rawType) ? rawType : 'text'; +} + +async function fetchTemplateById(pool, id) { + const [templates] = await pool.query(` + SELECT st.id, st.name, st.canvas_size_id, st.background_image_path, st.created_at, st.modified_at, + cs.name AS canvas_size_name, cs.width AS canvas_size_width, cs.height AS canvas_size_height + FROM slide_templates st + LEFT JOIN canvas_sizes cs ON cs.id = st.canvas_size_id + WHERE st.id = ? + `, [id]); + if (!templates.length) { + return null; + } + const template = templates[0]; + const [regions] = await pool.query('SELECT * FROM slide_template_regions WHERE template_id = ? ORDER BY z_index ASC, id ASC', [id]); + template.regions = regions; + return template; +} + +async function fetchTemplatesData(pool) { + const [templates] = await pool.query(` + SELECT st.id, st.name, st.canvas_size_id, st.background_image_path, st.created_at, st.modified_at, + cs.name AS canvas_size_name, cs.width AS canvas_size_width, cs.height AS canvas_size_height + FROM slide_templates st + LEFT JOIN canvas_sizes cs ON cs.id = st.canvas_size_id + ORDER BY st.id DESC + `); + const [templateRegions] = await pool.query('SELECT * FROM slide_template_regions ORDER BY template_id ASC, z_index ASC, id ASC'); + return { templates, templateRegions }; +} + +function extractTemplateRegions(body) { + const regionsJson = body.regions_json; + if (regionsJson) { + const parsed = parseJsonSafe(regionsJson); + if (Array.isArray(parsed)) { + return parsed.map((region) => ({ + region_key: String(region.region_name || region.region_key || region.label || '').trim(), + region_type: normalizeTemplateRegionType(region.region_type), + label: String(region.region_name || region.label || region.region_key || '').trim(), + font_family: ['text', 'html'].includes(normalizeTemplateRegionType(region.region_type)) ? String(region.font_family || '').trim() || null : null, + x: Number(region.x || 0), + y: Number(region.y || 0), + width: Number(region.width || 100), + height: Number(region.height || 100), + z_index: Number(region.z_index || 0) + })).filter((region) => region.region_key && region.label); + } + } + + const keys = readFormArray(body, 'region_key[]'); + const names = readFormArray(body, 'region_name[]'); + const labels = readFormArray(body, 'region_label[]'); + const types = readFormArray(body, 'region_type[]'); + const xs = readFormArray(body, 'region_x[]'); + const ys = readFormArray(body, 'region_y[]'); + const widths = readFormArray(body, 'region_width[]'); + const heights = readFormArray(body, 'region_height[]'); + const zs = readFormArray(body, 'region_z[]'); + const fonts = readFormArray(body, 'font_family[]'); + const regions = []; + + for (let i = 0; i < keys.length; i += 1) { + const name = String(names[i] || keys[i] || labels[i] || '').trim(); + const rawType = String(types[i] || 'text').trim(); + const regionType = normalizeTemplateRegionType(rawType); + if (!name) { + continue; + } + regions.push({ + region_key: name, + region_type: regionType, + label: name, + font_family: ['text', 'html'].includes(regionType) ? String(fonts[i] || 'Arial').trim() || 'Arial' : null, + x: Number(xs[i] || 0), + y: Number(ys[i] || 0), + width: Number(widths[i] || 100), + height: Number(heights[i] || 100), + z_index: Number(zs[i] || 0) + }); + } + + return regions; +} + +function getFilesByField(files) { + const map = {}; + (files || []).forEach((file) => { + map[file.fieldname] = file; + }); + return map; +} + +async function buildTemplatePayload(pool, req, existingTemplate) { + const name = String(req.body.name || '').trim(); + const canvasSizeId = req.body.canvas_size_id ? Number(req.body.canvas_size_id) : null; + let canvasWidth = Math.max(1, Number((existingTemplate && existingTemplate.canvas_size_width) || 1920)); + let canvasHeight = Math.max(1, Number((existingTemplate && existingTemplate.canvas_size_height) || 1080)); + let regions = extractTemplateRegions(req.body); + const filesByField = getFilesByField(req.files || []); + const backgroundImage = filesByField.background_image; + const backgroundImagePath = backgroundImage + ? `/uploads/${backgroundImage.filename}` + : String(req.body.existing_background_image_path || (existingTemplate && existingTemplate.background_image_path) || '').trim() || null; + + if (!name) { + const error = new Error('Template name is required.'); + error.statusCode = 400; + throw error; + } + + let resolvedCanvasSizeId = canvasSizeId; + if (resolvedCanvasSizeId) { + const [canvasSizes] = await pool.query('SELECT * FROM canvas_sizes WHERE id = ?', [resolvedCanvasSizeId]); + const canvasSize = canvasSizes[0]; + if (!canvasSize) { + const error = new Error('Canvas size not found.'); + error.statusCode = 400; + throw error; + } + canvasWidth = Number(canvasSize.width); + canvasHeight = Number(canvasSize.height); + } else { + resolvedCanvasSizeId = null; + } + + if (!regions.length) { + if (existingTemplate) { + const error = new Error('At least one region is required.'); + error.statusCode = 400; + throw error; + } + regions = [{ + region_key: 'region_1', + region_type: 'text', + label: 'Region 1', + font_family: 'Arial', + x: 120, + y: 120, + width: Math.max(200, Math.round(canvasWidth * 0.22)), + height: Math.max(120, Math.round(canvasHeight * 0.15)), + z_index: 1 + }]; + } + + return { + name, + canvasSizeId: resolvedCanvasSizeId, + canvasSizeWidth: canvasWidth, + canvasSizeHeight: canvasHeight, + backgroundImagePath, + regions + }; +} + +module.exports = { + fetchTemplateById, + fetchTemplatesData, + extractTemplateRegions, + buildTemplatePayload, + parseJsonSafe +}; diff --git a/src/data/utils.js b/src/data/utils.js new file mode 100644 index 0000000..e66f183 --- /dev/null +++ b/src/data/utils.js @@ -0,0 +1,28 @@ +function parseJsonSafe(value) { + if (!value) { + return null; + } + if (typeof value === 'object') { + return value; + } + try { + return JSON.parse(value); + } catch (_error) { + return null; + } +} + +function readFormArray(body, key) { + if (Array.isArray(body[key])) { + return body[key]; + } + if (body[key] === undefined) { + return []; + } + return [body[key]]; +} + +module.exports = { + parseJsonSafe, + readFormArray +}; diff --git a/src/db.js b/src/db.js new file mode 100644 index 0000000..9b2ea7e --- /dev/null +++ b/src/db.js @@ -0,0 +1,233 @@ +const mysql = require('mysql2/promise'); +const { hashPassword } = require('./auth'); + +function createPool() { + return mysql.createPool({ + host: process.env.DB_HOST || '127.0.0.1', + port: Number(process.env.DB_PORT || 3306), + user: process.env.DB_USER || 'signage_user', + password: process.env.DB_PASSWORD || 'signage_password', + database: process.env.DB_NAME || 'signage', + waitForConnections: true, + connectionLimit: 10, + namedPlaceholders: true + }); +} + +async function ensureSchema(pool) { + await pool.query(` + CREATE TABLE IF NOT EXISTS canvas_sizes ( + id INT AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(255) NOT NULL, + width INT NOT NULL, + height INT NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + modified_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + UNIQUE KEY uq_canvas_sizes_dimensions (width, height) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci + `); + await pool.query('ALTER TABLE canvas_sizes ADD COLUMN IF NOT EXISTS modified_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP AFTER created_at'); + await pool.query('ALTER TABLE canvas_sizes ADD COLUMN IF NOT EXISTS created_by INT NULL AFTER created_at'); + await pool.query('ALTER TABLE canvas_sizes ADD COLUMN IF NOT EXISTS modified_by INT NULL AFTER modified_at'); + + await pool.query(` + CREATE TABLE IF NOT EXISTS playlists ( + id INT AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(255) NOT NULL, + fade_between_slides TINYINT(1) NOT NULL DEFAULT 0, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + modified_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci + `); + await pool.query('ALTER TABLE playlists ADD COLUMN IF NOT EXISTS fade_between_slides TINYINT(1) NOT NULL DEFAULT 0 AFTER name'); + await pool.query('ALTER TABLE playlists ADD COLUMN IF NOT EXISTS modified_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP AFTER created_at'); + await pool.query('ALTER TABLE playlists ADD COLUMN IF NOT EXISTS created_by INT NULL AFTER created_at'); + await pool.query('ALTER TABLE playlists ADD COLUMN IF NOT EXISTS modified_by INT NULL AFTER modified_at'); + + await pool.query(` + CREATE TABLE IF NOT EXISTS slide_templates ( + id INT AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(255) NOT NULL, + canvas_size_id INT NULL, + background_image_path VARCHAR(512) NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + modified_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci + `); + + await pool.query('ALTER TABLE slide_templates ADD COLUMN IF NOT EXISTS canvas_size_id INT NULL AFTER name'); + await pool.query('ALTER TABLE slide_templates ADD COLUMN IF NOT EXISTS background_image_path VARCHAR(512) NULL'); + await pool.query('ALTER TABLE slide_templates ADD COLUMN IF NOT EXISTS modified_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP AFTER created_at'); + await pool.query('ALTER TABLE slide_templates ADD COLUMN IF NOT EXISTS created_by INT NULL AFTER created_at'); + await pool.query('ALTER TABLE slide_templates ADD COLUMN IF NOT EXISTS modified_by INT NULL AFTER modified_at'); + + await pool.query(` + INSERT IGNORE INTO canvas_sizes (name, width, height) VALUES + ('Full HD', 1920, 1080), + ('HD', 1280, 720), + ('4K UHD', 3840, 2160), + ('Portrait Full HD', 1080, 1920), + ('Portrait HD', 720, 1280) + `); + + const [legacyTemplateColumns] = await pool.query(` + SELECT COUNT(*) AS column_count + FROM information_schema.columns + WHERE table_schema = DATABASE() + AND table_name = 'slide_templates' + AND column_name IN ('canvas_width', 'canvas_height') + `); + if (legacyTemplateColumns[0] && Number(legacyTemplateColumns[0].column_count) === 2) { + await pool.query(` + UPDATE slide_templates st + JOIN canvas_sizes cs ON cs.width = st.canvas_width AND cs.height = st.canvas_height + SET st.canvas_size_id = cs.id + WHERE st.canvas_size_id IS NULL + `); + } + + await pool.query(` + CREATE TABLE IF NOT EXISTS slide_template_regions ( + id INT AUTO_INCREMENT PRIMARY KEY, + template_id INT NOT NULL, + region_key VARCHAR(100) NOT NULL, + region_type VARCHAR(20) NOT NULL, + label VARCHAR(255) NOT NULL, + x INT NOT NULL DEFAULT 0, + y INT NOT NULL DEFAULT 0, + width INT NOT NULL DEFAULT 100, + height INT NOT NULL DEFAULT 100, + z_index INT NOT NULL DEFAULT 0, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + modified_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci + `); + + await pool.query('ALTER TABLE slide_template_regions ADD COLUMN IF NOT EXISTS font_family VARCHAR(100) NULL AFTER label'); + await pool.query('ALTER TABLE slide_template_regions ADD COLUMN IF NOT EXISTS modified_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP AFTER created_at'); + await pool.query('ALTER TABLE slide_template_regions ADD COLUMN IF NOT EXISTS created_by INT NULL AFTER created_at'); + await pool.query('ALTER TABLE slide_template_regions ADD COLUMN IF NOT EXISTS modified_by INT NULL AFTER modified_at'); + + await pool.query(` + CREATE TABLE IF NOT EXISTS slides ( + id INT AUTO_INCREMENT PRIMARY KEY, + title VARCHAR(255) NOT NULL, + body TEXT NULL, + template_id INT NULL, + content_json JSON NULL, + media_path VARCHAR(512) NULL, + media_type VARCHAR(100) NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + modified_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci + `); + + await pool.query('ALTER TABLE slides ADD COLUMN IF NOT EXISTS body TEXT NULL AFTER title'); + await pool.query('ALTER TABLE slides ADD COLUMN IF NOT EXISTS template_id INT NULL AFTER body'); + await pool.query('ALTER TABLE slides ADD COLUMN IF NOT EXISTS content_json JSON NULL AFTER template_id'); + await pool.query('ALTER TABLE slides ADD COLUMN IF NOT EXISTS media_path VARCHAR(512) NULL AFTER content_json'); + await pool.query('ALTER TABLE slides ADD COLUMN IF NOT EXISTS media_type VARCHAR(100) NULL AFTER media_path'); + await pool.query('ALTER TABLE slides ADD COLUMN IF NOT EXISTS modified_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP AFTER created_at'); + await pool.query('ALTER TABLE slides ADD COLUMN IF NOT EXISTS created_by INT NULL AFTER created_at'); + await pool.query('ALTER TABLE slides ADD COLUMN IF NOT EXISTS modified_by INT NULL AFTER modified_at'); + + await pool.query(` + CREATE TABLE IF NOT EXISTS playlist_slides ( + id INT AUTO_INCREMENT PRIMARY KEY, + playlist_id INT NOT NULL, + slide_id INT NOT NULL, + position INT NOT NULL DEFAULT 0, + duration_seconds INT NOT NULL DEFAULT 10, + schedule_mode VARCHAR(20) NOT NULL DEFAULT 'always', + schedule_start_datetime DATETIME NULL, + schedule_end_datetime DATETIME NULL, + schedule_start_time TIME NULL, + schedule_end_time TIME NULL, + schedule_days_json JSON NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + modified_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + CONSTRAINT fk_playlist_slides_playlist FOREIGN KEY (playlist_id) REFERENCES playlists(id) ON DELETE CASCADE, + CONSTRAINT fk_playlist_slides_slide FOREIGN KEY (slide_id) REFERENCES slides(id) ON DELETE CASCADE + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci + `); + + await pool.query('ALTER TABLE playlist_slides ADD COLUMN IF NOT EXISTS duration_seconds INT NOT NULL DEFAULT 10 AFTER position'); + await pool.query("ALTER TABLE playlist_slides ADD COLUMN IF NOT EXISTS schedule_mode VARCHAR(20) NOT NULL DEFAULT 'always' AFTER duration_seconds"); + await pool.query('ALTER TABLE playlist_slides ADD COLUMN IF NOT EXISTS schedule_start_datetime DATETIME NULL AFTER schedule_mode'); + await pool.query('ALTER TABLE playlist_slides ADD COLUMN IF NOT EXISTS schedule_end_datetime DATETIME NULL AFTER schedule_start_datetime'); + await pool.query('ALTER TABLE playlist_slides ADD COLUMN IF NOT EXISTS schedule_start_time TIME NULL AFTER schedule_end_datetime'); + await pool.query('ALTER TABLE playlist_slides ADD COLUMN IF NOT EXISTS schedule_end_time TIME NULL AFTER schedule_start_time'); + await pool.query('ALTER TABLE playlist_slides ADD COLUMN IF NOT EXISTS schedule_days_json JSON NULL AFTER schedule_end_time'); + await pool.query('ALTER TABLE playlist_slides ADD COLUMN IF NOT EXISTS modified_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP AFTER created_at'); + await pool.query('ALTER TABLE playlist_slides ADD COLUMN IF NOT EXISTS created_by INT NULL AFTER created_at'); + await pool.query('ALTER TABLE playlist_slides ADD COLUMN IF NOT EXISTS modified_by INT NULL AFTER modified_at'); + + await pool.query(` + CREATE TABLE IF NOT EXISTS screens ( + id INT AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(255) NOT NULL, + slug VARCHAR(255) NOT NULL UNIQUE, + playlist_id INT NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + modified_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + CONSTRAINT fk_screens_playlist FOREIGN KEY (playlist_id) REFERENCES playlists(id) ON DELETE SET NULL + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci + `); + + await pool.query('ALTER TABLE screens ADD COLUMN IF NOT EXISTS playlist_id INT NULL AFTER slug'); + await pool.query('ALTER TABLE screens ADD COLUMN IF NOT EXISTS modified_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP AFTER created_at'); + await pool.query('ALTER TABLE screens ADD COLUMN IF NOT EXISTS created_by INT NULL AFTER created_at'); + await pool.query('ALTER TABLE screens ADD COLUMN IF NOT EXISTS modified_by INT NULL AFTER modified_at'); + + await pool.query(` + CREATE TABLE IF NOT EXISTS users ( + id INT AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(255) NULL, + username VARCHAR(255) NOT NULL UNIQUE, + password_hash CHAR(64) NOT NULL, + password_salt VARCHAR(64) NOT NULL, + password_iterations INT NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + modified_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci + `); + await pool.query('ALTER TABLE users ADD COLUMN IF NOT EXISTS name VARCHAR(255) NULL AFTER id'); + await pool.query('ALTER TABLE users ADD COLUMN IF NOT EXISTS password_hash CHAR(64) NOT NULL AFTER username'); + await pool.query('ALTER TABLE users ADD COLUMN IF NOT EXISTS password_salt VARCHAR(64) NOT NULL AFTER password_hash'); + await pool.query('ALTER TABLE users ADD COLUMN IF NOT EXISTS password_iterations INT NOT NULL AFTER password_salt'); + await pool.query('ALTER TABLE users ADD COLUMN IF NOT EXISTS modified_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP AFTER created_at'); + await pool.query('ALTER TABLE users ADD COLUMN IF NOT EXISTS created_by INT NULL AFTER created_at'); + await pool.query('ALTER TABLE users ADD COLUMN IF NOT EXISTS modified_by INT NULL AFTER modified_at'); + + await pool.query(` + CREATE TABLE IF NOT EXISTS auth_sessions ( + session_hash CHAR(64) PRIMARY KEY, + user_id INT NOT NULL, + expires_at DATETIME NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + last_used_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + CONSTRAINT fk_auth_sessions_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci + `); + await pool.query('ALTER TABLE auth_sessions ADD COLUMN IF NOT EXISTS created_by INT NULL AFTER created_at'); + await pool.query('ALTER TABLE auth_sessions ADD COLUMN IF NOT EXISTS modified_by INT NULL AFTER last_used_at'); + + const [userCountRows] = await pool.query('SELECT COUNT(*) AS user_count FROM users'); + if (!userCountRows.length || Number(userCountRows[0].user_count) === 0) { + const username = String(process.env.DEFAULT_ADMIN_USERNAME || 'admin').trim() || 'admin'; + const name = String(process.env.DEFAULT_ADMIN_NAME || 'Admin').trim() || 'Admin'; + const password = String(process.env.DEFAULT_ADMIN_PASSWORD || 'admin').trim() || 'admin'; + const passwordRecord = hashPassword(password); + await pool.query( + 'INSERT INTO users (name, username, password_hash, password_salt, password_iterations, created_by, modified_by) VALUES (?, ?, ?, ?, ?, ?, ?)', + [name, username, passwordRecord.hash, passwordRecord.salt, passwordRecord.iterations, null, null] + ); + } + + await pool.query('UPDATE users SET name = username WHERE name IS NULL OR name = ""'); +} + +module.exports = { + createPool, + ensureSchema +}; diff --git a/src/player-render.js b/src/player-render.js new file mode 100644 index 0000000..46f93f0 --- /dev/null +++ b/src/player-render.js @@ -0,0 +1,1236 @@ +const path = require('path'); + +function mediaKind(mediaType, mediaPath) { + const ext = path.extname(mediaPath || '').toLowerCase(); + if ((mediaType || '').startsWith('image/') || ['.png', '.jpg', '.jpeg', '.gif', '.webp', '.bmp', '.svg'].includes(ext)) { + return 'image'; + } + if ((mediaType || '').startsWith('video/') || ['.mp4', '.webm', '.ogg'].includes(ext)) { + return 'video'; + } + if ((mediaType || '').includes('pdf') || ext === '.pdf') { + return 'pdf'; + } + return 'file'; +} + +function escapeHtml(value) { + return String(value ?? '') + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); +} + +function sanitizeFontFamily(value) { + return String(value || '').replace(/[^a-zA-Z0-9 ,"-]/g, '').trim(); +} + +function sanitizeFontSize(value) { + return Math.max(8, Number(value || 0) || 24); +} + +function sanitizeTextColor(value, fallback) { + const raw = String(value || '').trim(); + if (/^#[0-9a-fA-F]{6}$/.test(raw) || /^#[0-9a-fA-F]{3}$/.test(raw)) { + return raw; + } + return fallback || '#000000'; +} + +const ALLOWED_RICH_TEXT_TAGS = ['b', 'strong', 'i', 'em', 'u', 'br', 'p', 'div', 'ul', 'ol', 'li']; + +function safeJsonForScript(value) { + return JSON.stringify(value === undefined ? null : value).replace(//gi, ''); + output = output.replace(//gi, ''); + return output.replace(/<[^>]+>/g, (tag) => { + const match = tag.match(/^<\s*(\/?)\s*([a-z0-9]+)(?:\s[^>]*)?>$/i); + if (!match) { + return ''; + } + const closing = Boolean(match[1]); + const name = String(match[2] || '').toLowerCase(); + if (!ALLOWED_RICH_TEXT_TAGS.includes(name)) { + return ''; + } + if (name === 'br') { + return '
'; + } + return closing ? `` : `<${name}>`; + }); +} + +function renderEditorJsBlock(block) { + if (!block || !block.type || !block.data) { + return ''; + } + + if (block.type === 'header') { + const level = Math.max(1, Math.min(6, Number(block.data.level || 2))); + return '' + sanitizeRichText(block.data.text || '') + ''; + } + + if (block.type === 'list') { + const tag = block.data.style === 'ordered' ? 'ol' : 'ul'; + const items = Array.isArray(block.data.items) ? block.data.items : []; + return '<' + tag + ' style="list-style-type:' + (tag === 'ol' ? 'decimal' : 'disc') + ';padding-left:1.4em;">' + items.map((item) => renderEditorJsListItem(item, tag)).join('') + ''; + } + + if (block.type === 'delimiter') { + return '
'; + } + + if (block.type === 'code') { + return '
' + escapeHtml(block.data.code || '') + '
'; + } + + if (block.type === 'table') { + return renderEditorJsTable(block.data); + } + + if (block.type === 'paragraph') { + return '

' + sanitizeRichText(block.data.text || '') + '

'; + } + + return ''; +} + +function renderEditorJsListItem(item, tag) { + if (item && typeof item === 'object') { + const content = item.content !== undefined ? item.content : (item.text !== undefined ? item.text : item.value !== undefined ? item.value : ''); + const children = Array.isArray(item.items) ? item.items : Array.isArray(item.subItems) ? item.subItems : Array.isArray(item.children) ? item.children : []; + const nested = children.length ? '<' + tag + ' style="list-style-type:' + (tag === 'ol' ? 'decimal' : 'disc') + ';padding-left:1.4em;">' + children.map((child) => renderEditorJsListItem(child, tag)).join('') + '' : ''; + return '
  • ' + sanitizeRichText(content || '') + nested + '
  • '; + } + return '
  • ' + sanitizeRichText(item || '') + '
  • '; +} + +function renderEditorJsTable(data) { + const rows = Array.isArray(data.content) ? data.content : Array.isArray(data.rows) ? data.rows : []; + if (!rows.length) { + return ''; + } + const hasHeadings = Boolean(data.withHeadings); + const tableRows = rows.map(function (row, rowIndex) { + const cells = Array.isArray(row) ? row : []; + const cellTag = hasHeadings && rowIndex === 0 ? 'th' : 'td'; + const cellAttrs = cellTag === 'th' ? ' scope="col"' : ''; + return '' + cells.map(function (cell) { + return '<' + cellTag + cellAttrs + '>' + sanitizeRichText(cell || '') + ''; + }).join('') + ''; + }).join(''); + return '' + tableRows + '
    '; +} + +function renderEditorJsContent(value) { + if (value && typeof value === 'object') { + if (Array.isArray(value.blocks)) { + return value.blocks.map(renderEditorJsBlock).join(''); + } + if (value.value !== undefined) { + return renderEditorJsContent(value.value); + } + } + const raw = String(value || ''); + try { + const parsed = JSON.parse(raw); + if (parsed && Array.isArray(parsed.blocks)) { + return parsed.blocks.map(renderEditorJsBlock).join(''); + } + } catch (_error) { + // fall through to legacy HTML rendering + } + return sanitizeRichText(raw); +} + +function renderHtmlRegionContent(value) { + const html = String(value || '').trim(); + if (!html) { + return '
    HTML
    '; + } + return ''; +} + +function fitCanvasSize(canvasWidth, canvasHeight, maxWidth, maxHeight) { + const width = Math.max(1, Number(canvasWidth || 0) || 1920); + const height = Math.max(1, Number(canvasHeight || 0) || 1080); + const viewportWidth = Math.max(1, Number(maxWidth || 0) || width); + const viewportHeight = Math.max(1, Number(maxHeight || 0) || height); + const scale = Math.min(viewportWidth / width, viewportHeight / height); + return { + width: Math.round(width * scale), + height: Math.round(height * scale) + }; +} + +function renderPlayerPage(slug, initialData) { + return ` + + + + + Screen ${escapeHtml(slug)} + + + +
    Loading screen...
    + + + `; +} + +module.exports = { + mediaKind, + renderPlayerPage +}; diff --git a/src/player.js b/src/player.js new file mode 100644 index 0000000..b804e1d --- /dev/null +++ b/src/player.js @@ -0,0 +1,487 @@ +const express = require('express'); +const fs = require('fs'); +const http = require('http'); +const crypto = require('crypto'); +const path = require('path'); +const { WebSocketServer, WebSocket } = require('ws'); +const common = require('./common'); + +async function buildScreenPlaylist(pool, slug) { + const [screenRows] = await pool.query('SELECT * FROM screens WHERE slug = ?', [slug]); + if (!screenRows.length) { + return { screen: null, playlist: null, slides: [] }; + } + + const screen = screenRows[0]; + if (!screen.playlist_id) { + return { screen, playlist: null, slides: [] }; + } + + const [playlistRows] = await pool.query('SELECT * FROM playlists WHERE id = ?', [screen.playlist_id]); + const playlist = playlistRows[0] || null; + const [slideRows] = await pool.query(` + SELECT sl.id, sl.title, sl.body, sl.template_id, sl.content_json, sl.media_path, sl.media_type, sl.created_at, sl.modified_at, + ps.position, ps.duration_seconds AS duration_seconds, ps.schedule_mode, ps.schedule_start_datetime, ps.schedule_end_datetime, ps.schedule_start_time, ps.schedule_end_time, ps.schedule_days_json, + st.name AS template_name, cs.name AS canvas_size_name, cs.width AS canvas_size_width, cs.height AS canvas_size_height, cs.width AS canvas_width, cs.height AS canvas_height + FROM playlist_slides ps + JOIN slides sl ON sl.id = ps.slide_id + LEFT JOIN slide_templates st ON st.id = sl.template_id + LEFT JOIN canvas_sizes cs ON cs.id = st.canvas_size_id + WHERE ps.playlist_id = ? + ORDER BY ps.position ASC, ps.id ASC + `, [screen.playlist_id]); + + const templateIds = slideRows + .filter(function (slide) { return slide.template_id; }) + .map(function (slide) { return slide.template_id; }); + const templatesById = {}; + if (templateIds.length) { + const [templateRows] = await pool.query(` + SELECT st.id, st.name, st.canvas_size_id, st.background_image_path, st.created_at, st.modified_at, + cs.name AS canvas_size_name, cs.width AS canvas_size_width, cs.height AS canvas_size_height, cs.width AS canvas_width, cs.height AS canvas_height + FROM slide_templates st + LEFT JOIN canvas_sizes cs ON cs.id = st.canvas_size_id + WHERE st.id IN (?) + `, [templateIds]); + const [regionRows] = await pool.query('SELECT * FROM slide_template_regions WHERE template_id IN (?) ORDER BY template_id ASC, z_index ASC, id ASC', [templateIds]); + templateRows.forEach(function (template) { + template.regions = regionRows.filter(function (region) { return region.template_id === template.id; }); + templatesById[template.id] = template; + }); + } + + const slides = slideRows.map(function (slide) { + return { + id: slide.id, + title: slide.title, + body: slide.body, + duration_seconds: slide.duration_seconds, + schedule_mode: slide.schedule_mode, + schedule_start_datetime: slide.schedule_start_datetime, + schedule_end_datetime: slide.schedule_end_datetime, + schedule_start_time: slide.schedule_start_time, + schedule_end_time: slide.schedule_end_time, + schedule_days_json: slide.schedule_days_json, + media_url: slide.media_path, + media_type: slide.media_type, + kind: common.mediaKind(slide.media_type, slide.media_path), + template_id: slide.template_id, + template: slide.template_id ? templatesById[slide.template_id] || null : null, + content: common.parseJsonSafe(slide.content_json) || {} + }; + }); + + return { screen, playlist, slides }; +} + +async function start() { + const app = express(); + const pool = common.createPool(); + const PORT = Number(process.env.PLAYER_PORT || 3001); + const ASSET_DIR = path.join(__dirname, 'player', 'public'); + const UPLOAD_DIR = process.env.UPLOAD_DIR || path.join(__dirname, '..', 'uploads'); + const connectionsBySlug = new Map(); + const dashboardListenersBySlug = new Map(); + const server = http.createServer(app); + const wss = new WebSocketServer({ noServer: true }); + + app.use(express.json()); + + app.use('/assets', express.static(ASSET_DIR)); + app.use('/uploads', express.static(UPLOAD_DIR)); + + function getConnectionBucket(slug) { + const key = String(slug || '').trim(); + if (!key) { + return null; + } + if (!connectionsBySlug.has(key)) { + connectionsBySlug.set(key, new Map()); + } + return connectionsBySlug.get(key); + } + + function removeConnection(slug, connectionId) { + const bucket = connectionsBySlug.get(slug); + if (!bucket) { + return; + } + bucket.delete(connectionId); + if (!bucket.size) { + connectionsBySlug.delete(slug); + } + } + + function getDashboardListenerBucket(slug) { + const key = String(slug || '').trim(); + if (!key) { + return null; + } + if (!dashboardListenersBySlug.has(key)) { + dashboardListenersBySlug.set(key, new Set()); + } + return dashboardListenersBySlug.get(key); + } + + function removeDashboardListener(slug, socket) { + const key = String(slug || '').trim(); + const bucket = dashboardListenersBySlug.get(key); + if (!bucket) { + return; + } + bucket.delete(socket); + if (!bucket.size) { + dashboardListenersBySlug.delete(key); + } + } + + function buildClientLabel(connection) { + const clientId = String(connection.clientId || '').trim(); + const userAgent = String(connection.userAgent || '').trim(); + const clientIp = String(connection.clientIp || '').trim(); + const viewport = connection.viewport && typeof connection.viewport === 'object' + ? connection.viewport + : null; + const labelParts = []; + + if (userAgent) { + labelParts.push(userAgent.length > 72 ? `${userAgent.slice(0, 72)}...` : userAgent); + } + + if (clientId) { + labelParts.push(`id ${clientId.slice(-6)}`); + } + + if (clientIp) { + labelParts.push(clientIp); + } + + if (viewport && Number.isFinite(Number(viewport.width)) && Number.isFinite(Number(viewport.height))) { + labelParts.push(`${Number(viewport.width)}x${Number(viewport.height)}`); + } + + if (!labelParts.length) { + return connection.remoteAddress || 'connected client'; + } + + return labelParts.join(' • '); + } + + function snapshotConnections(slug) { + const bucket = connectionsBySlug.get(String(slug || '').trim()); + if (!bucket) { + return []; + } + + return Array.from(bucket.values()).map(function (connection) { + return { + id: connection.id, + clientId: connection.clientId || null, + label: connection.label, + userAgent: connection.userAgent || null, + viewport: connection.viewport || null, + page: connection.page || null, + currentSlide: connection.currentSlide || null, + paused: Boolean(connection.paused), + blackout: Boolean(connection.blackout), + currentSlideId: connection.currentSlide && connection.currentSlide.id ? connection.currentSlide.id : null, + currentSlideTitle: connection.currentSlide && connection.currentSlide.title ? connection.currentSlide.title : null, + clientIp: connection.clientIp || null, + remoteAddress: connection.remoteAddress || null, + connectedAt: connection.connectedAt ? connection.connectedAt.toISOString() : null, + lastSeenAt: connection.lastSeenAt ? connection.lastSeenAt.toISOString() : null + }; + }); + } + + function broadcastConnectionSnapshot(slug) { + const key = String(slug || '').trim(); + const bucket = dashboardListenersBySlug.get(key); + if (!bucket || !bucket.size) { + return; + } + + const payload = JSON.stringify({ + type: 'snapshot', + slug: key, + connections: snapshotConnections(slug), + sentAt: new Date().toISOString() + }); + + bucket.forEach(function (socket) { + if (socket.readyState === WebSocket.OPEN) { + socket.send(payload); + } + }); + } + + function sendCommandToConnection(slug, connectionId, commandOrPayload) { + const bucket = connectionsBySlug.get(String(slug || '').trim()); + if (!bucket || !bucket.size) { + return 0; + } + + const target = bucket.get(String(connectionId || '').trim()); + if (!target || target.socket.readyState !== WebSocket.OPEN) { + return 0; + } + + const payload = typeof commandOrPayload === 'object' && commandOrPayload !== null + ? Object.assign({}, commandOrPayload) + : { command: commandOrPayload }; + payload.type = 'command'; + payload.targetConnectionId = target.id; + payload.sentAt = new Date().toISOString(); + + target.socket.send(JSON.stringify(payload)); + return 1; + } + + function broadcastCommand(slug, commandOrPayload) { + const bucket = connectionsBySlug.get(String(slug || '').trim()); + if (!bucket || !bucket.size) { + return 0; + } + + let sent = 0; + bucket.forEach(function (connection) { + if (connection.socket.readyState !== WebSocket.OPEN) { + return; + } + const payload = typeof commandOrPayload === 'object' && commandOrPayload !== null + ? Object.assign({}, commandOrPayload) + : { command: commandOrPayload }; + payload.type = 'command'; + payload.sentAt = new Date().toISOString(); + + connection.socket.send(JSON.stringify(payload)); + sent += 1; + }); + return sent; + } + + app.get('/', function (_req, res) { + res.send('Pulse Signage player service'); + }); + + app.get('/screen/:slug', function (req, res) { + res.set('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate'); + res.set('Pragma', 'no-cache'); + buildScreenPlaylist(pool, req.params.slug).then(function (data) { + res.send(common.renderPlayerPage(req.params.slug, data)); + }).catch(function (error) { + console.error(error); + res.status(500).send('Internal server error'); + }); + }); + + app.get('/api/screens/:slug/playlist', async function (req, res, next) { + try { + res.set('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate'); + const data = await buildScreenPlaylist(pool, req.params.slug); + if (!data.screen) { + return res.status(404).json({ error: 'Screen not found' }); + } + res.json(data); + } catch (error) { + next(error); + } + }); + + app.get(['/api/screens/:slug/connections', '/api/screens/:slug/clients'], async function (req, res, next) { + try { + const [screenRows] = await pool.query('SELECT id, name, slug FROM screens WHERE slug = ?', [req.params.slug]); + if (!screenRows.length) { + return res.status(404).json({ error: 'Screen not found' }); + } + const connections = snapshotConnections(req.params.slug); + res.json({ + screen: screenRows[0], + screenSlug: req.params.slug, + count: connections.length, + connections: connections + }); + } catch (error) { + next(error); + } + }); + + app.post('/api/screens/:slug/commands', async function (req, res, next) { + try { + const command = String((req.body && req.body.command) || req.query.command || '').trim().toLowerCase(); + const connectionId = String((req.body && (req.body.connectionId || req.body.clientId)) || req.query.connectionId || req.query.clientId || '').trim(); + const blackoutValue = req.body && Object.prototype.hasOwnProperty.call(req.body, 'blackout') + ? req.body.blackout + : req.query.blackout; + if (!command) { + return res.status(400).json({ error: 'Command is required' }); + } + if (['refresh', 'reload', 'pause', 'blackout', 'previous', 'next', 'left', 'right'].indexOf(command) === -1) { + return res.status(400).json({ error: 'Unsupported command' }); + } + + const [screenRows] = await pool.query('SELECT id, name, slug FROM screens WHERE slug = ?', [req.params.slug]); + if (!screenRows.length) { + return res.status(404).json({ error: 'Screen not found' }); + } + + const commandPayload = command === 'blackout' && blackoutValue !== undefined + ? { + command: command, + blackout: blackoutValue + } + : command; + + const sent = connectionId + ? sendCommandToConnection(req.params.slug, connectionId, commandPayload) + : broadcastCommand(req.params.slug, commandPayload); + res.json({ + screen: screenRows[0], + screenSlug: req.params.slug, + command: command, + connectionId: connectionId || null, + sent: sent + }); + } catch (error) { + next(error); + } + }); + + app.use(function (error, _req, res, _next) { + console.error(error); + res.status(error.statusCode || 500).send(error.statusCode ? error.message : 'Internal server error'); + }); + + await common.ensureSchema(pool); + fs.mkdirSync(UPLOAD_DIR, { recursive: true }); + server.on('upgrade', function (request, socket, head) { + let pathname = ''; + try { + pathname = new URL(request.url, 'http://localhost').pathname; + } catch (_error) { + socket.destroy(); + return; + } + + const dashboardMatch = pathname.match(/^\/ws\/screens\/([^/]+)\/events$/); + const playerMatch = pathname.match(/^\/ws\/screens\/([^/]+)$/); + if (!dashboardMatch && !playerMatch) { + socket.destroy(); + return; + } + + const slug = decodeURIComponent((dashboardMatch || playerMatch)[1]); + wss.handleUpgrade(request, socket, head, function (ws) { + wss.emit('connection', ws, request, slug, dashboardMatch ? 'dashboard' : 'player'); + }); + }); + + wss.on('connection', function (socket, request, slug, role) { + if (role === 'dashboard') { + const listenerBucket = getDashboardListenerBucket(slug); + if (!listenerBucket) { + socket.close(); + return; + } + + listenerBucket.add(socket); + socket.send(JSON.stringify({ + type: 'snapshot', + slug: String(slug || '').trim(), + connections: snapshotConnections(slug), + sentAt: new Date().toISOString() + })); + + socket.on('close', function () { + removeDashboardListener(slug, socket); + }); + + socket.on('error', function () { + removeDashboardListener(slug, socket); + }); + return; + } + + const remoteAddress = request.socket && request.socket.remoteAddress ? request.socket.remoteAddress : null; + const forwardedFor = String(request.headers['x-forwarded-for'] || '').split(',')[0].trim(); + const connectionId = crypto.randomUUID(); + const connection = { + id: connectionId, + slug: slug, + socket: socket, + clientId: null, + userAgent: null, + viewport: null, + page: null, + paused: false, + blackout: false, + clientIp: forwardedFor || remoteAddress, + remoteAddress: remoteAddress, + label: forwardedFor || remoteAddress || 'connected client', + connectedAt: new Date(), + lastSeenAt: new Date() + }; + const bucket = getConnectionBucket(slug); + + if (!bucket) { + socket.close(); + return; + } + + bucket.set(connectionId, connection); + + socket.on('message', function (rawMessage) { + connection.lastSeenAt = new Date(); + let payload = null; + try { + payload = JSON.parse(String(rawMessage || '')); + } catch (_error) { + return; + } + + if (!payload || (payload.type !== 'hello' && payload.type !== 'state')) { + return; + } + + connection.clientId = payload.clientId ? String(payload.clientId).trim() : connection.clientId; + connection.userAgent = payload.userAgent ? String(payload.userAgent).trim() : connection.userAgent; + connection.viewport = payload.viewport && typeof payload.viewport === 'object' ? payload.viewport : connection.viewport; + connection.page = payload.page ? String(payload.page).trim() : connection.page; + connection.paused = Boolean(payload.paused); + connection.blackout = Boolean(payload.blackout); + connection.clientIp = payload.clientIp ? String(payload.clientIp).trim() : connection.clientIp; + connection.currentSlide = payload.currentSlide && typeof payload.currentSlide === 'object' ? { + id: payload.currentSlide.id || null, + title: payload.currentSlide.title || '', + kind: payload.currentSlide.kind || '', + playlistSignature: payload.currentSlide.playlistSignature || '' + } : connection.currentSlide; + connection.label = buildClientLabel(connection); + connection.lastSeenAt = new Date(); + broadcastConnectionSnapshot(slug); + }); + + socket.on('close', function () { + removeConnection(slug, connectionId); + broadcastConnectionSnapshot(slug); + }); + + socket.on('error', function () { + removeConnection(slug, connectionId); + broadcastConnectionSnapshot(slug); + }); + }); + + server.listen(PORT, function () { + console.log(`Pulse Signage app listening on port ${PORT}`); + }); +} + +module.exports = { start }; + +if (require.main === module) { + start().catch(function (error) { + console.error(error); + process.exit(1); + }); +} diff --git a/src/player/public/css/player.css b/src/player/public/css/player.css new file mode 100644 index 0000000..808c805 --- /dev/null +++ b/src/player/public/css/player.css @@ -0,0 +1,253 @@ +html, +body { + margin: 0; + width: 100%; + height: 100%; + overflow: hidden; + background: #000; + color: #fff; + font-family: Arial, sans-serif; +} + +#app { + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + background: #000; + position: relative; +} + +.slide-shell { + position: absolute; + inset: 0; + width: 100%; + height: 100%; + opacity: 0; + transition: opacity 560ms ease; +} + +.slide-shell.is-visible { + opacity: 1; + z-index: 1; +} + +.slide-shell.is-exiting { + opacity: 0; + z-index: 0; +} + +.slide-shell > .slide { + width: 100%; + height: 100%; +} + +body.screen-blackout #app { + opacity: 0; +} + +.slide { + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + position: relative; +} + +.slide-canvas { + position: relative; + overflow: hidden; + background: #000; +} + +.slide-media { + width: 100%; + height: 100%; +} + +.slide-media img, +.slide-media video, +.slide-media iframe { + width: 100%; + height: 100%; + object-fit: contain; + border: 0; + display: block; +} + +.slide img, +.slide video, +.slide iframe { + width: 100%; + height: 100%; + object-fit: contain; + border: 0; +} + +.overlay { + position: absolute; + left: 0; + right: 0; + top: 0; + padding: 14px 18px; + background: rgba(0, 0, 0, 0.35); + font-size: 14px; + display: flex; + justify-content: space-between; +} + +.body { + position: absolute; + left: 5%; + right: 5%; + bottom: 8%; + padding: 16px; + background: rgba(0, 0, 0, 0.4); + border-radius: 16px; + font-size: 2.5vw; + line-height: 1.35; +} + +.empty { + font-size: 2rem; + color: #9ca3af; +} + +.debug-info { + margin: 16px auto 0; + padding: 12px; + max-width: 90%; + white-space: pre-wrap; + text-align: left; + font-size: 12px; + line-height: 1.4; + background: rgba(255, 255, 255, 0.08); + color: #e5e7eb; + border: 1px solid rgba(255, 255, 255, 0.15); + border-radius: 8px; +} + +.template-stage { + position: relative; + width: 100%; + height: 100%; + background: #111; +} + +.template-stage .template-background { + position: absolute; + inset: 0; + width: 100%; + height: 100%; + object-fit: fill; + display: block; + z-index: 0; +} + +.template-region { + position: absolute; + overflow: hidden; + box-sizing: border-box; +} + +.template-region.text { + color: #fff; + display: block; + padding: 0; + text-align: left; + white-space: normal; + word-break: break-word; + line-height: 1.35; +} + +.template-region.text > * { + margin: 0; +} + +.template-region.text > * + * { + margin-top: 0.5em; +} + +.template-region.text ul, +.template-region.text ol { + padding-left: 1.2em; +} + +.template-region.image img { + width: 100%; + height: 100%; + object-fit: contain; + display: block; +} + +.template-region.webpage iframe { + width: 100%; + height: 100%; + border: 0; + display: block; + background: #fff; + overflow: hidden; +} + +.template-region.html iframe { + width: 100%; + height: 100%; + border: 0; + display: block; + background: transparent; + overflow: hidden; +} + +.template-region-placeholder { + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + color: rgba(255, 255, 255, 0.65); + background: rgba(255, 255, 255, 0.06); + font-size: 0.9rem; +} + +.template-region.text table, +.template-region.text th, +.template-region.text td { + border: 1px solid rgba(255, 255, 255, 0.35); + border-collapse: collapse; +} + +.template-region.text table { + width: 100%; + border-spacing: 0; +} + +.template-region.text th, +.template-region.text td { + padding: 0.35em 0.5em; + text-align: left; + vertical-align: top; +} + +.template-region.text th { + font-weight: 700; +} + +.webpage-preloads { + position: fixed; + width: 1px; + height: 1px; + left: -9999px; + top: -9999px; + overflow: hidden; + pointer-events: none; + opacity: 0; +} + +.webpage-preload-frame { + width: 1px; + height: 1px; + border: 0; + display: block; +} diff --git a/src/server.js b/src/server.js new file mode 100644 index 0000000..957ade6 --- /dev/null +++ b/src/server.js @@ -0,0 +1 @@ +module.exports = require('./common'); diff --git a/src/webui.js b/src/webui.js new file mode 100644 index 0000000..a17dad1 --- /dev/null +++ b/src/webui.js @@ -0,0 +1,1704 @@ +const express = require('express'); +const http = require('http'); +const multer = require('multer'); +const fs = require('fs'); +const path = require('path'); +const crypto = require('crypto'); +const { WebSocketServer, WebSocket } = require('ws'); +const common = require('./common'); +const { verifyPassword, createSessionToken, hashSessionToken, hashPassword } = require('./auth'); +const pages = require('./webui/routes'); +const PLAYER_INTERNAL_BASE_URL = (process.env.PLAYER_INTERNAL_BASE_URL || process.env.PLAYER_BASE_URL || 'http://player:3001').replace(/\/$/, ''); +const PLAYER_PUBLIC_BASE_URL = (process.env.PLAYER_PUBLIC_BASE_URL || process.env.PLAYER_BASE_URL || 'http://localhost:3001').replace(/\/$/, ''); +const PLAYER_WS_BASE_URL = PLAYER_INTERNAL_BASE_URL.replace(/^http/, 'ws'); +const SESSION_COOKIE_NAME = 'digital_signage_session'; +const SESSION_MAX_AGE_DAYS = Number(process.env.SESSION_MAX_AGE_DAYS || 14); +const SESSION_MAX_AGE_MS = (Number.isFinite(SESSION_MAX_AGE_DAYS) && SESSION_MAX_AGE_DAYS > 0 ? SESSION_MAX_AGE_DAYS : 14) * 24 * 60 * 60 * 1000; + +const playerSnapshotCache = new Map(); +const playerSnapshotSockets = new Map(); + +function readArrayField(body, keys) { + const searchKeys = Array.isArray(keys) ? keys : [keys]; + for (let i = 0; i < searchKeys.length; i += 1) { + const value = body[searchKeys[i]]; + if (Array.isArray(value)) { + return value; + } + if (value !== undefined && value !== null && value !== '') { + return [value]; + } + } + return []; +} + +function parseDateTimeLocal(value) { + if (!value) { + return null; + } + const date = new Date(String(value)); + return Number.isNaN(date.getTime()) ? null : date; +} + +function parseTimeLocal(value) { + const raw = String(value || '').trim(); + if (!raw) { + return null; + } + if (!/^\d{2}:\d{2}(:\d{2})?$/.test(raw)) { + return null; + } + return raw.length === 5 ? raw + ':00' : raw; +} + +function normalizeScheduleMode(value) { + const mode = String(value || 'always'); + if (mode === 'dates' || mode === 'times') { + return mode; + } + return 'always'; +} + +function getAuditUserId(req) { + return req && req.currentUser ? Number(req.currentUser.id) : null; +} + +function getCanvasSignature(width, height) { + const normalizedWidth = Number(width); + const normalizedHeight = Number(height); + if (!Number.isFinite(normalizedWidth) || !Number.isFinite(normalizedHeight)) { + return null; + } + return normalizedWidth + 'x' + normalizedHeight; +} + +async function fetchPlaylistCanvasSignature(pool, playlistId) { + const [rows] = await pool.query(` + SELECT DISTINCT cs.width AS canvas_width, cs.height AS canvas_height + FROM playlist_slides ps + JOIN slides sl ON sl.id = ps.slide_id + LEFT JOIN slide_templates st ON st.id = sl.template_id + LEFT JOIN canvas_sizes cs ON cs.id = st.canvas_size_id + WHERE ps.playlist_id = ? + AND cs.width IS NOT NULL + AND cs.height IS NOT NULL + `, [playlistId]); + const signatures = Array.from(new Set(rows.map((row) => getCanvasSignature(row.canvas_width, row.canvas_height)).filter(Boolean))); + if (!signatures.length) { + return null; + } + return signatures.length === 1 ? signatures[0] : 'mismatch'; +} + +function getPlayerSnapshotSocketUrl(slug) { + const url = new URL(PLAYER_WS_BASE_URL); + url.pathname = `/ws/screens/${encodeURIComponent(slug)}/events`; + url.search = ''; + return url.toString(); +} + +function storePlayerSnapshot(slug, connections) { + const normalizedConnections = Array.isArray(connections) ? connections : []; + playerSnapshotCache.set(String(slug || '').trim(), { + slug: String(slug || '').trim(), + count: normalizedConnections.length, + connections: normalizedConnections + }); +} + +function clearPlayerSnapshotSocket(slug) { + const key = String(slug || '').trim(); + playerSnapshotSockets.delete(key); +} + +function ensurePlayerSnapshotSubscription(slug) { + const key = String(slug || '').trim(); + if (!key || playerSnapshotSockets.has(key)) { + return; + } + + const socket = new WebSocket(getPlayerSnapshotSocketUrl(key)); + playerSnapshotSockets.set(key, socket); + + socket.onmessage = function (event) { + try { + const payload = JSON.parse(String(event.data || '{}')); + if (!payload || payload.type !== 'snapshot' || payload.slug !== key) { + return; + } + storePlayerSnapshot(key, payload.connections || []); + broadcastDashboardState().catch(function (error) { + console.error(error); + }); + } catch (_error) { + // Ignore malformed player snapshot payloads. + } + }; + + socket.onclose = function () { + clearPlayerSnapshotSocket(key); + setTimeout(function () { + ensurePlayerSnapshotSubscription(key); + }, 2000); + }; + + socket.onerror = function () { + try { + socket.close(); + } catch (_error) { + // ignore close errors + } + }; +} + +function enrichScreensWithConnections(screens, connectionsBySlug) { + return (screens || []).map(function (screen) { + const connectionState = connectionsBySlug[screen.slug] || { count: 0, connections: [] }; + return Object.assign({}, screen, { + player_connection_count: connectionState.count || 0, + player_connections: Array.isArray(connectionState.connections) ? connectionState.connections : [] + }); + }); +} + +function buildClientRows(screens, connectionsBySlug) { + return (screens || []).flatMap(function (screen) { + const connectionState = connectionsBySlug[screen.slug] || { count: 0, connections: [] }; + return (connectionState.connections || []).map(function (connection) { + return Object.assign({}, connection, { + screen_slug: screen.slug, + screen_name: screen.name, + playlist_name: screen.playlist_name || null, + connectedAtLabel: formatDashboardDate(connection.connectedAt), + lastSeenAtLabel: formatDashboardDate(connection.lastSeenAt), + player_url: `${PLAYER_PUBLIC_BASE_URL}/screen/${encodeURIComponent(screen.slug)}` + }); + }); + }); +} + +async function forwardPlayerCommand(slug, commandOrPayload, connectionId) { + const payload = typeof commandOrPayload === 'object' && commandOrPayload !== null + ? Object.assign({}, commandOrPayload) + : { command: commandOrPayload }; + if (connectionId) { + payload.connectionId = connectionId; + } + const response = await fetch(`${PLAYER_INTERNAL_BASE_URL}/api/screens/${encodeURIComponent(slug)}/commands`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Accept: 'application/json' + }, + body: JSON.stringify(payload) + }); + + if (!response.ok) { + const errorText = await response.text().catch(function () { return ''; }); + const error = new Error(errorText || `Unable to send command to player ${slug}.`); + error.statusCode = response.status; + throw error; + } + + return response.json().catch(function () { + return { ok: true }; + }); +} + +async function notifyPlayerScreens(slugs, commandOrPayload) { + const uniqueSlugs = Array.from(new Set((slugs || []).map(function (slug) { + return String(slug || '').trim(); + }).filter(Boolean))); + + if (!uniqueSlugs.length) { + return 0; + } + + const results = await Promise.allSettled(uniqueSlugs.map(function (slug) { + return forwardPlayerCommand(slug, commandOrPayload || 'refresh'); + })); + + return results.filter(function (result) { + return result.status === 'fulfilled'; + }).length; +} + +async function fetchAllScreenSlugs(connection) { + const [rows] = await connection.query('SELECT slug FROM screens WHERE slug IS NOT NULL'); + return rows.map(function (row) { + return row.slug; + }); +} + +async function fetchScreensByPlaylistId(connection, playlistId) { + const [rows] = await connection.query( + 'SELECT slug FROM screens WHERE playlist_id = ? AND slug IS NOT NULL', + [playlistId] + ); + return rows.map(function (row) { + return row.slug; + }); +} + +async function fetchScreensBySlideId(connection, slideId) { + const [rows] = await connection.query( + `SELECT DISTINCT s.slug + FROM screens s + JOIN playlist_slides ps ON ps.playlist_id = s.playlist_id + WHERE ps.slide_id = ? + AND s.slug IS NOT NULL`, + [slideId] + ); + return rows.map(function (row) { + return row.slug; + }); +} + +function formatDashboardDate(value) { + if (!value) { + return ''; + } + const date = new Date(value); + if (Number.isNaN(date.getTime())) { + return ''; + } + return new Intl.DateTimeFormat('en-US', { + month: 'short', + day: '2-digit', + year: 'numeric', + hour: 'numeric', + minute: '2-digit', + second: '2-digit' + }).format(date); +} + +async function buildDashboardState(pool) { + const data = await common.fetchAdminData(pool); + const screensData = data.screens || []; + screensData.forEach(function (screen) { + ensurePlayerSnapshotSubscription(screen.slug); + }); + + const connectionsBySlug = {}; + screensData.forEach(function (screen) { + const cached = playerSnapshotCache.get(String(screen.slug || '').trim()); + if (cached && Array.isArray(cached.connections)) { + connectionsBySlug[screen.slug] = cached; + } + }); + + const screens = enrichScreensWithConnections(data.screens || [], connectionsBySlug).map(function (screen) { + return Object.assign({}, screen, { + player_url: `${PLAYER_PUBLIC_BASE_URL}/screen/${encodeURIComponent(screen.slug)}` + }); + }); + const clients = buildClientRows(data.screens || [], connectionsBySlug); + const playerServiceConnected = Array.from(playerSnapshotSockets.values()).some(function (socket) { + return socket && socket.readyState === WebSocket.OPEN; + }); + + return { + playlists: data.playlists || [], + screens: screens, + clients: clients, + slides: data.slides || [], + playerServiceConnected: playerServiceConnected, + connectedClientsCount: screens.reduce(function (total, screen) { + return total + Number(screen.player_connection_count || 0); + }, 0) + }; +} + +function buildDashboardPayload(state) { + return JSON.stringify({ + type: 'dashboard-state', + state: state + }); +} + +async function fetchOrderedPlaylistSlides(connection, playlistId) { + const [rows] = await connection.query( + 'SELECT id, position FROM playlist_slides WHERE playlist_id = ? ORDER BY position ASC, id ASC', + [playlistId] + ); + return rows; +} + +function createUploadMiddleware(uploadDir) { + const storage = multer.diskStorage({ + destination: function (_req, _file, cb) { + cb(null, uploadDir); + }, + filename: function (_req, file, cb) { + const safeExt = path.extname(file.originalname || '').toLowerCase(); + const stamp = `${Date.now()}-${crypto.randomUUID()}`; + cb(null, `${stamp}${safeExt}`); + } + }); + return multer({ storage }); +} + +function parseCookies(cookieHeader) { + return String(cookieHeader || '').split(/;\s*/).reduce(function (cookies, pair) { + if (!pair) { + return cookies; + } + const separatorIndex = pair.indexOf('='); + if (separatorIndex === -1) { + return cookies; + } + const name = decodeURIComponent(pair.slice(0, separatorIndex).trim()); + const value = decodeURIComponent(pair.slice(separatorIndex + 1).trim()); + if (name) { + cookies[name] = value; + } + return cookies; + }, {}); +} + +function serializeCookie(name, value, options) { + const parts = [`${encodeURIComponent(name)}=${encodeURIComponent(value)}`]; + if (options && options.maxAge !== undefined) { + parts.push(`Max-Age=${Math.max(0, Math.trunc(Number(options.maxAge) / 1000))}`); + } + parts.push('Path=/'); + parts.push('HttpOnly'); + parts.push('SameSite=Lax'); + return parts.join('; '); +} + +function clearSessionCookie(res) { + res.setHeader('Set-Cookie', serializeCookie(SESSION_COOKIE_NAME, '', { maxAge: 0 })); +} + +function setSessionCookie(res, token) { + res.setHeader('Set-Cookie', serializeCookie(SESSION_COOKIE_NAME, token, { maxAge: SESSION_MAX_AGE_MS })); +} + +async function loadCurrentUser(pool, req) { + const cookies = parseCookies(req.headers.cookie || ''); + const token = cookies[SESSION_COOKIE_NAME]; + if (!token) { + return null; + } + + const tokenHash = hashSessionToken(token); + const [rows] = await pool.query( + `SELECT s.user_id, u.id, u.name, u.username + FROM auth_sessions s + JOIN users u ON u.id = s.user_id + WHERE s.session_hash = ? + AND s.expires_at > NOW() + LIMIT 1`, + [tokenHash] + ); + if (!rows.length) { + return null; + } + + await pool.query('UPDATE auth_sessions SET last_used_at = CURRENT_TIMESTAMP, modified_by = ? WHERE session_hash = ?', [rows[0].user_id, tokenHash]); + return rows[0]; +} + +async function createUserSession(pool, userId) { + const token = createSessionToken(); + const tokenHash = hashSessionToken(token); + const expiresAt = new Date(Date.now() + SESSION_MAX_AGE_MS); + await pool.query( + 'INSERT INTO auth_sessions (session_hash, user_id, expires_at, created_by, modified_by) VALUES (?, ?, ?, ?, ?)', + [tokenHash, userId, expiresAt, userId, userId] + ); + return token; +} + +function requireAuth(req, res, next) { + if (req.currentUser) { + return next(); + } + res.redirect('/login?message=' + encodeURIComponent('Please sign in to continue.')); +} + +async function start() { + const app = express(); + const server = http.createServer(app); + const pool = common.createPool(); + const PORT = Number(process.env.WEB_PORT || 3000); + const UPLOAD_DIR = process.env.UPLOAD_DIR || path.join(__dirname, '..', 'uploads'); + const ASSET_DIR = path.join(__dirname, 'webui', 'public'); + const dashboardWs = new WebSocketServer({ noServer: true }); + const dashboardClients = new Set(); + const dashboardRefreshIntervalMs = Number(process.env.DASHBOARD_REFRESH_INTERVAL_MS || 2000); + let dashboardRefreshInFlight = null; + fs.mkdirSync(UPLOAD_DIR, { recursive: true }); + + const upload = createUploadMiddleware(UPLOAD_DIR); + + app.use(express.urlencoded({ extended: true })); + app.use(express.json()); + app.use('/assets', express.static(ASSET_DIR)); + app.use('/uploads', express.static(UPLOAD_DIR)); + + app.use(async function (req, _res, next) { + try { + req.currentUser = await loadCurrentUser(pool, req); + next(); + } catch (error) { + next(error); + } + }); + + app.get('/', function (_req, res) { + res.redirect(_req.currentUser ? '/admin' : '/login'); + }); + + app.get('/login', function (req, res) { + if (req.currentUser) { + return res.redirect('/admin'); + } + res.send(pages.renderLoginPage(req.query.message ? String(req.query.message) : '')); + }); + + app.post('/login', async function (req, res, next) { + try { + const username = String(req.body.username || '').trim(); + const password = String(req.body.password || ''); + if (!username || !password) { + return res.status(400).send('Username and password are required.'); + } + + const [rows] = await pool.query('SELECT id, name, username, password_hash, password_salt, password_iterations FROM users WHERE username = ? LIMIT 1', [username]); + const user = rows[0] || null; + if (!user || !verifyPassword(password, user)) { + return res.redirect('/login?message=' + encodeURIComponent('Invalid username or password.')); + } + + const token = await createUserSession(pool, user.id); + setSessionCookie(res, token); + res.redirect('/admin'); + } catch (error) { + next(error); + } + }); + + app.get('/logout', async function (req, res, next) { + try { + const cookies = parseCookies(req.headers.cookie || ''); + const token = cookies[SESSION_COOKIE_NAME]; + if (token) { + await pool.query('DELETE FROM auth_sessions WHERE session_hash = ?', [hashSessionToken(token)]); + } + clearSessionCookie(res); + res.redirect('/login?message=' + encodeURIComponent('You have been signed out.')); + } catch (error) { + next(error); + } + }); + + app.use('/admin', requireAuth); + + app.get('/admin', async function (req, res, next) { + try { + const data = await buildDashboardState(pool); + res.send(pages.renderDashboardPage(data, req.query.message ? String(req.query.message) : '', req.currentUser)); + } catch (error) { + next(error); + } + }); + + app.get('/admin/account', function (req, res) { + res.send(pages.renderAccountPage(req.currentUser, req.query.message ? String(req.query.message) : '')); + }); + + app.post('/admin/account/name', async function (req, res, next) { + try { + const name = String(req.body.name || '').trim(); + if (!name) { + return res.status(400).send('Name is required.'); + } + + const actorId = getAuditUserId(req); + const [result] = await pool.query('UPDATE users SET name = ?, modified_by = ? WHERE id = ?', [name, actorId, req.currentUser.id]); + if (!result.affectedRows) { + return res.status(404).send('User not found.'); + } + + res.redirect('/admin/account?message=' + encodeURIComponent('Name updated.')); + } catch (error) { + next(error); + } + }); + + app.post('/admin/account/password', async function (req, res, next) { + try { + const currentPassword = String(req.body.current_password || ''); + const newPassword = String(req.body.new_password || ''); + const confirmPassword = String(req.body.confirm_password || ''); + + const [rows] = await pool.query('SELECT id, name, username, password_hash, password_salt, password_iterations FROM users WHERE id = ? LIMIT 1', [req.currentUser.id]); + const user = rows[0] || null; + if (!user) { + return res.status(404).send('User not found.'); + } + if (!verifyPassword(currentPassword, user)) { + return res.status(400).send('Current password is incorrect.'); + } + if (!newPassword || newPassword.length < 8) { + return res.status(400).send('New password must be at least 8 characters.'); + } + if (newPassword !== confirmPassword) { + return res.status(400).send('New passwords do not match.'); + } + + const passwordRecord = hashPassword(newPassword); + await pool.query( + 'UPDATE users SET password_hash = ?, password_salt = ?, password_iterations = ?, modified_by = ? WHERE id = ?', + [passwordRecord.hash, passwordRecord.salt, passwordRecord.iterations, getAuditUserId(req), user.id] + ); + await pool.query('DELETE FROM auth_sessions WHERE user_id = ?', [user.id]); + + const token = await createUserSession(pool, user.id); + setSessionCookie(res, token); + res.redirect('/admin/account?message=' + encodeURIComponent('Password updated.')); + } catch (error) { + next(error); + } + }); + + app.get('/admin/users', async function (req, res, next) { + try { + const [rows] = await pool.query('SELECT id, name, username, created_at, modified_at FROM users ORDER BY id ASC'); + const users = rows.map(function (user) { + return Object.assign({}, user, { + isCurrentUser: Number(user.id) === Number(req.currentUser.id), + createdAtLabel: formatDashboardDate(user.created_at), + modifiedAtLabel: formatDashboardDate(user.modified_at) + }); + }); + res.send(pages.renderUsersPage({ users: users }, req.query.message ? String(req.query.message) : '', req.currentUser)); + } catch (error) { + next(error); + } + }); + + app.get('/admin/users/new', function (req, res) { + res.send(pages.renderUsersAddPage(req.query.message ? String(req.query.message) : '', req.currentUser)); + }); + + app.get('/admin/users/:id/edit', async function (req, res, next) { + try { + const userId = Number(req.params.id); + if (!Number.isInteger(userId) || userId <= 0) { + return res.status(400).send('Invalid user.'); + } + + const [rows] = await pool.query('SELECT id, name, username, created_at, modified_at FROM users WHERE id = ? LIMIT 1', [userId]); + const user = rows[0] || null; + if (!user) { + return res.status(404).send('User not found.'); + } + if (Number(req.currentUser.id) === userId) { + return res.redirect('/admin/account?message=' + encodeURIComponent('Use My Account to update your own login details.')); + } + + res.send(pages.renderUsersEditPage(Object.assign({}, user, { + isCurrentUser: false, + createdAtLabel: formatDashboardDate(user.created_at), + modifiedAtLabel: formatDashboardDate(user.modified_at) + }), req.query.message ? String(req.query.message) : '', req.currentUser)); + } catch (error) { + next(error); + } + }); + + app.post('/admin/users', async function (req, res, next) { + try { + const name = String(req.body.name || '').trim(); + const username = String(req.body.username || '').trim(); + const password = String(req.body.password || ''); + const confirmPassword = String(req.body.confirm_password || ''); + + if (!name) { + return res.redirect('/admin/users/new?message=' + encodeURIComponent('Name is required.')); + } + if (!username) { + return res.redirect('/admin/users/new?message=' + encodeURIComponent('Username is required.')); + } + if (!password || password.length < 8) { + return res.redirect('/admin/users/new?message=' + encodeURIComponent('Password must be at least 8 characters.')); + } + if (password !== confirmPassword) { + return res.redirect('/admin/users/new?message=' + encodeURIComponent('Passwords do not match.')); + } + + const [existingRows] = await pool.query('SELECT id FROM users WHERE username = ? LIMIT 1', [username]); + if (existingRows.length) { + return res.redirect('/admin/users/new?message=' + encodeURIComponent('That username already exists.')); + } + + const passwordRecord = hashPassword(password); + const actorId = getAuditUserId(req); + await pool.query( + 'INSERT INTO users (name, username, password_hash, password_salt, password_iterations, created_by, modified_by) VALUES (?, ?, ?, ?, ?, ?, ?)', + [name, username, passwordRecord.hash, passwordRecord.salt, passwordRecord.iterations, actorId, actorId] + ); + res.redirect('/admin/users?message=' + encodeURIComponent('User created.')); + } catch (error) { + next(error); + } + }); + + app.post('/admin/users/:id/username', async function (req, res, next) { + try { + const userId = Number(req.params.id); + const name = String(req.body.name || '').trim(); + const username = String(req.body.username || '').trim(); + + if (!Number.isInteger(userId) || userId <= 0) { + return res.status(400).send('Invalid user.'); + } + if (!name) { + return res.redirect('/admin/users/' + userId + '/edit?message=' + encodeURIComponent('Name is required.')); + } + if (Number(req.currentUser.id) === userId) { + return res.redirect('/admin/account?message=' + encodeURIComponent('Use My Account to update your own username or password.')); + } + if (!username) { + return res.redirect('/admin/users/' + userId + '/edit?message=' + encodeURIComponent('Username is required.')); + } + + const [existingRows] = await pool.query('SELECT id FROM users WHERE username = ? AND id <> ? LIMIT 1', [username, userId]); + if (existingRows.length) { + return res.redirect('/admin/users/' + userId + '/edit?message=' + encodeURIComponent('That username already exists.')); + } + + const [result] = await pool.query('UPDATE users SET name = ?, username = ?, modified_by = ? WHERE id = ?', [name, username, getAuditUserId(req), userId]); + if (!result.affectedRows) { + return res.status(404).send('User not found.'); + } + res.redirect('/admin/users?message=' + encodeURIComponent('Username updated.')); + } catch (error) { + next(error); + } + }); + + app.post('/admin/users/:id/password', async function (req, res, next) { + try { + const userId = Number(req.params.id); + const password = String(req.body.password || ''); + const confirmPassword = String(req.body.confirm_password || ''); + + if (!Number.isInteger(userId) || userId <= 0) { + return res.status(400).send('Invalid user.'); + } + if (Number(req.currentUser.id) === userId) { + return res.redirect('/admin/account?message=' + encodeURIComponent('Use My Account to change your own password.')); + } + if (!password || password.length < 8) { + return res.redirect('/admin/users?message=' + encodeURIComponent('Password must be at least 8 characters.')); + } + if (password !== confirmPassword) { + return res.redirect('/admin/users?message=' + encodeURIComponent('Passwords do not match.')); + } + + const [rows] = await pool.query('SELECT id FROM users WHERE id = ? LIMIT 1', [userId]); + if (!rows.length) { + return res.status(404).send('User not found.'); + } + + const passwordRecord = hashPassword(password); + await pool.query( + 'UPDATE users SET password_hash = ?, password_salt = ?, password_iterations = ?, modified_by = ? WHERE id = ?', + [passwordRecord.hash, passwordRecord.salt, passwordRecord.iterations, getAuditUserId(req), userId] + ); + await pool.query('DELETE FROM auth_sessions WHERE user_id = ?', [userId]); + res.redirect('/admin/users?message=' + encodeURIComponent('Password updated.')); + } catch (error) { + next(error); + } + }); + + app.post('/admin/users/:id/delete', async function (req, res, next) { + try { + const userId = Number(req.params.id); + if (!Number.isInteger(userId) || userId <= 0) { + return res.status(400).send('Invalid user.'); + } + if (Number(req.currentUser.id) === userId) { + return res.redirect('/admin/users?message=' + encodeURIComponent('You cannot delete your own account from the users page.')); + } + + const [countRows] = await pool.query('SELECT COUNT(*) AS user_count FROM users'); + if (!countRows.length || Number(countRows[0].user_count) <= 1) { + return res.redirect('/admin/users?message=' + encodeURIComponent('At least one user must remain.')); + } + + const [result] = await pool.query('DELETE FROM users WHERE id = ?', [userId]); + if (!result.affectedRows) { + return res.status(404).send('User not found.'); + } + res.redirect('/admin/users?message=' + encodeURIComponent('User deleted.')); + } catch (error) { + next(error); + } + }); + + app.get('/admin/clients', async function (req, res, next) { + try { + const data = await common.fetchAdminData(pool); + res.send(pages.renderConnectedClientsPage(data, req.query.message ? String(req.query.message) : '', req.currentUser)); + } catch (error) { + next(error); + } + }); + + async function getDashboardStateSnapshot() { + if (!dashboardRefreshInFlight) { + dashboardRefreshInFlight = buildDashboardState(pool).finally(function () { + dashboardRefreshInFlight = null; + }); + } + return dashboardRefreshInFlight; + } + + function sendDashboardState(socket, state) { + if (!socket || socket.readyState !== WebSocket.OPEN) { + return; + } + socket.send(buildDashboardPayload(state)); + } + + async function broadcastDashboardState() { + if (!dashboardClients.size) { + return; + } + const state = await getDashboardStateSnapshot(); + const payload = buildDashboardPayload(state); + dashboardClients.forEach(function (socket) { + if (socket.readyState === WebSocket.OPEN) { + socket.send(payload); + } + }); + } + + async function sendDashboardStateToSocket(socket) { + try { + const state = await getDashboardStateSnapshot(); + sendDashboardState(socket, state); + } catch (_error) { + try { + socket.close(); + } catch (_closeError) { + // ignore close errors + } + } + } + + app.post('/admin/commands', async function (req, res, next) { + try { + const command = String(req.body.command || '').trim().toLowerCase(); + if (!command) { + return res.status(400).send('Command is required.'); + } + if (['reload', 'blackout'].indexOf(command) === -1) { + return res.status(400).send('Unsupported command.'); + } + + const slugs = await fetchAllScreenSlugs(pool); + const sent = command === 'blackout' + ? await notifyPlayerScreens(slugs, { + command: 'blackout', + blackout: !['0', 'false', 'off', 'restore'].includes(String(req.body.blackout || '').trim().toLowerCase()) + }) + : await notifyPlayerScreens(slugs, command); + await broadcastDashboardState(); + + const message = sent + ? `Sent ${command} command to ${sent} screen${sent === 1 ? '' : 's'}.` + : `No screens were available for the ${command} command.`; + + if (String(req.get('X-Requested-With') || '').toLowerCase() === 'xmlhttprequest') { + return res.json({ + ok: true, + command: command, + sent: sent, + message: message + }); + } + + res.redirect('/admin?message=' + encodeURIComponent(message)); + } catch (error) { + next(error); + } + }); + + app.post('/admin/screens/:slug/commands', async function (req, res, next) { + try { + const command = String(req.body.command || '').trim().toLowerCase(); + const connectionId = String(req.body.connectionId || '').trim(); + const blackoutValue = req.body && Object.prototype.hasOwnProperty.call(req.body, 'blackout') + ? req.body.blackout + : undefined; + if (!command) { + return res.status(400).send('Command is required.'); + } + if (['refresh', 'reload', 'pause', 'blackout', 'previous', 'next', 'left', 'right'].indexOf(command) === -1) { + return res.status(400).send('Unsupported command.'); + } + const commandPayload = command === 'blackout' && blackoutValue !== undefined + ? { + command: command, + blackout: blackoutValue + } + : command; + await forwardPlayerCommand(req.params.slug, commandPayload, connectionId); + await broadcastDashboardState(); + + if (String(req.get('X-Requested-With') || '').toLowerCase() === 'xmlhttprequest') { + return res.json({ + ok: true, + message: connectionId ? `Sent ${command} command to a connected client on ${req.params.slug}.` : `Sent ${command} command to ${req.params.slug}.` + }); + } + + res.redirect('/admin?message=' + encodeURIComponent(connectionId ? `Sent ${command} command to a connected client on ${req.params.slug}.` : `Sent ${command} command to ${req.params.slug}.`)); + } catch (error) { + next(error); + } + }); + + app.get('/admin/playlists', async function (req, res, next) { + try { + const data = await common.fetchAdminData(pool); + if (req.query.edit) { + const playlist = await common.fetchPlaylistById(pool, Number(req.query.edit)); + if (!playlist) { + return res.status(404).send('Playlist not found'); + } + return res.send(pages.renderPlaylistEditPage(playlist, data, req.query.message ? String(req.query.message) : '', req.currentUser)); + } + res.send(pages.renderPlaylistsPage(data, req.query.message ? String(req.query.message) : '', req.currentUser)); + } catch (error) { + next(error); + } + }); + + app.get('/admin/playlists/new', function (req, res) { + res.send(pages.renderPlaylistFormPage(req.query.message ? String(req.query.message) : '', req.currentUser)); + }); + + app.post('/admin/playlists', async function (req, res, next) { + try { + const name = String(req.body.name || '').trim(); + const fadeBetweenSlides = req.body.fade_between_slides ? 1 : 0; + if (!name) { + return res.status(400).send('Playlist name is required.'); + } + const actorId = getAuditUserId(req); + await pool.query('INSERT INTO playlists (name, fade_between_slides, created_by, modified_by) VALUES (?, ?, ?, ?)', [name, fadeBetweenSlides, actorId, actorId]); + res.redirect('/admin/playlists?message=' + encodeURIComponent('Playlist created.')); + } catch (error) { + next(error); + } + }); + + app.post('/admin/playlists/:id', async function (req, res, next) { + const connection = await pool.getConnection(); + try { + const name = String(req.body.name || '').trim(); + const fadeBetweenSlides = req.body.fade_between_slides ? 1 : 0; + if (!name) { + return res.status(400).send('Playlist name is required.'); + } + const playlist = await common.fetchPlaylistById(connection, Number(req.params.id)); + if (!playlist) { + return res.status(404).send('Playlist not found'); + } + + const affectedScreens = await fetchScreensByPlaylistId(connection, playlist.id); + + const slideIds = readArrayField(req.body, ['slide_id[]', 'slide_id']); + const durations = readArrayField(req.body, ['duration_seconds[]', 'duration_seconds']); + const scheduleModes = readArrayField(req.body, ['schedule_mode[]', 'schedule_mode']); + const scheduleStartDateTimes = readArrayField(req.body, ['schedule_start_datetime[]', 'schedule_start_datetime']); + const scheduleEndDateTimes = readArrayField(req.body, ['schedule_end_datetime[]', 'schedule_end_datetime']); + const scheduleStartTimes = readArrayField(req.body, ['schedule_start_time[]', 'schedule_start_time']); + const scheduleEndTimes = readArrayField(req.body, ['schedule_end_time[]', 'schedule_end_time']); + const scheduleDaysJsonValues = readArrayField(req.body, ['schedule_days_json[]', 'schedule_days_json']); + + if (durations.length && durations.length !== slideIds.length) { + return res.status(400).send('Playlist slide data is invalid.'); + } + if (scheduleModes.length && scheduleModes.length !== slideIds.length) { + return res.status(400).send('Playlist schedule data is invalid.'); + } + + const normalizedSlides = []; + const seenSlideIds = new Set(); + for (let i = 0; i < slideIds.length; i += 1) { + const slideId = Number(slideIds[i]); + if (!Number.isInteger(slideId) || slideId <= 0) { + return res.status(400).send('Invalid slide selection.'); + } + if (seenSlideIds.has(slideId)) { + return res.status(400).send('A slide can only be added to a playlist once.'); + } + seenSlideIds.add(slideId); + + const durationRaw = Number(durations[i]); + const durationSeconds = Number.isFinite(durationRaw) ? Math.max(1, Math.trunc(durationRaw)) : 10; + const scheduleMode = normalizeScheduleMode(scheduleModes[i]); + + let scheduleStartDatetime = null; + let scheduleEndDatetime = null; + let scheduleStartTime = null; + let scheduleEndTime = null; + let scheduleDaysJson = null; + + if (scheduleMode === 'dates') { + scheduleStartDatetime = parseDateTimeLocal(scheduleStartDateTimes[i]); + scheduleEndDatetime = parseDateTimeLocal(scheduleEndDateTimes[i]); + if (!scheduleStartDatetime || !scheduleEndDatetime) { + return res.status(400).send('Start and end datetimes are required for date scheduling.'); + } + if (scheduleEndDatetime < scheduleStartDatetime) { + return res.status(400).send('End datetime must be after start datetime.'); + } + } else if (scheduleMode === 'times') { + scheduleStartTime = parseTimeLocal(scheduleStartTimes[i]); + scheduleEndTime = parseTimeLocal(scheduleEndTimes[i]); + if (!scheduleStartTime || !scheduleEndTime) { + return res.status(400).send('Start and end times are required for time scheduling.'); + } + + let scheduleDays = []; + try { + const parsedDays = JSON.parse(String(scheduleDaysJsonValues[i] || '[]')); + scheduleDays = Array.isArray(parsedDays) ? parsedDays : []; + } catch (_error) { + scheduleDays = []; + } + scheduleDays = scheduleDays + .map(function (value) { return Number(value); }) + .filter(function (value) { return Number.isInteger(value) && value >= 0 && value <= 6; }); + if (!scheduleDays.length) { + return res.status(400).send('Select at least one day for time scheduling.'); + } + scheduleDaysJson = JSON.stringify(Array.from(new Set(scheduleDays)).sort()); + } + + normalizedSlides.push({ + slideId, + position: i, + durationSeconds, + scheduleMode, + scheduleStartDatetime, + scheduleEndDatetime, + scheduleStartTime, + scheduleEndTime, + scheduleDaysJson + }); + } + + if (normalizedSlides.length) { + const [slides] = await connection.query( + `SELECT sl.id, cs.width AS canvas_width, cs.height AS canvas_height + FROM slides sl + LEFT JOIN slide_templates st ON st.id = sl.template_id + LEFT JOIN canvas_sizes cs ON cs.id = st.canvas_size_id + WHERE sl.id IN (?)`, + [normalizedSlides.map(function (item) { return item.slideId; })] + ); + if (slides.length !== normalizedSlides.length) { + return res.status(400).send('One or more selected slides no longer exist.'); + } + const signatures = Array.from(new Set( + slides + .map(function (slide) { return getCanvasSignature(slide.canvas_width, slide.canvas_height); }) + .filter(Boolean) + )); + if (signatures.length > 1) { + return res.status(400).send('All playlist slides must share the same canvas size.'); + } + } + + const actorId = getAuditUserId(req); + + await connection.beginTransaction(); + await connection.query('UPDATE playlists SET name = ?, fade_between_slides = ?, modified_by = ? WHERE id = ?', [name, fadeBetweenSlides, actorId, playlist.id]); + await connection.query('DELETE FROM playlist_slides WHERE playlist_id = ?', [playlist.id]); + for (let i = 0; i < normalizedSlides.length; i += 1) { + const item = normalizedSlides[i]; + await connection.query( + 'INSERT INTO playlist_slides (playlist_id, slide_id, position, duration_seconds, schedule_mode, schedule_start_datetime, schedule_end_datetime, schedule_start_time, schedule_end_time, schedule_days_json, created_by, modified_by) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', + [ + playlist.id, + item.slideId, + item.position, + item.durationSeconds, + item.scheduleMode, + item.scheduleStartDatetime, + item.scheduleEndDatetime, + item.scheduleStartTime, + item.scheduleEndTime, + item.scheduleDaysJson, + actorId, + actorId + ] + ); + } + await connection.commit(); + await notifyPlayerScreens(affectedScreens, 'refresh'); + await broadcastDashboardState(); + res.redirect('/admin/playlists?edit=' + playlist.id + '&message=' + encodeURIComponent('Playlist updated.')); + } catch (error) { + try { + await connection.rollback(); + } catch (_rollbackError) { + // Ignore rollback failures and surface the original error. + } + next(error); + } finally { + connection.release(); + } + }); + + app.post('/admin/playlists/:id/delete', async function (req, res, next) { + try { + const playlist = await common.fetchPlaylistById(pool, Number(req.params.id)); + if (!playlist) { + return res.status(404).send('Playlist not found'); + } + await pool.query('DELETE FROM playlists WHERE id = ?', [playlist.id]); + res.redirect('/admin/playlists?message=' + encodeURIComponent('Playlist deleted.')); + } catch (error) { + next(error); + } + }); + + app.post('/admin/playlists/:id/slides', async function (req, res, next) { + try { + const playlist = await common.fetchPlaylistById(pool, Number(req.params.id)); + if (!playlist) { + return res.status(404).send('Playlist not found'); + } + const slideId = Number(req.body.slide_id); + if (!slideId) { + return res.status(400).send('Slide is required.'); + } + const playlistCanvasSignature = await fetchPlaylistCanvasSignature(pool, playlist.id); + if (playlistCanvasSignature === 'mismatch') { + return res.status(400).send('This playlist already contains slides with different canvas sizes.'); + } + const slide = await common.fetchSlideById(pool, slideId); + if (!slide) { + return res.status(404).send('Slide not found'); + } + const slideCanvasSignature = getCanvasSignature(slide.canvas_width, slide.canvas_height); + if (playlistCanvasSignature && slideCanvasSignature !== playlistCanvasSignature) { + return res.status(400).send('The slide canvas size must match the existing playlist items.'); + } + const durationSeconds = Math.max(1, Number(req.body.duration_seconds || 10)); + const [positionRows] = await pool.query('SELECT COALESCE(MAX(position), -1) AS max_position FROM playlist_slides WHERE playlist_id = ?', [playlist.id]); + const nextPosition = Number(positionRows[0].max_position) + 1; + const actorId = getAuditUserId(req); + await pool.query('INSERT INTO playlist_slides (playlist_id, slide_id, position, duration_seconds, created_by, modified_by) VALUES (?, ?, ?, ?, ?, ?)', [playlist.id, slideId, nextPosition, durationSeconds, actorId, actorId]); + await notifyPlayerScreens(await fetchScreensByPlaylistId(pool, playlist.id), 'refresh'); + await broadcastDashboardState(); + res.redirect('/admin/playlists?edit=' + playlist.id + '&message=' + encodeURIComponent('Slide added to playlist.')); + } catch (error) { + next(error); + } + }); + + app.post('/admin/playlists/:id/slides/:playlistSlideId', async function (req, res, next) { + try { + const playlist = await common.fetchPlaylistById(pool, Number(req.params.id)); + if (!playlist) { + return res.status(404).send('Playlist not found'); + } + const playlistSlideId = Number(req.params.playlistSlideId); + const durationSeconds = Math.max(1, Number(req.body.duration_seconds || 10)); + const actorId = getAuditUserId(req); + const [result] = await pool.query( + 'UPDATE playlist_slides SET duration_seconds = ?, modified_by = ? WHERE id = ? AND playlist_id = ?', + [durationSeconds, actorId, playlistSlideId, playlist.id] + ); + if (!result.affectedRows) { + return res.status(404).send('Playlist slide not found'); + } + await notifyPlayerScreens(await fetchScreensByPlaylistId(pool, playlist.id), 'refresh'); + await broadcastDashboardState(); + res.redirect('/admin/playlists?edit=' + playlist.id + '&message=' + encodeURIComponent('Slide duration updated.')); + } catch (error) { + next(error); + } + }); + + app.post('/admin/playlists/:id/slides/:playlistSlideId/move', async function (req, res, next) { + const connection = await pool.getConnection(); + try { + const playlist = await common.fetchPlaylistById(connection, Number(req.params.id)); + if (!playlist) { + return res.status(404).send('Playlist not found'); + } + const playlistSlideId = Number(req.params.playlistSlideId); + const direction = String(req.body.direction || '').toLowerCase(); + if (direction !== 'up' && direction !== 'down') { + return res.status(400).send('Invalid move direction.'); + } + + await connection.beginTransaction(); + const orderedSlides = await fetchOrderedPlaylistSlides(connection, playlist.id); + const currentIndex = orderedSlides.findIndex(function (item) { + return Number(item.id) === playlistSlideId; + }); + if (currentIndex === -1) { + await connection.rollback(); + return res.status(404).send('Playlist slide not found'); + } + + const swapIndex = direction === 'up' ? currentIndex - 1 : currentIndex + 1; + if (swapIndex < 0 || swapIndex >= orderedSlides.length) { + await connection.rollback(); + return res.redirect('/admin/playlists?edit=' + playlist.id + '&message=' + encodeURIComponent('Slide is already at the ' + (direction === 'up' ? 'top' : 'bottom') + '.')); + } + + const currentSlide = orderedSlides[currentIndex]; + const swapSlide = orderedSlides[swapIndex]; + const actorId = getAuditUserId(req); + + await connection.query('UPDATE playlist_slides SET position = ?, modified_by = ? WHERE id = ? AND playlist_id = ?', [swapSlide.position, actorId, currentSlide.id, playlist.id]); + await connection.query('UPDATE playlist_slides SET position = ?, modified_by = ? WHERE id = ? AND playlist_id = ?', [currentSlide.position, actorId, swapSlide.id, playlist.id]); + await connection.commit(); + await notifyPlayerScreens(await fetchScreensByPlaylistId(connection, playlist.id), 'refresh'); + await broadcastDashboardState(); + res.redirect('/admin/playlists?edit=' + playlist.id + '&message=' + encodeURIComponent('Slide order updated.')); + } catch (error) { + await connection.rollback(); + next(error); + } finally { + connection.release(); + } + }); + + app.get('/admin/playlists/:id/slides/:playlistSlideId/config', async function (req, res, next) { + try { + const playlist = await common.fetchPlaylistById(pool, Number(req.params.id)); + if (!playlist) { + return res.status(404).send('Playlist not found'); + } + const data = await common.fetchAdminData(pool); + const playlistSlide = (data.playlistSlides || []).find(function (item) { + return item.id === Number(req.params.playlistSlideId) && item.playlist_id === playlist.id; + }); + if (!playlistSlide) { + return res.status(404).send('Playlist slide not found'); + } + playlistSlide.schedule_days = common.parseJsonSafe(playlistSlide.schedule_days_json) || []; + return res.send(pages.renderPlaylistSlideConfigPage(playlist, playlistSlide, req.query.message ? String(req.query.message) : '', req.query.row_key ? String(req.query.row_key) : '', req.currentUser)); + } catch (error) { + next(error); + } + }); + + app.post('/admin/playlists/:id/slides/:playlistSlideId/config', async function (req, res, next) { + try { + const playlist = await common.fetchPlaylistById(pool, Number(req.params.id)); + if (!playlist) { + return res.status(404).send('Playlist not found'); + } + const rowKey = String(req.body.row_key || '').trim(); + if (rowKey) { + return res.status(400).send('Schedule changes from the playlist editor are staged until you click Save changes.'); + } + const playlistSlideId = Number(req.params.playlistSlideId); + let scheduleMode = normalizeScheduleMode(req.body.schedule_mode); + let scheduleStartDatetime = null; + let scheduleEndDatetime = null; + let scheduleStartTime = null; + let scheduleEndTime = null; + let scheduleDaysJson = null; + + const hasDateRange = Boolean(req.body.schedule_start_datetime && req.body.schedule_end_datetime); + const hasTimeRange = Boolean(req.body.schedule_start_time && req.body.schedule_end_time); + const hasSelectedDays = Boolean(readArrayField(req.body, ['schedule_days', 'schedule_days[]']).length); + + if (scheduleMode === 'dates' && !hasDateRange) { + scheduleMode = 'always'; + } else if (scheduleMode === 'times' && (!hasTimeRange || !hasSelectedDays)) { + scheduleMode = 'always'; + } + + if (scheduleMode === 'dates') { + scheduleStartDatetime = parseDateTimeLocal(req.body.schedule_start_datetime); + scheduleEndDatetime = parseDateTimeLocal(req.body.schedule_end_datetime); + if (!scheduleStartDatetime || !scheduleEndDatetime) { + scheduleMode = 'always'; + scheduleStartDatetime = null; + scheduleEndDatetime = null; + } else if (scheduleEndDatetime < scheduleStartDatetime) { + return res.status(400).send('End datetime must be after start datetime.'); + } + } else if (scheduleMode === 'times') { + scheduleStartTime = parseTimeLocal(req.body.schedule_start_time); + scheduleEndTime = parseTimeLocal(req.body.schedule_end_time); + const scheduleDays = readArrayField(req.body, ['schedule_days', 'schedule_days[]']).map(function (value) { + return Number(value); + }).filter(function (value) { + return Number.isInteger(value) && value >= 0 && value <= 6; + }); + if (!scheduleStartTime || !scheduleEndTime) { + scheduleMode = 'always'; + scheduleStartTime = null; + scheduleEndTime = null; + scheduleDaysJson = null; + } else if (!scheduleDays.length) { + scheduleMode = 'always'; + scheduleStartTime = null; + scheduleEndTime = null; + scheduleDaysJson = null; + } else { + if (scheduleEndTime < scheduleStartTime) { + return res.status(400).send('End time must be after start time.'); + } + scheduleDaysJson = JSON.stringify(Array.from(new Set(scheduleDays)).sort()); + } + } + + const actorId = getAuditUserId(req); + const [result] = await pool.query( + 'UPDATE playlist_slides SET schedule_mode = ?, schedule_start_datetime = ?, schedule_end_datetime = ?, schedule_start_time = ?, schedule_end_time = ?, schedule_days_json = ?, modified_by = ? WHERE id = ? AND playlist_id = ?', + [scheduleMode, scheduleStartDatetime, scheduleEndDatetime, scheduleStartTime, scheduleEndTime, scheduleDaysJson, actorId, playlistSlideId, playlist.id] + ); + if (!result.affectedRows) { + return res.status(404).send('Playlist slide not found'); + } + await notifyPlayerScreens(await fetchScreensByPlaylistId(pool, playlist.id), 'refresh'); + await broadcastDashboardState(); + res.redirect('/admin/playlists?edit=' + playlist.id + '&message=' + encodeURIComponent('Slide timings updated.')); + } catch (error) { + next(error); + } + }); + + app.post('/admin/playlists/:id/slides/:playlistSlideId/delete', async function (req, res, next) { + try { + const playlist = await common.fetchPlaylistById(pool, Number(req.params.id)); + if (!playlist) { + return res.status(404).send('Playlist not found'); + } + await pool.query('DELETE FROM playlist_slides WHERE id = ? AND playlist_id = ?', [Number(req.params.playlistSlideId), playlist.id]); + await notifyPlayerScreens(await fetchScreensByPlaylistId(pool, playlist.id), 'refresh'); + await broadcastDashboardState(); + res.redirect('/admin/playlists?edit=' + playlist.id + '&message=' + encodeURIComponent('Slide removed.')); + } catch (error) { + next(error); + } + }); + + app.get('/admin/screens', async function (req, res, next) { + try { + const data = await buildDashboardState(pool); + if (req.query.edit) { + const screen = await common.fetchScreenById(pool, Number(req.query.edit)); + if (!screen) { + return res.status(404).send('Screen not found'); + } + const editData = await common.fetchScreenEditData(pool); + return res.send(pages.renderScreenEditPage(screen, editData, req.query.message ? String(req.query.message) : '', req.currentUser)); + } + res.send(pages.renderScreensPage(data, req.query.message ? String(req.query.message) : '', req.currentUser)); + } catch (error) { + next(error); + } + }); + + app.get('/admin/screens/:id/edit', async function (req, res, next) { + try { + const screen = await common.fetchScreenById(pool, Number(req.params.id)); + if (!screen) { + return res.status(404).send('Screen not found'); + } + const editData = await common.fetchScreenEditData(pool); + return res.send(pages.renderScreenEditPage(screen, editData, req.query.message ? String(req.query.message) : '', req.currentUser)); + } catch (error) { + next(error); + } + }); + + app.get('/admin/playlists/:id/edit', async function (req, res, next) { + try { + const playlist = await common.fetchPlaylistById(pool, Number(req.params.id)); + if (!playlist) { + return res.status(404).send('Playlist not found'); + } + const data = await common.fetchAdminData(pool); + res.send(pages.renderPlaylistEditPage(playlist, data, req.query.message ? String(req.query.message) : '', req.currentUser)); + } catch (error) { + next(error); + } + }); + + app.get('/admin/screens/new', async function (req, res, next) { + try { + const data = await common.fetchScreenEditData(pool); + res.send(pages.renderScreenFormPage(data, req.query.message ? String(req.query.message) : '', req.currentUser)); + } catch (error) { + next(error); + } + }); + + app.post('/admin/screens', async function (req, res, next) { + try { + const name = String(req.body.name || '').trim(); + if (!name) { + return res.status(400).send('Screen name is required.'); + } + const playlistId = req.body.playlist_id ? Number(req.body.playlist_id) : null; + const slug = await common.uniqueScreenSlug(pool, common.slugify(name)); + const actorId = getAuditUserId(req); + await pool.query('INSERT INTO screens (name, slug, playlist_id, created_by, modified_by) VALUES (?, ?, ?, ?, ?)', [name, slug, playlistId, actorId, actorId]); + res.redirect('/admin/screens?message=' + encodeURIComponent('Screen created.')); + } catch (error) { + next(error); + } + }); + + app.post('/admin/screens/:id', async function (req, res, next) { + try { + const name = String(req.body.name || '').trim(); + if (!name) { + return res.status(400).send('Screen name is required.'); + } + const screen = await common.fetchScreenById(pool, Number(req.params.id)); + if (!screen) { + return res.status(404).send('Screen not found'); + } + const playlistId = req.body.playlist_id ? Number(req.body.playlist_id) : null; + await pool.query('UPDATE screens SET name = ?, playlist_id = ?, modified_by = ? WHERE id = ?', [name, playlistId, getAuditUserId(req), screen.id]); + res.redirect('/admin/screens?edit=' + screen.id + '&message=' + encodeURIComponent('Screen updated.')); + } catch (error) { + next(error); + } + }); + + app.post('/admin/screens/:id/delete', async function (req, res, next) { + try { + const screen = await common.fetchScreenById(pool, Number(req.params.id)); + if (!screen) { + return res.status(404).send('Screen not found'); + } + await pool.query('DELETE FROM screens WHERE id = ?', [screen.id]); + res.redirect('/admin/screens?message=' + encodeURIComponent('Screen deleted.')); + } catch (error) { + next(error); + } + }); + + app.get('/admin/slides', async function (req, res, next) { + try { + const data = await common.fetchAdminData(pool); + res.send(pages.renderSlidesPage(data, req.query.message ? String(req.query.message) : '', req.currentUser)); + } catch (error) { + next(error); + } + }); + + app.get('/admin/slides/new', async function (req, res, next) { + try { + const data = await common.fetchTemplatesData(pool); + res.send(pages.renderSlideFormPage(data, 'create', null, req.query.message ? String(req.query.message) : '', req.currentUser)); + } catch (error) { + next(error); + } + }); + + app.get('/admin/slides/:id/edit', async function (req, res, next) { + try { + const slide = await common.fetchSlideById(pool, Number(req.params.id)); + if (!slide) { + return res.status(404).send('Slide not found'); + } + const data = await common.fetchTemplatesData(pool); + res.send(pages.renderSlideFormPage(data, 'edit', slide, req.query.message ? String(req.query.message) : '', req.currentUser)); + } catch (error) { + next(error); + } + }); + + app.post('/admin/slides', upload.any(), async function (req, res, next) { + try { + const payload = await common.buildSlidePayload(pool, req, null); + const actorId = getAuditUserId(req); + const [result] = await pool.query( + 'INSERT INTO slides (title, body, template_id, content_json, media_path, media_type, created_by, modified_by) VALUES (?, ?, ?, ?, ?, ?, ?, ?)', + [payload.title, payload.body, payload.templateId, payload.contentJson, payload.mediaPath, payload.mediaType, actorId, actorId] + ); + res.redirect('/admin/slides/' + result.insertId + '/edit?message=' + encodeURIComponent('Slide created.')); + } catch (error) { + next(error); + } + }); + + app.post('/admin/slides/:id', upload.any(), async function (req, res, next) { + try { + const slide = await common.fetchSlideById(pool, Number(req.params.id)); + if (!slide) { + return res.status(404).send('Slide not found'); + } + const affectedScreens = await fetchScreensBySlideId(pool, slide.id); + const payload = await common.buildSlidePayload(pool, req, slide); + const actorId = getAuditUserId(req); + await pool.query( + 'UPDATE slides SET title = ?, body = ?, template_id = ?, content_json = ?, media_path = ?, media_type = ?, modified_by = ? WHERE id = ?', + [payload.title, payload.body, payload.templateId, payload.contentJson, payload.mediaPath, payload.mediaType, actorId, slide.id] + ); + await notifyPlayerScreens(affectedScreens, 'refresh'); + await broadcastDashboardState(); + res.redirect('/admin/slides/' + slide.id + '/edit?message=' + encodeURIComponent('Slide updated.')); + } catch (error) { + next(error); + } + }); + + app.post('/admin/slides/:id/delete', async function (req, res, next) { + try { + const slide = await common.fetchSlideById(pool, Number(req.params.id)); + if (!slide) { + return res.status(404).send('Slide not found'); + } + const affectedScreens = await fetchScreensBySlideId(pool, slide.id); + await pool.query('DELETE FROM slides WHERE id = ?', [slide.id]); + await notifyPlayerScreens(affectedScreens, 'refresh'); + await broadcastDashboardState(); + res.redirect('/admin/slides?message=' + encodeURIComponent('Slide deleted.')); + } catch (error) { + next(error); + } + }); + + app.get('/admin/templates', async function (req, res, next) { + try { + const data = await common.fetchTemplatesData(pool); + res.send(pages.renderTemplatesPage(data, req.query.message ? String(req.query.message) : '', req.currentUser)); + } catch (error) { + next(error); + } + }); + + app.get('/admin/templates/new', async function (req, res, next) { + try { + const data = await common.fetchCanvasSizesData(pool); + res.send(pages.renderTemplateFormPage(null, 'create', req.query.message ? String(req.query.message) : '', data.canvasSizes, req.currentUser)); + } catch (error) { + next(error); + } + }); + + app.post('/admin/templates', upload.any(), async function (req, res, next) { + try { + const payload = await common.buildTemplatePayload(pool, req, null); + const actorId = getAuditUserId(req); + const [result] = await pool.query( + 'INSERT INTO slide_templates (name, canvas_size_id, background_image_path, created_by, modified_by) VALUES (?, ?, ?, ?, ?)', + [payload.name, payload.canvasSizeId, payload.backgroundImagePath, actorId, actorId] + ); + for (let i = 0; i < payload.regions.length; i += 1) { + const region = payload.regions[i]; + await pool.query( + 'INSERT INTO slide_template_regions (template_id, region_key, region_type, label, font_family, x, y, width, height, z_index, created_by, modified_by) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', + [result.insertId, region.region_key, region.region_type, region.label, region.font_family, region.x, region.y, region.width, region.height, region.z_index, actorId, actorId] + ); + } + res.redirect('/admin/templates/' + result.insertId + '/edit?message=' + encodeURIComponent('Template created.')); + } catch (error) { + next(error); + } + }); + + app.get('/admin/templates/:id/edit', async function (req, res, next) { + try { + const template = await common.fetchTemplateById(pool, Number(req.params.id)); + if (!template) { + return res.status(404).send('Template not found'); + } + const sizeData = await common.fetchCanvasSizesData(pool); + res.send(pages.renderTemplateFormPage(template, 'edit', req.query.message ? String(req.query.message) : '', sizeData.canvasSizes, req.currentUser)); + } catch (error) { + next(error); + } + }); + + app.post('/admin/templates/:id', upload.any(), async function (req, res, next) { + try { + const template = await common.fetchTemplateById(pool, Number(req.params.id)); + if (!template) { + return res.status(404).send('Template not found'); + } + const payload = await common.buildTemplatePayload(pool, req, template); + const actorId = getAuditUserId(req); + await pool.query( + 'UPDATE slide_templates SET name = ?, canvas_size_id = ?, background_image_path = ?, modified_by = ? WHERE id = ?', + [payload.name, payload.canvasSizeId, payload.backgroundImagePath, actorId, template.id] + ); + await pool.query('DELETE FROM slide_template_regions WHERE template_id = ?', [template.id]); + for (let i = 0; i < payload.regions.length; i += 1) { + const region = payload.regions[i]; + await pool.query( + 'INSERT INTO slide_template_regions (template_id, region_key, region_type, label, font_family, x, y, width, height, z_index, created_by, modified_by) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', + [template.id, region.region_key, region.region_type, region.label, region.font_family, region.x, region.y, region.width, region.height, region.z_index, actorId, actorId] + ); + } + res.redirect('/admin/templates/' + template.id + '/edit?message=' + encodeURIComponent('Template updated.')); + } catch (error) { + next(error); + } + }); + + app.post('/admin/templates/:id/delete', async function (req, res, next) { + try { + const template = await common.fetchTemplateById(pool, Number(req.params.id)); + if (!template) { + return res.status(404).send('Template not found'); + } + await pool.query('UPDATE slides SET template_id = NULL, modified_by = ? WHERE template_id = ?', [getAuditUserId(req), template.id]); + await pool.query('DELETE FROM slide_template_regions WHERE template_id = ?', [template.id]); + await pool.query('DELETE FROM slide_templates WHERE id = ?', [template.id]); + res.redirect('/admin/templates?message=' + encodeURIComponent('Template deleted.')); + } catch (error) { + next(error); + } + }); + + app.get('/admin/canvas-sizes', async function (req, res, next) { + try { + const data = await common.fetchAdminData(pool); + res.send(pages.renderCanvasSizesPage(data, req.query.message ? String(req.query.message) : '', req.currentUser)); + } catch (error) { + next(error); + } + }); + + app.get('/admin/canvas-sizes/new', function (req, res) { + res.send(pages.renderCanvasSizeFormPage(null, 'create', req.query.message ? String(req.query.message) : '', req.currentUser)); + }); + + app.post('/admin/canvas-sizes', async function (req, res, next) { + try { + const payload = common.buildCanvasSizePayload(req, null); + const actorId = getAuditUserId(req); + await pool.query('INSERT INTO canvas_sizes (name, width, height, created_by, modified_by) VALUES (?, ?, ?, ?, ?)', [payload.name, payload.width, payload.height, actorId, actorId]); + res.redirect('/admin/canvas-sizes?message=' + encodeURIComponent('Canvas size created.')); + } catch (error) { + next(error); + } + }); + + app.get('/admin/canvas-sizes/:id/edit', async function (req, res, next) { + try { + const canvasSize = await common.fetchCanvasSizeById(pool, Number(req.params.id)); + if (!canvasSize) { + return res.status(404).send('Canvas size not found'); + } + res.send(pages.renderCanvasSizeEditPage(canvasSize, null, req.query.message ? String(req.query.message) : '', req.currentUser)); + } catch (error) { + next(error); + } + }); + + app.post('/admin/canvas-sizes/:id', async function (req, res, next) { + try { + const canvasSize = await common.fetchCanvasSizeById(pool, Number(req.params.id)); + if (!canvasSize) { + return res.status(404).send('Canvas size not found'); + } + const payload = common.buildCanvasSizePayload(req, canvasSize); + await pool.query('UPDATE canvas_sizes SET name = ?, width = ?, height = ?, modified_by = ? WHERE id = ?', [payload.name, payload.width, payload.height, getAuditUserId(req), canvasSize.id]); + res.redirect('/admin/canvas-sizes?message=' + encodeURIComponent('Canvas size updated.')); + } catch (error) { + next(error); + } + }); + + app.post('/admin/canvas-sizes/:id/delete', async function (req, res, next) { + try { + const canvasSize = await common.fetchCanvasSizeById(pool, Number(req.params.id)); + if (!canvasSize) { + return res.status(404).send('Canvas size not found'); + } + await pool.query('UPDATE slide_templates SET canvas_size_id = NULL, modified_by = ? WHERE canvas_size_id = ?', [getAuditUserId(req), canvasSize.id]); + await pool.query('DELETE FROM canvas_sizes WHERE id = ?', [canvasSize.id]); + res.redirect('/admin/canvas-sizes?message=' + encodeURIComponent('Canvas size deleted.')); + } catch (error) { + next(error); + } + }); + + app.use(function (error, _req, res, _next) { + console.error(error); + res.status(error.statusCode || 500).send(error.statusCode ? error.message : 'Internal server error'); + }); + + await common.ensureSchema(pool); + server.on('upgrade', async function (request, socket, head) { + let pathname = ''; + try { + pathname = new URL(request.url, 'http://localhost').pathname; + } catch (_error) { + socket.destroy(); + return; + } + + if (pathname !== '/ws/admin/dashboard') { + socket.destroy(); + return; + } + + try { + const currentUser = await loadCurrentUser(pool, request); + if (!currentUser) { + socket.destroy(); + return; + } + } catch (_error) { + socket.destroy(); + return; + } + + dashboardWs.handleUpgrade(request, socket, head, function (ws) { + dashboardWs.emit('connection', ws, request); + }); + }); + + dashboardWs.on('connection', function (socket) { + dashboardClients.add(socket); + sendDashboardStateToSocket(socket); + + socket.on('close', function () { + dashboardClients.delete(socket); + }); + + socket.on('error', function () { + dashboardClients.delete(socket); + }); + }); + + server.listen(PORT, function () { + console.log(`Pulse Signage app listening on port ${PORT}`); + }); + + setInterval(function () { + broadcastDashboardState().catch(function (error) { + console.error(error); + }); + }, dashboardRefreshIntervalMs); +} + +module.exports = { start }; + +if (require.main === module) { + start().catch(function (error) { + console.error(error); + process.exit(1); + }); +} diff --git a/src/webui/public/css/admin-editor.css b/src/webui/public/css/admin-editor.css new file mode 100644 index 0000000..78629a9 --- /dev/null +++ b/src/webui/public/css/admin-editor.css @@ -0,0 +1,356 @@ +.slide-editor-top { + display: grid; + grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); + gap: 16px; + align-items: start; + margin-bottom: 16px; +} + +.slide-image-file { + margin-top: 8px; +} + +.slide-preview-card { + display: flex; + flex-direction: column; + gap: 0; + padding: 0; +} + +.slide-preview-header { + margin-bottom: 0; + padding: 16px 16px 8px; +} + +.slide-preview-header h3 { + margin: 0; +} + +.slide-preview-stage { + position: relative; + width: 100%; + aspect-ratio: 16 / 9; + overflow: hidden; + border-radius: 0; + background: linear-gradient(135deg, #111827, #1f2937); +} + +.slide-preview-scroll { + min-height: 0; + padding: 0 16px 16px; + margin-top: 0; +} + +.slide-preview-scroll button { + margin-bottom: 8px; +} + +.slide-preview-background { + position: absolute; + inset: 0; + width: 100%; + height: 100%; + object-fit: cover; +} + +.slide-preview-empty { + position: absolute; + inset: 0; + display: flex; + align-items: center; + justify-content: center; + text-align: center; + padding: 20px; + color: #cbd5e1; + background: rgba(17, 24, 39, 0.35); + z-index: 1; +} + +.slide-preview-overlay { + position: absolute; + inset: 0; + z-index: 2; +} + +.slide-preview-region { + position: absolute; + box-sizing: border-box; + padding: 0; + overflow: hidden; + color: #fff; + border: 1px solid rgba(255, 255, 255, 0.25); + background: rgba(15, 23, 42, 0.2); + backdrop-filter: blur(1px); +} + +.slide-preview-region h1, +.slide-preview-region h2, +.slide-preview-region h3, +.slide-preview-region h4, +.slide-preview-region h5, +.slide-preview-region h6, +.slide-preview-region p, +.slide-preview-region blockquote, +.slide-preview-region ul, +.slide-preview-region ol, +.slide-preview-region pre { + margin: 0; +} + +.slide-preview-region ul, +.slide-preview-region ol { + padding-left: 18px; +} + +.slide-preview-region code { + white-space: pre-wrap; +} + +.slide-preview-text-content { + width: 100%; + height: 100%; +} + +.slide-preview-image-region { + background: rgba(15, 23, 42, 0.25); +} + +.slide-preview-image { + width: 100%; + height: 100%; + object-fit: contain; + display: block; +} + +.slide-preview-webpage-region { + background: rgba(15, 23, 42, 0.35); + overflow: hidden; +} + +.slide-preview-webpage-frame { + width: 100%; + height: 100%; + border: 0; + display: block; + background: #fff; + overflow: hidden; +} + +.slide-preview-html-region { + background: rgba(15, 23, 42, 0.35); + overflow: hidden; +} + +.slide-preview-html-frame { + width: 100%; + height: 100%; + border: 0; + display: block; + background: transparent; + overflow: hidden; +} + +.slide-schedule-dialog { + width: min(900px, calc(100vw - 32px)); + max-width: 900px; + border: none; + border-radius: 12px; + padding: 16px; + background: var(--surface); + color: var(--text); + box-shadow: 0 24px 80px rgba(15, 23, 42, 0.35); +} + +.slide-schedule-dialog::backdrop { + background: rgba(15, 23, 42, 0.55); +} + +.slide-schedule-dialog iframe { + width: 100%; + height: auto; + min-height: 120px; + border: 0; + margin-top: 0; + background: var(--surface); +} + +.slide-schedule-dialog-head { + display: flex; + justify-content: space-between; + gap: 12px; + align-items: center; +} + +.slide-schedule-dialog button { + width: auto; + margin: 0; +} + +.slide-schedule-dialog button.secondary { + box-shadow: none; +} + +.slide-schedule-dialog button.secondary:hover { + filter: brightness(0.92); +} + +.slide-schedule-dialog form { + display: grid; + gap: 16px; +} + +.slide-schedule-dialog .card { + display: grid; + gap: 12px; +} + +.schedule-type-field { + display: block; + margin-bottom: 20px; +} + +.schedule-day-option { + margin-top: 0; + justify-content: flex-start; + width: 100%; + gap: 14px; +} + +.schedule-day-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(110px, 1fr)); + gap: 8px; + margin-top: 16px; +} + +.schedule-day-label { + font-size: 13px; + font-weight: 700; + color: var(--text); + line-height: 1.1; +} + +.schedule-day-option .switch-ui { + flex: none; +} + +.schedule-day-option input:focus-visible + .switch-ui { + outline: 3px solid var(--focus-ring); + outline-offset: 2px; +} + +.schedule-day-option input:checked + .switch-ui { + background: linear-gradient(180deg, var(--primary), var(--primary-dark)); + border-color: rgba(37, 99, 235, 0.55); + box-shadow: inset 0 1px 2px rgba(15, 23, 42, 0.08), 0 0 0 4px rgba(37, 99, 235, 0.12); +} + +.schedule-save-actions { + margin-top: 16px; +} + +.schedule-save-actions button { + margin-top: 0; +} + +.slide-preview-placeholder { + display: flex; + align-items: center; + justify-content: center; + height: 100%; + color: #cbd5e1; + font-size: 12px; +} + +.slide-preview-table { + width: 100%; + border-collapse: collapse; + border-spacing: 0; + background: none; +} + +.slide-preview-table tbody tr { + background: none; + border: default; +} + +.slide-preview-table th, +.slide-preview-table td { + border: 1px solid rgba(255, 255, 255, 0.35); + padding: 6px 8px; + text-align: left; + vertical-align: top; +} + +.slide-preview-table th { + font-weight: 700; +} + +.rich-editor { + display: grid; + gap: 8px; +} + +.rich-toolbar { + display: flex; + flex-wrap: wrap; + gap: 6px; +} + +.rich-toolbar button { + width: auto; + margin-top: 0; + padding: 8px 10px; +} + +.rich-surface { + min-height: 240px; + padding: 12px; + border: 1px solid #d1d5db; + border-radius: 8px; + background: #fff; + color: var(--text); + line-height: 1.4; + overflow: auto; +} + +.rich-surface:focus { + outline: 2px solid rgba(37, 99, 235, 0.3); + border-color: var(--primary); +} + +.editorjs-holder { + min-height: 240px; + border: 1px solid var(--border-strong); + border-radius: 8px; + padding: 16px 12px 12px; + background: var(--editor-paper); + color: var(--editor-text); + margin-top: 8px; +} + +.editorjs-holder .codex-editor, +.editorjs-holder .codex-editor__redactor, +.editorjs-holder .ce-block, +.editorjs-holder .ce-paragraph, +.editorjs-holder .ce-header, +.editorjs-holder .ce-list, +.editorjs-holder .ce-list__item, +.editorjs-holder [contenteditable], +.editorjs-holder [contenteditable] * { + color: var(--editor-text); +} + +.editorjs-holder [contenteditable] { + caret-color: var(--editor-text); +} + +.editorjs-holder .codex-editor__redactor [contenteditable]:empty:after { + color: rgba(0, 0, 0, 0.45); +} + +@media (max-width: 1100px) { + .slide-editor-top { + grid-template-columns: 1fr; + } +} \ No newline at end of file diff --git a/src/webui/public/css/admin.css b/src/webui/public/css/admin.css new file mode 100644 index 0000000..66b7aee --- /dev/null +++ b/src/webui/public/css/admin.css @@ -0,0 +1,1887 @@ +:root { + color-scheme: light; + --bg: #eef2f7; + --bg-accent: #f8fbff; + --surface: #ffffff; + --surface-alt: #f6f8fc; + --text: #102033; + --muted: #66758a; + --border: rgba(16, 32, 51, 0.1); + --border-strong: rgba(16, 32, 51, 0.16); + --primary: #2563eb; + --primary-dark: #1d4ed8; + --primary-soft: rgba(37, 99, 235, 0.12); + --success: #0f9d58; + --danger: #dc2626; + --warning: #b45309; + --focus-ring: rgba(37, 99, 235, 0.18); + --page-html-bg: radial-gradient(circle at top left, rgba(37, 99, 235, 0.12), transparent 30%), linear-gradient(180deg, #f9fbff 0%, var(--bg) 100%); + --page-body-bg: radial-gradient(circle at top right, rgba(14, 165, 233, 0.16), transparent 24%), radial-gradient(circle at 20% 0%, rgba(37, 99, 235, 0.12), transparent 22%), linear-gradient(180deg, #f8fbff 0%, var(--bg) 100%); + --auth-body-bg: radial-gradient(circle at top left, rgba(34, 197, 94, 0.16), transparent 24%), radial-gradient(circle at 80% 20%, rgba(37, 99, 235, 0.18), transparent 28%), linear-gradient(180deg, #eef5fb 0%, #e6edf6 100%); + --sidebar-background: radial-gradient(circle at top right, rgba(59, 130, 246, 0.18), transparent 30%), linear-gradient(180deg, #0f172a 0%, #111c32 100%); + --sidebar-border: rgba(255, 255, 255, 0.08); + --sidebar-text: #dbe7ff; + --sidebar-text-strong: #ffffff; + --sidebar-muted: rgba(219, 231, 255, 0.7); + --sidebar-surface: rgba(255, 255, 255, 0.06); + --sidebar-hover: rgba(255, 255, 255, 0.12); + --sidebar-divider: rgba(219, 231, 255, 0.12); + --sidebar-link: #93c5fd; + --table-border: rgba(16, 32, 51, 0.08); + --table-hover: rgba(37, 99, 235, 0.04); + --table-row-odd: rgba(15, 23, 42, 0.02); + --table-row-even: rgba(255, 255, 255, 0.82); + --stat-background: linear-gradient(180deg, var(--surface), var(--surface-alt)); + --button-secondary-bg: #334155; + --button-secondary-hover: #1f2937; + --theme-toggle-bg: linear-gradient(180deg, var(--surface), var(--surface-alt)); + --theme-toggle-hover: linear-gradient(180deg, var(--surface-alt), var(--surface)); + --editor-paper: #ffffff; + --editor-text: #000000; + --shadow-lg: 0 24px 60px rgba(15, 23, 42, 0.16); + --shadow-md: 0 16px 28px rgba(15, 23, 42, 0.12); + --shadow-sm: 0 8px 20px rgba(15, 23, 42, 0.08); +} + +html[data-theme='dark'] { + color-scheme: dark; + --bg: #0b1220; + --bg-accent: #111827; + --surface: #111b2d; + --surface-alt: #172337; + --text: #e6eefb; + --muted: #9ba9be; + --border: rgba(148, 163, 184, 0.16); + --border-strong: rgba(148, 163, 184, 0.26); + --primary: #60a5fa; + --primary-dark: #3b82f6; + --primary-soft: rgba(96, 165, 250, 0.16); + --success: #34d399; + --danger: #f87171; + --warning: #f59e0b; + --focus-ring: rgba(96, 165, 250, 0.28); + --page-html-bg: radial-gradient(circle at top left, rgba(96, 165, 250, 0.16), transparent 26%), linear-gradient(180deg, #060b14 0%, #0b1220 100%); + --page-body-bg: radial-gradient(circle at top right, rgba(14, 165, 233, 0.12), transparent 24%), radial-gradient(circle at 20% 0%, rgba(96, 165, 250, 0.12), transparent 22%), linear-gradient(180deg, #08111d 0%, #0b1220 100%); + --auth-body-bg: radial-gradient(circle at top left, rgba(34, 197, 94, 0.12), transparent 24%), radial-gradient(circle at 80% 20%, rgba(96, 165, 250, 0.16), transparent 28%), linear-gradient(180deg, #070d18 0%, #0b1220 100%); + --sidebar-background: radial-gradient(circle at top right, rgba(59, 130, 246, 0.14), transparent 30%), linear-gradient(180deg, #09101d 0%, #0e1728 100%); + --sidebar-border: rgba(148, 163, 184, 0.1); + --sidebar-text: #dce8fb; + --sidebar-text-strong: #ffffff; + --sidebar-muted: rgba(220, 232, 251, 0.66); + --sidebar-surface: rgba(255, 255, 255, 0.08); + --sidebar-hover: rgba(255, 255, 255, 0.14); + --sidebar-divider: rgba(148, 163, 184, 0.12); + --sidebar-link: #bfdbfe; + --table-border: rgba(148, 163, 184, 0.14); + --table-hover: rgba(96, 165, 250, 0.08); + --table-row-odd: rgba(255, 255, 255, 0.03); + --table-row-even: rgba(15, 23, 42, 0.28); + --stat-background: linear-gradient(180deg, var(--surface), var(--surface-alt)); + --button-secondary-bg: #334155; + --button-secondary-hover: #475569; + --theme-toggle-bg: linear-gradient(180deg, #162235, #111b2d); + --theme-toggle-hover: linear-gradient(180deg, #1b2a41, #162235); + --editor-paper: #ffffff; + --editor-text: #000000; + --shadow-lg: 0 28px 64px rgba(2, 6, 23, 0.45); + --shadow-md: 0 18px 30px rgba(2, 6, 23, 0.28); + --shadow-sm: 0 10px 22px rgba(2, 6, 23, 0.24); +} + +* { + box-sizing: border-box; +} + +html { + min-height: 100%; + -webkit-text-size-adjust: 100%; + background: var(--page-html-bg); +} + +body { + margin: 0; + min-height: 100vh; + font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; + color: var(--text); + background: var(--page-body-bg); +} + +a { + color: var(--primary); +} + +.admin-shell-body { + overflow-x: hidden; +} + +.app-shell { + min-height: 100vh; + display: grid; + grid-template-columns: 280px minmax(0, 1fr); +} + +.sidebar { + position: sticky; + top: 0; + align-self: start; + min-height: 100vh; + padding: 22px 18px 18px; + color: var(--sidebar-text); + background: var(--sidebar-background); + border-right: 1px solid var(--sidebar-border); +} + +.brand { + display: flex; + align-items: center; + gap: 12px; + padding: 6px 8px 16px; +} + +.brand-mark { + width: 44px; + height: 44px; + border-radius: 14px; + display: grid; + place-items: center; + background: linear-gradient(135deg, #f97316, #eab308 24%, #22c55e 48%, #06b6d4 72%, #8b5cf6); + background-size: 220% 220%; + animation: logo-rainbow 8s ease-in-out infinite; + color: #fff; + font-weight: 800; + letter-spacing: 0.08em; + box-shadow: 0 12px 30px rgba(37, 99, 235, 0.4); +} + +.brand-copy { + display: grid; + gap: 2px; +} + +.brand-copy strong { + font-size: 16px; + letter-spacing: 0.01em; +} + +.brand-copy span { + color: var(--sidebar-muted); + font-size: 12px; +} + +.sidebar-actions { + padding: 4px 8px 12px; +} + +.nav { + display: grid; + gap: 8px; +} + +.nav-label { + padding: 10px 12px 4px; + color: var(--sidebar-muted); + text-transform: uppercase; + letter-spacing: 0.12em; + font-size: 11px; +} + +.nav-item { + display: flex; + align-items: center; + gap: 10px; + color: var(--sidebar-text); + text-decoration: none; + padding: 12px 14px; + border-radius: 14px; + background: transparent; + transition: background-color 160ms ease, color 160ms ease, transform 160ms ease; +} + +.nav-item::before { + content: ''; + width: 8px; + height: 8px; + border-radius: 999px; + background: rgba(255, 255, 255, 0.24); + flex: none; +} + +.nav-item.active, +.nav-item:hover { + background: var(--sidebar-surface); + color: var(--sidebar-text-strong); + transform: translateX(2px); +} + +.nav-item.active::before, +.nav-item:hover::before { + background: #60a5fa; +} + +.sidebar-footer { + margin-top: auto; + padding: 16px 14px 8px; + display: flex; + align-items: center; + gap: 12px; + color: var(--sidebar-text); +} + +.sidebar-footer strong, +.sidebar-footer span { + display: block; +} + +.sidebar-footer strong { + color: var(--sidebar-text-strong); + font-size: 14px; +} + +.sidebar-account-name { + color: var(--sidebar-text-strong); + font-size: 14px; + font-weight: 800; +} + +.sidebar-footer span { + color: var(--sidebar-muted); + font-size: 12px; + line-height: 1.35; +} + +.sidebar-footer--account { + align-items: flex-start; + gap: 10px; + justify-content: flex-start; +} + +.sidebar-divider { + height: 1px; + margin: 10px 14px 4px; + background: var(--sidebar-divider); +} + +.user-avatar { + width: 34px; + height: 34px; + min-width: 34px; + border-radius: 999px; + display: grid; + place-items: center; + flex: none; + background: linear-gradient(180deg, #60a5fa, #2563eb); + color: #fff; + font-size: 14px; + font-weight: 800; + letter-spacing: 0.02em; + box-shadow: 0 10px 22px rgba(37, 99, 235, 0.28); +} + +.user-avatar--fallback { + background: linear-gradient(180deg, #94a3b8, #64748b); + box-shadow: 0 10px 22px rgba(100, 116, 139, 0.2); +} + +.sidebar-footer-links { + display: flex; + gap: 12px; + margin-top: 8px; + flex-wrap: wrap; +} + +.sidebar-footer-links a { + color: var(--sidebar-link); + text-decoration: none; + font-size: 12px; + font-weight: 700; +} + +.sidebar-footer-links a:hover { + color: var(--sidebar-text-strong); +} + +.theme-toggle { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 0; + width: auto; + min-height: 0; + margin-top: 0; + padding: 6px; + border: 0; + border-radius: 0; + background: transparent; + color: var(--text); + box-shadow: none; +} + +.theme-toggle:hover { + background: transparent; + filter: none; +} + +.theme-toggle:active { + transform: translateY(1px); + filter: none; +} + +.theme-toggle__label { + display: block; + text-align: left; + line-height: 1.1; +} + +.theme-toggle__icon { + position: relative; + display: inline-block; + width: 16px; + height: 16px; + flex: none; +} + +.theme-toggle__icon--moon { + border-radius: 50%; + background: var(--theme-toggle-bg); + color: var(--text); +} + +.login-page-body .theme-toggle__icon--moon { + background: currentColor; +} + +.theme-toggle__icon--moon::after { + content: ''; + position: absolute; + top: -1px; + right: -3px; + width: 12px; + height: 12px; + border-radius: 50%; + background: currentColor; +} + +.login-page-body .theme-toggle__icon--moon::after { + background: var(--auth-body-bg); +} + +.theme-toggle__icon--sun { + display: none; + color: var(--text); +} + +.theme-toggle__icon--sun::before, +.theme-toggle__icon--sun::after { + content: ''; + position: absolute; + left: 50%; + top: 50%; + transform: translate(-50%, -50%); + border-radius: 50%; +} + +.theme-toggle__icon--sun::before { + width: 2px; + height: 2px; + background: currentColor; + box-shadow: + 0 -8px 0 0 currentColor, + 0 8px 0 0 currentColor, + 8px 0 0 0 currentColor, + -8px 0 0 0 currentColor, + 6px 6px 0 0 currentColor, + -6px 6px 0 0 currentColor, + 6px -6px 0 0 currentColor, + -6px -6px 0 0 currentColor; +} + +.theme-toggle__icon--sun::after { + width: 7px; + height: 7px; + background: var(--theme-toggle-bg); + box-shadow: inset 0 0 0 2px currentColor; +} + +html[data-theme='dark'] .theme-toggle__icon--sun { + display: inline-block; +} + +.theme-toggle--sidebar { + position: absolute; + right: 18px; + bottom: 18px; + width: auto; + padding: 0; + justify-content: center; + margin: 0; + z-index: 2; +} + +.theme-toggle--sidebar .theme-toggle__label { + display: none; +} + +.theme-toggle--sidebar .theme-toggle__icon { + width: 18px; + height: 18px; +} + +.theme-toggle--floating { + width: auto; + padding: 0; + justify-content: center; +} + +.theme-toggle--floating .theme-toggle__label { + display: none; +} + +.theme-toggle--floating .theme-toggle__icon { + width: 18px; + height: 18px; +} + +.theme-toggle--floating { + position: fixed; + top: 16px; + right: 16px; + z-index: 90; +} + +.auth-shell-body .theme-toggle--floating { + top: auto; + bottom: 16px; +} + +.status-dot { + width: 12px; + height: 12px; + border-radius: 999px; + background: #22c55e; + box-shadow: 0 0 0 6px rgba(34, 197, 94, 0.14); + flex: none; +} + +.status-dot--unknown { + background: #94a3b8; + box-shadow: 0 0 0 6px rgba(148, 163, 184, 0.14); +} + +.status-dot--online { + background: #22c55e; + box-shadow: 0 0 0 6px rgba(34, 197, 94, 0.14); +} + +.status-dot--offline { + background: #ef4444; + box-shadow: 0 0 0 6px rgba(239, 68, 68, 0.14); +} + +.status-pill--unknown { + color: #334155; + background: rgba(148, 163, 184, 0.18); +} + +.status-pill--online { + color: #14532d; + background: rgba(34, 197, 94, 0.16); +} + +.status-pill--offline { + color: #7f1d1d; + background: rgba(239, 68, 68, 0.14); +} + +.shell-main { + min-width: 0; +} + +.status-pill { + display: inline-flex; + align-items: center; + gap: 8px; + min-height: 38px; + padding: 0 14px; + border-radius: 999px; + font-size: 13px; + font-weight: 700; + text-decoration: none; +} + +.status-pill { + color: #0f5132; + background: rgba(16, 185, 129, 0.14); +} + +.content { + padding: 28px; +} + +.page-shell { + display: grid; + gap: 18px; +} + +.page-shell > * { + animation: fade-up 240ms ease-out both; +} + +.page-header { + display: flex; + align-items: end; + justify-content: space-between; + gap: 16px; + margin-bottom: 4px; +} + +.page-header h2 { + margin: 0; + font-size: 22px; + line-height: 1.2; +} + +.page-header p { + margin: 0; + color: var(--muted); +} + +.toast { + position: fixed; + top: auto; + bottom: 20px; + right: 20px; + z-index: 1000; + display: flex; + align-items: center; + gap: 12px; + max-width: min(360px, calc(100vw - 40px)); + padding: 10px 14px; + border-radius: 16px; + background: linear-gradient(180deg, var(--surface), var(--surface-alt)); + border: 1px solid var(--border-strong); + border-left: 4px solid var(--primary); + color: var(--text); + box-shadow: var(--shadow-md); +} + +.toast-body { + flex: 1; + line-height: 1.4; +} + +.toast-close { + width: 30px; + min-width: 30px; + height: 30px; + margin: 0; + padding: 0; + border-radius: 999px; + background: var(--bg); + color: var(--muted); + font-size: 20px; + line-height: 1; +} + +.toast-close:hover { + background: var(--surface-alt); + color: var(--text); +} + +.toast-hide { + opacity: 0; + transform: translateY(-8px); + transition: opacity 180ms ease, transform 180ms ease; +} + +.grid { + display: grid; + gap: 16px; + grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); +} + +.card { + background: var(--surface); + border: 1px solid var(--border); + border-radius: 20px; + padding: 18px; + box-shadow: var(--shadow-sm); + margin-bottom: 0; +} + +.auth-shell-body { + background: var(--auth-body-bg); +} + +.auth-page { + min-height: 100vh; + display: grid; + place-items: center; + padding: 24px; +} + +.auth-card { + width: min(100%, 480px); + padding: 28px; +} + +.auth-brand { + display: flex; + gap: 16px; + align-items: center; + margin-bottom: 18px; +} + +.auth-brand h1 { + margin: 4px 0 6px; + font-size: 28px; +} + +.auth-brand p, +.auth-hint { + margin: 0; + color: var(--muted); +} + +.eyebrow { + display: block; + color: var(--primary-dark); + text-transform: uppercase; + letter-spacing: 0.12em; + font-size: 11px; + font-weight: 800; +} + +.auth-message { + margin: 18px 0 0; + padding: 12px 14px; + border-radius: 14px; + background: var(--primary-soft); + color: var(--primary-dark); + font-weight: 700; +} + +.auth-form { + display: grid; + gap: 16px; + margin-top: 18px; +} + +.auth-form button { + margin-top: 4px; +} + +.auth-hint { + margin-top: 16px; + font-size: 13px; + line-height: 1.5; +} + +.account-card { + max-width: 720px; +} + +.user-create-card { + max-width: 980px; +} + +.user-create-form { + display: grid; + gap: 20px; +} + +.user-create-section { + display: grid; + gap: 14px; +} + +.user-create-section h3 { + margin: 0; +} + +.user-create-fields { + display: grid; + gap: 14px; + grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); +} + +.user-create-fields label { + display: grid; + gap: 8px; +} + +.user-create-actions { + margin-top: 4px; + flex-wrap: wrap; +} + +.user-create-button { + min-height: 42px; + padding-left: 18px; + padding-right: 18px; +} + +.button-link.secondary { + background: var(--button-secondary-bg); +} + +.button-link.secondary:hover { + background: var(--button-secondary-hover); +} + +.user-edit-card { + max-width: 980px; +} + +.user-edit-header { + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; + margin-bottom: 16px; +} + +.user-edit-grid { + display: grid; + gap: 16px; + grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); +} + +.user-edit-panel { + display: grid; + gap: 14px; + margin-bottom: 0; +} + +.user-edit-panel h3 { + margin: 0; +} + +.user-edit-button { + width: 100%; + min-height: 42px; + display: flex; + align-items: center; + justify-content: center; + white-space: nowrap; +} + + +.users-table th:last-child { + width: 220px; +} + +.users-row-actions { + justify-content: flex-start; +} + +.user-cell { + display: flex; + align-items: center; + gap: 12px; +} + +.user-avatar--small { + width: 30px; + height: 30px; + min-width: 30px; + font-size: 12px; +} + +.user-inline-form, +.user-password-form { + display: grid; + gap: 8px; +} + +.user-password-form { + grid-template-columns: minmax(120px, 1fr) minmax(120px, 1fr) auto; + align-items: start; +} + +.user-inline-form .button-link, +.user-password-form .button-link { + width: fit-content; +} + +.account-form { + display: grid; + gap: 16px; +} + +.account-form .actions { + margin-top: 4px; +} + +.account-form .button-link.secondary { + background: var(--button-secondary-bg); +} + +.account-form .button-link.secondary:hover { + background: var(--button-secondary-hover); +} + +.playlist-name-card { + margin-bottom: 16px; +} + +.slide-template-card { + margin-bottom: 16px; +} + +.template-meta-card { + margin-bottom: 16px; +} + +.canvas-size-form { + display: grid; + gap: 16px; +} + +.canvas-size-form .row { + align-items: stretch; +} + +.canvas-size-form button { + margin-top: 0; +} + +.screen-edit-form { + display: grid; + gap: 14px; +} + +.screen-edit-form label { + display: grid; + gap: 8px; +} + +.screen-edit-form input, +.screen-edit-form select, +.screen-edit-form button { + margin-top: 0; +} + +.screen-edit-form button { + width: fit-content; +} + +.screen-edit-actions { + display: flex; + padding-top: 14px; + gap: 10px; + flex-wrap: wrap; + align-items: center; +} + +.screen-edit-actions .inline-form { + display: inline-flex; +} + +.screen-edit-actions button, +.screen-edit-actions .inline-form button { + width: fit-content; + margin-top: 0; +} + +.card h3 { + margin: 0 0 14px; + font-size: 18px; +} + +.stats { + display: grid; + gap: 16px; + grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); +} + +.stat { + position: relative; + overflow: hidden; + background: var(--stat-background); + border: 1px solid var(--border); + border-radius: 20px; + padding: 18px; + box-shadow: var(--shadow-sm); +} + +.stat::after { + content: ''; + position: absolute; + inset: auto -16px -18px auto; + width: 88px; + height: 88px; + background-repeat: no-repeat; + background-position: center; + background-size: 64% 64%; + opacity: 0.18; + pointer-events: none; +} + +.stats .stat:nth-child(1)::after { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%232563eb' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 6h16'/%3E%3Cpath d='M4 12h16'/%3E%3Cpath d='M4 18h10'/%3E%3C/svg%3E"); +} + +.stats .stat:nth-child(2)::after { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%232563eb' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='4' y='4' width='16' height='16' rx='2'/%3E%3Cpath d='M8 13l2-2 3 3 2-2 3 3'/%3E%3Cpath d='M8 9h.01'/%3E%3C/svg%3E"); +} + +.stats .stat:nth-child(3)::after { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%232563eb' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='5' width='18' height='12' rx='2'/%3E%3Cpath d='M8 19h8'/%3E%3Cpath d='M12 17v2'/%3E%3C/svg%3E"); +} + +.stats .stat:nth-child(4)::after { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%232563eb' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M8 11a3 3 0 1 0 0-6 3 3 0 0 0 0 6Z'/%3E%3Cpath d='M16 12a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5Z'/%3E%3Cpath d='M4.5 19a5.5 5.5 0 0 1 11 0'/%3E%3Cpath d='M14 19a4.5 4.5 0 0 1 5.5-4.4'/%3E%3C/svg%3E"); +} + +.stat strong { + position: relative; + display: block; + font-size: 32px; + line-height: 1; + margin-bottom: 8px; +} + +.stat span { + position: relative; + color: var(--muted); + font-size: 13px; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.08em; +} + +.row { + display: flex; + gap: 12px; + flex-wrap: wrap; + align-items: center; +} + +.switch-field { + display: inline-flex; + align-items: center; + gap: 12px; + width: fit-content; + margin-top: 5px; + align-self: center; +} + +.switch-control { + position: relative; + display: inline-flex; + align-items: center; + cursor: pointer; + flex: none; +} + +.switch-control input { + position: absolute; + width: 1px; + height: 1px; + margin: 0; + padding: 0; + border: 0; + clip: rect(0, 0, 0, 0); + clip-path: inset(50%); + overflow: hidden; + white-space: nowrap; +} + +.switch-ui { + position: relative; + width: 54px; + height: 32px; + border-radius: 999px; + background: #cbd5e1; + border: 1px solid rgba(16, 32, 51, 0.14); + box-shadow: inset 0 1px 2px rgba(15, 23, 42, 0.12); + flex: none; + transition: background-color 160ms ease, border-color 160ms ease, box-shadow 160ms ease; +} + +.switch-ui::after { + content: ''; + position: absolute; + top: 3px; + left: 3px; + width: 24px; + height: 24px; + border-radius: 50%; + background: #fff; + box-shadow: 0 6px 16px rgba(15, 23, 42, 0.18); + transition: transform 160ms ease; +} + +.switch-label { + font-size: 14px; + font-weight: 700; + color: var(--text); + line-height: 1; +} + +.switch-control input:checked + .switch-ui { + background: linear-gradient(180deg, var(--primary), var(--primary-dark)); + border-color: rgba(37, 99, 235, 0.55); + box-shadow: inset 0 1px 2px rgba(15, 23, 42, 0.08), 0 0 0 4px rgba(37, 99, 235, 0.12); +} + +.switch-control input:checked + .switch-ui::after { + transform: translateX(22px); +} + +.switch-field:focus-within .switch-ui { + outline: 3px solid rgba(37, 99, 235, 0.18); + outline-offset: 2px; +} + +.actions { + display: flex; + gap: 8px; + flex-wrap: wrap; + align-items: center; +} + +.page-toolbar { + display: flex; + gap: 8px; + flex-wrap: wrap; + align-items: center; + justify-content: flex-end; +} + +label { + display: block; + font-size: 14px; + font-weight: 700; + color: var(--text); +} + +input, +textarea, +select, +button { + width: 100%; + box-sizing: border-box; + padding: 10px 12px; + margin-top: 6px; + border: 1px solid var(--border-strong); + border-radius: 12px; + font: inherit; + background: var(--surface); + color: var(--text); +} + +input:focus, +textarea:focus, +button:focus-visible, +a.button-link:focus-visible, +button.sidebar-toggle:focus-visible { + outline: 3px solid var(--focus-ring); + outline-offset: 2px; +} + +select:focus-visible { + outline: 3px solid var(--focus-ring); + outline-offset: 2px; +} + +select:focus { + outline: none; +} + +button:focus, +a.button-link:focus, +button.sidebar-toggle:focus { + outline: none; +} + +button { + background: linear-gradient(180deg, var(--primary), var(--primary-dark)); + color: #fff; + border: none; + cursor: pointer; + font-weight: 800; + box-shadow: 0 10px 20px rgba(37, 99, 235, 0.18); +} + +button:hover { + filter: brightness(0.92); +} + +button:active, +a.button-link:active, +button.sidebar-toggle:active { + filter: brightness(1.08); +} + +button.secondary, +a.button-link, +.status-pill { + border: 1px solid transparent; +} + +button.secondary { + background: var(--button-secondary-bg); +} + +button.secondary:hover { + background: var(--button-secondary-hover); +} + +button.danger, +button.is-danger { + background: linear-gradient(180deg, #ef4444, #dc2626); + box-shadow: 0 10px 18px rgba(220, 38, 38, 0.18); +} + +button.danger:hover, +button.is-danger:hover { + background: linear-gradient(180deg, #dc2626, #b91c1c); +} + +button.is-paused, +button.is-blackout { + background: linear-gradient(180deg, #f59e0b, #d97706); +} + +a.button-link { + display: inline-flex; + align-items: center; + justify-content: center; + min-height: 42px; + background: linear-gradient(180deg, var(--primary), var(--primary-dark)); + color: #fff; + text-decoration: none; + padding: 10px 14px; + border-radius: 12px; + font-weight: 800; + box-shadow: 0 10px 20px rgba(37, 99, 235, 0.18); +} + +a.button-link:hover { + color: #fff; + filter: brightness(0.92); +} + +.list-action { + display: inline-flex; + align-items: center; + justify-content: center; + width: 104px; + min-width: 104px; + box-sizing: border-box; + font: inherit; + font-size: 14px; + line-height: 1.2; + font-weight: 800; + text-align: center; + box-shadow: none; +} + +.actions a.button-link.list-action, +.actions button.list-action { + min-height: 38px; + padding: 8px 12px; + border-radius: 11px; + font-size: 13px; + line-height: 1.1; +} + +.actions a.button-link.list-action { + min-width: 96px; +} + +.actions button.list-action { + min-width: 96px; +} + +.list-action--nav, +a.button-link.list-action.list-action--nav, +button.list-action.list-action--nav { + width: 36.8px; + min-width: 36.8px; + height: 36.8px; + padding: 0; + display: inline-grid; + place-items: center; + font-size: 15px; + line-height: 1; + border-radius: 12px; + background: linear-gradient(180deg, #f59e0b, #d97706); + color: #fff; +} + +table .inline-form button.list-action--nav { + width: 36.8px; + min-width: 36.8px; + height: 36.8px; + padding: 0; +} + +.list-action--nav:hover { + filter: brightness(0.92); +} + +a.button-link.list-action, +button.list-action { + padding: 10px 14px; +} + +table { + width: 100%; + border-collapse: collapse; + border-spacing: 0; + overflow: hidden; + border-radius: 16px; +} + +th, +td { + text-align: left; + padding: 13px 14px; + border-bottom: 1px solid var(--table-border); + vertical-align: middle; + line-height: 1.4; +} + +thead th { + font-size: 13px; + font-weight: 800; + color: var(--muted); + text-transform: uppercase; + letter-spacing: 0.06em; + background: var(--surface-alt); +} + +tbody tr:hover { + background: var(--table-hover); +} + +tbody tr:last-child td { + border-bottom: none; +} + +.inline-form { + display: inline-flex; + margin: 0; +} + +.subtle { + color: var(--muted); + font-size: 13px; +} + +.connection-count { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 4px 10px; + border-radius: 999px; + background: rgba(37, 99, 235, 0.1); + color: #1d4ed8; + font-size: 12px; + font-weight: 800; + margin-bottom: 8px; +} + +.connection-id { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 4px 10px; + border-radius: 999px; + background: rgba(15, 118, 110, 0.12); + color: #0f766e; + font-size: 12px; + font-weight: 700; +} + +#dashboard-screens-table th:last-child, +#dashboard-screens-table td:last-child { + width: auto; + white-space: normal; +} + +.sidebar-toggle { + display: none; + align-items: center; + justify-content: center; + gap: 5px; + width: 46px; + height: 46px; + margin: 0; + padding: 0; + border-radius: 14px; + background: var(--surface); + border: 1px solid var(--border); + box-shadow: var(--shadow-sm); + flex: none; +} + +.sidebar-toggle--shell { + position: fixed; + top: 16px; + right: 16px; + left: auto; + z-index: 70; +} + +.sidebar-toggle-icon { + position: relative; + display: block; + width: 18px; + height: 2px; + border-radius: 999px; + background: #334155; + box-shadow: 0 -6px 0 #334155, 0 6px 0 #334155; +} + +.sidebar-backdrop { + display: none; +} + +.app-shell--frame { + grid-template-columns: 1fr; +} + +.shell-main--frame { + min-height: 100vh; +} + +.topbar--frame { + position: sticky; + top: 0; +} + +.content--frame { + padding: 20px; +} + +.page-shell--frame { + gap: 0; +} + +.admin-shell-body--frame-plain { + background: var(--surface); +} + +.admin-shell-body--frame-plain .content--frame { + padding: 0; + background: var(--surface); +} + +.admin-shell-body--frame-plain .page-shell--frame { + padding-top: 12px; +} + +.admin-shell-body--frame-plain .card { + box-shadow: none; +} + +.admin-shell-body--frame-plain button[type="submit"] { + padding: 8px 12px; +} + +@keyframes fade-up { + from { + opacity: 0; + transform: translateY(8px); + } + + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes logo-rainbow { + 0% { + background-position: 0% 50%; + } + + 50% { + background-position: 100% 50%; + } + + 100% { + background-position: 0% 50%; + } +} + +@media (max-width: 1100px) { + .app-shell { + grid-template-columns: 1fr; + } + + .sidebar { + position: fixed; + z-index: 60; + inset: 0 auto 0 0; + width: min(88vw, 320px); + transform: translateX(-104%); + transition: transform 220ms ease; + box-shadow: var(--shadow-lg); + } + + .admin-shell-body.is-sidebar-open .sidebar { + transform: translateX(0); + } + + .sidebar-toggle { + display: inline-flex; + } + + .sidebar-toggle--shell { + display: inline-flex; + } + + .sidebar-backdrop { + display: block; + position: fixed; + inset: 0; + z-index: 50; + width: 100%; + height: 100%; + margin: 0; + padding: 0; + border: 0; + border-radius: 0; + background: rgba(15, 23, 42, 0.42); + opacity: 0; + pointer-events: none; + transition: opacity 220ms ease; + } + + .admin-shell-body.is-sidebar-open .sidebar-backdrop { + opacity: 1; + pointer-events: auto; + } + + .content { + padding: 18px; + } +} + +@media (max-width: 720px) { + .toast { + top: auto; + bottom: 12px; + left: auto; + right: 12px; + max-width: none; + } + + .content { + padding: 12px; + } + + .page-shell { + gap: 12px; + } + + .page-header { + align-items: flex-start; + flex-direction: column; + padding-right: 72px; + } + + .card { + padding: 14px; + border-radius: 16px; + } + + .actions, + .playlist-item-actions, + .page-toolbar, + .playlist-edit-actions { + align-items: stretch; + flex-direction: row; + flex-wrap: wrap; + } + + .actions > *, + .playlist-item-actions > *, + .page-toolbar > *, + .playlist-edit-actions > * { + flex: 1 1 calc(50% - 6px); + min-width: 140px; + } + + .page-toolbar > *, + .playlist-edit-actions > * { + min-width: 160px; + } + + .stats { + grid-template-columns: 1fr; + } + + table.table-cards { + display: block; + width: 100%; + border-collapse: separate; + border-spacing: 0 12px; + overflow: visible; + } + + table.table-cards thead { + display: none; + } + + table.table-cards tbody { + display: grid; + gap: 12px; + } + + table.table-cards tbody tr { + display: grid; + gap: 8px; + padding: 14px; + border: 1px solid var(--border); + border-radius: 16px; + background: var(--surface); + box-shadow: var(--shadow-sm); + } + + table.table-cards th, + table.table-cards td { + display: flex; + flex-direction: column; + gap: 6px; + padding: 0; + border: 0; + white-space: normal; + } + + table.table-cards td::before { + content: attr(data-label); + color: var(--muted); + font-size: 11px; + font-weight: 800; + text-transform: uppercase; + letter-spacing: 0.06em; + } + + table.table-cards td.empty::before { + content: ''; + } + + table.table-cards td[data-label="Actions"] { + width: 100%; + } + + table.table-cards td .actions { + gap: 6px; + justify-content: flex-start; + } + + table.table-cards td[data-label="Actions"] .actions { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: flex-start; + width: 100%; + } + + table.table-cards td[data-label="Actions"] .actions > * { + flex: 0 0 auto; + min-width: 0; + } + + table.table-cards td[data-label="Actions"] .actions > * { + width: auto; + align-self: flex-start; + } + + .inline-form { + display: flex; + flex: 0 0 auto; + min-width: 0; + } + + table.table-cards td[data-label="Actions"] .inline-form { + display: inline-flex; + min-width: 0; + width: auto; + } + + table.table-cards td[data-label="Actions"] .inline-form button, + table.table-cards td[data-label="Actions"] .button-link, + table.table-cards td[data-label="Actions"] .list-action { + display: inline-flex; + width: auto; + min-width: 96px; + white-space: nowrap; + } + + table.table-cards td[data-label="Actions"] .inline-form button.list-action--nav, + table.table-cards td[data-label="Actions"] .list-action--nav { + flex: 0 0 36.8px; + min-width: 36.8px; + width: 36.8px; + height: 36.8px; + padding: 0; + } + + .inline-form button { + min-width: 0; + width: auto; + } +} + +thead th.sortable { + cursor: pointer; + user-select: none; + position: relative; + padding-right: 36px; +} + +thead th.sortable::after { + content: ''; + position: absolute; + right: 10px; + top: 50%; + display: grid; + place-items: center; + width: 18px; + height: 18px; + margin-top: -9px; + border-radius: 999px; + background: rgba(37, 99, 235, 0.08); + background-repeat: no-repeat; + background-position: center; + background-size: 10px 10px; + color: var(--primary); + font-size: 0; + font-weight: 900; + line-height: 1; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' fill='none'%3E%3Cpath d='M6 1 L2.5 4.5 H9.5 Z' fill='%232563eb'/%3E%3Cpath d='M6 11 L2.5 7.5 H9.5 Z' fill='%232563eb'/%3E%3C/svg%3E"); +} + +thead th.sortable.sort-asc::after { + background: rgba(37, 99, 235, 0.16); + background-repeat: no-repeat; + background-position: center; + background-size: 10px 10px; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' fill='none'%3E%3Cpath d='M6 1 L2.5 4.5 H9.5 Z' fill='%232563eb'/%3E%3C/svg%3E"); +} + +thead th.sortable.sort-asc, +thead th.sortable.sort-desc { + color: var(--text); +} + +thead th.sortable.sort-desc::after { + background: rgba(37, 99, 235, 0.16); + background-repeat: no-repeat; + background-position: center; + background-size: 10px 10px; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' fill='none'%3E%3Cpath d='M6 11 L2.5 7.5 H9.5 Z' fill='%232563eb'/%3E%3C/svg%3E"); +} + +thead th.unsortable { + cursor: default; +} + +tbody tr:nth-child(odd) { + background: var(--table-row-odd); +} + +tbody tr:nth-child(even) { + background: var(--table-row-even); +} + +tbody tr:hover { + background: rgba(37, 99, 235, 0.07); +} + +table input, +table select, +table textarea, +table button { + margin-top: 0; +} + +td .actions { + align-items: center; +} + +table th:last-child, +table td:last-child { + width: 1%; + white-space: nowrap; +} + +table td .actions { + width: auto; + justify-content: flex-end; + flex-wrap: nowrap; +} + +#dashboard-clients-table th:nth-child(1), +#dashboard-clients-table td:nth-child(1) { + width: 24%; + min-width: 240px; +} + +#dashboard-clients-table th:nth-child(2), +#dashboard-clients-table td:nth-child(2) { + width: 16%; + min-width: 120px; +} + +#dashboard-clients-table th:nth-child(3), +#dashboard-clients-table td:nth-child(3) { + width: 14%; + min-width: 110px; +} + +#dashboard-clients-table th:nth-child(4), +#dashboard-clients-table td:nth-child(4) { + width: 18%; + min-width: 150px; +} + +#dashboard-clients-table th:nth-child(5), +#dashboard-clients-table td:nth-child(5) { + width: 20%; + min-width: 180px; +} + +#dashboard-clients-table th:nth-child(6), +#dashboard-clients-table td:nth-child(6) { + width: 8%; + min-width: 220px; +} + +table td .inline-form, +table td .actions form { + flex: 0 0 auto; +} + +.inline-form button { + width: auto; + min-width: 112px; +} + +.list-action.is-paused { + background: #b45309; + color: #fff; + border-color: rgba(180, 83, 9, 0.24); + animation: action-flash 1.5s ease-in-out infinite alternate; +} + +.list-action.is-paused:hover { + background: #92400e; +} + +.list-action.is-danger { + background: #dc2626; + color: #fff; +} + +.list-action.is-danger:hover { + background: #b91c1c; +} + +.list-action.is-blackout { + background: #1f2937; + color: #fff; + border-color: rgba(31, 41, 55, 0.24); + animation: action-flash-blackout 0.75s ease-in-out infinite alternate; +} + +.list-action.is-blackout:hover { + background: #111827; +} + +@keyframes action-flash { + from { + background: #b45309; + filter: brightness(1); + } + + to { + background: #f59e0b; + filter: brightness(1.08); + } +} + +@keyframes action-flash-blackout { + from { + background: #235ce8; + filter: brightness(1); + } + + to { + background: #dd0b0b; + filter: brightness(1.08); + } +} + +.muted { + color: var(--muted); +} + +.stats { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); + gap: 12px; + margin-bottom: 0; +} + +.stat { + background: var(--stat-background); + border: 1px solid var(--border); + border-radius: 12px; + padding: 16px; + box-shadow: var(--shadow-sm); + margin-bottom: 0; +} + +.stat strong { + display: block; + font-size: 28px; + margin-bottom: 4px; +} + +.playlist-item-actions { + flex-wrap: nowrap; + align-items: center; +} + +.actions form { + display: inline; +} + +.playlist-edit-actions button { + width: auto; +} + +#playlist-add-slide-button { + margin-top: 12px; +} + +.playlist-edit-header { + align-items: flex-end; +} + +.playlist-name-field { + flex: 1; +} + +.empty { + color: var(--muted); + padding: 12px 0; +} + +.chip { + display: inline-block; + background: #e5e7eb; + color: #374151; + border-radius: 999px; + padding: 4px 10px; + font-size: 12px; + margin-left: 6px; +} + diff --git a/src/webui/public/css/template-designer.css b/src/webui/public/css/template-designer.css new file mode 100644 index 0000000..80078c7 --- /dev/null +++ b/src/webui/public/css/template-designer.css @@ -0,0 +1,245 @@ +.template-designer-layout { + display: grid; + grid-template-columns: minmax(0, 2fr) minmax(280px, 1fr); + gap: 12px; + align-items: start; +} + +.template-designer-main { + display: grid; + gap: 8px; +} + +.template-meta-row { + display: grid; + grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1fr); + gap: 8px; +} + +.template-meta-row > label { + flex: 1 1 0; + margin-top: 0; +} + +.template-designer-sidebar { + display: grid; + gap: 8px; +} + +.template-designer-actions { + display: flex; + flex-wrap: nowrap; + gap: 8px; + margin: 0 0 8px 0; +} + +#template-form .template-designer-actions button { + flex: 1 1 0; + width: auto; + margin-top: 0; +} + +.region-select { + width: 100%; + margin-bottom: 8px; +} + +#canvas-size-select:disabled { + opacity: 0.55; + cursor: not-allowed; +} + +#canvas-size-summary.is-locked { + color: #94a3b8; +} + +.designer-stage { + position: relative; + width: 100%; + background: #0f172a; + border-radius: 0; + overflow: hidden; + border: 1px solid #1f2937; + cursor: crosshair; + user-select: none; + box-sizing: border-box; +} + +.designer-stage img { + position: absolute; + inset: 0; + width: 100%; + height: 100%; + object-fit: fill; + pointer-events: none; +} + +.designer-stage .empty-bg { + position: absolute; + inset: 0; + display: flex; + align-items: center; + justify-content: center; + color: #94a3b8; + font-size: 14px; + pointer-events: none; +} + +.designer-overlay { + position: absolute; + inset: 0; +} + +.designer-rect { + position: absolute; + border: 2px solid #22c55e; + background: rgba(34, 197, 94, 0.14); + color: #fff; + font-size: 12px; + overflow: hidden; + box-sizing: border-box; +} + +.designer-rect.selected { + border-color: #f59e0b; + background: rgba(245, 158, 11, 0.18); +} + +.designer-rect-label { + position: absolute; + left: 0; + top: 0; + padding: 2px 6px; + background: rgba(15, 23, 42, 0.8); + font-size: 11px; + pointer-events: none; +} + +.designer-rect .resize-handle { + position: absolute; + width: 12px; + height: 12px; + background: var(--surface); + border: 1px solid var(--border-strong); + box-sizing: border-box; +} + +.designer-rect .resize-handle.nw { + left: -7px; + top: -7px; + cursor: nwse-resize; +} + +.designer-rect .resize-handle.ne { + right: -7px; + top: -7px; + cursor: nesw-resize; +} + +.designer-rect .resize-handle.sw { + left: -7px; + bottom: -7px; + cursor: nesw-resize; +} + +.designer-rect .resize-handle.se { + right: -7px; + bottom: -7px; + cursor: nwse-resize; +} + +.designer-draft { + border-style: dashed; + background: rgba(59, 130, 246, 0.12); + border-color: #60a5fa; +} + +.region-list { + display: grid; + gap: 8px; +} + +#template-form { + display: grid; +} + +#template-form label { + margin-top: 8px; +} + +#template-form input, +#template-form textarea, +#template-form select, +#template-form button { + margin-top: 4px; +} + +#template-form .card { + padding: 12px; +} + +.template-field-card + .template-field-card { + margin-top: 12px; +} + +.template-designer-sidebar .row.template-designer-actions { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 8px; +} + +.template-save-button { + margin-bottom: 0; +} + +.template-field-head { + display: flex; + justify-content: space-between; + gap: 8px; + align-items: center; + margin-bottom: 8px; +} + +.template-text-style-row { + margin-top: 0; + align-items: flex-end; +} + +.template-text-style-row input[type="color"] { + height: 36px; + padding: 4px; +} + +@media (max-width: 720px) { + .template-designer-sidebar .row.template-designer-actions { + grid-template-columns: 1fr; + } +} + +.image-filename { + display: block; + margin-top: 8px; +} + +.region-item { + background: var(--surface); + border: 1px solid var(--border); + border-radius: 12px; + padding: 8px; + display: grid; + gap: 8px; +} + +.region-item.selected { + border-color: #f59e0b; + box-shadow: 0 0 0 1px #f59e0b inset; +} + +.region-item .row { + align-items: end; + gap: 6px; +} + +.region-item[hidden] { + display: none !important; +} diff --git a/src/webui/public/js/admin-page.js b/src/webui/public/js/admin-page.js new file mode 100644 index 0000000..69e53ac --- /dev/null +++ b/src/webui/public/js/admin-page.js @@ -0,0 +1,942 @@ +(function () { + var THEME_STORAGE_KEY = 'webui-theme'; + + function escapeHtml(value) { + return String(value ?? '') + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); + } + + function formatDashboardDate(value) { + if (!value) { + return ''; + } + var date = new Date(value); + if (Number.isNaN(date.getTime())) { + return ''; + } + return new Intl.DateTimeFormat('en-US', { + month: 'short', + day: '2-digit', + year: 'numeric', + hour: 'numeric', + minute: '2-digit', + second: '2-digit' + }).format(date); + } + + function getClientRowKey(client) { + return String(client && client.id ? client.id : client && client.clientId ? client.clientId : '').trim(); + } + + function getTableHeaderText(headerCell) { + return String(headerCell && headerCell.textContent ? headerCell.textContent : '').trim().toLowerCase(); + } + + function cellHasButtonContent(cell) { + return Boolean(cell && cell.querySelector && cell.querySelector('button, form, input, select, textarea')); + } + + function getCellSortValue(cell) { + if (!cell) { + return ''; + } + var sortValue = cell.getAttribute && cell.getAttribute('data-sort-value'); + if (sortValue !== null && sortValue !== undefined && sortValue !== '') { + return String(sortValue).trim(); + } + return String(cell.textContent || '').replace(/\s+/g, ' ').trim(); + } + + function getComparableSortValue(rawValue) { + var value = String(rawValue || '').trim(); + if (!value) { + return { type: 'empty', value: '' }; + } + + var numericValue = Number(value.replace(/,/g, '')); + if (!Number.isNaN(numericValue) && value !== '') { + return { type: 'number', value: numericValue }; + } + + var dateValue = Date.parse(value); + if (!Number.isNaN(dateValue)) { + return { type: 'date', value: dateValue }; + } + + return { type: 'string', value: value.toLowerCase() }; + } + + function compareSortValues(leftValue, rightValue) { + if (leftValue.type === 'empty' && rightValue.type === 'empty') { + return 0; + } + if (leftValue.type === 'empty') { + return 1; + } + if (rightValue.type === 'empty') { + return -1; + } + if (leftValue.type === rightValue.type) { + if (leftValue.value < rightValue.value) { + return -1; + } + if (leftValue.value > rightValue.value) { + return 1; + } + return 0; + } + return String(leftValue.value).localeCompare(String(rightValue.value), undefined, { numeric: true, sensitivity: 'base' }); + } + + function getSortableTableState(table) { + if (!table._sortableState) { + table._sortableState = { + columnIndex: null, + direction: 'asc' + }; + } + return table._sortableState; + } + + function updateSortableHeaderState(table) { + var state = getSortableTableState(table); + var headerCells = table.tHead && table.tHead.rows && table.tHead.rows.length ? Array.prototype.slice.call(table.tHead.rows[0].cells || []) : []; + headerCells.forEach(function (headerCell, index) { + if (!headerCell) { + return; + } + headerCell.classList.remove('sort-asc', 'sort-desc', 'sortable', 'unsortable'); + headerCell.removeAttribute('aria-sort'); + headerCell.removeAttribute('role'); + headerCell.removeAttribute('tabindex'); + + if (isSortableTableColumn(table, index)) { + headerCell.classList.add('sortable'); + headerCell.setAttribute('role', 'button'); + headerCell.setAttribute('tabindex', '0'); + if (state.columnIndex === index) { + headerCell.classList.add(state.direction === 'desc' ? 'sort-desc' : 'sort-asc'); + headerCell.setAttribute('aria-sort', state.direction === 'desc' ? 'descending' : 'ascending'); + } else { + headerCell.setAttribute('aria-sort', 'none'); + } + } else { + headerCell.classList.add('unsortable'); + } + }); + } + + function isSortableTableColumn(table, columnIndex) { + var headerCell = table.tHead && table.tHead.rows && table.tHead.rows.length ? table.tHead.rows[0].cells[columnIndex] : null; + if (!headerCell) { + return false; + } + + if (/actions?|buttons?/i.test(getTableHeaderText(headerCell))) { + return false; + } + + var bodies = table.tBodies ? Array.prototype.slice.call(table.tBodies) : []; + for (var i = 0; i < bodies.length; i += 1) { + var rows = Array.prototype.slice.call(bodies[i].rows || []); + for (var j = 0; j < rows.length; j += 1) { + var cell = rows[j].cells ? rows[j].cells[columnIndex] : null; + if (cell && cellHasButtonContent(cell)) { + return false; + } + } + } + + return true; + } + + function sortTable(table, columnIndex, direction) { + var tbody = table.tBodies && table.tBodies[0] ? table.tBodies[0] : null; + if (!tbody) { + return; + } + + var rows = Array.prototype.slice.call(tbody.rows || []); + if (!rows.length) { + return; + } + + var multiplier = direction === 'desc' ? -1 : 1; + rows.sort(function (leftRow, rightRow) { + var leftCell = leftRow.cells ? leftRow.cells[columnIndex] : null; + var rightCell = rightRow.cells ? rightRow.cells[columnIndex] : null; + var leftComparable = getComparableSortValue(getCellSortValue(leftCell)); + var rightComparable = getComparableSortValue(getCellSortValue(rightCell)); + return compareSortValues(leftComparable, rightComparable) * multiplier; + }); + + rows.forEach(function (row) { + tbody.appendChild(row); + }); + } + + function applyTableSort(table) { + var state = getSortableTableState(table); + if (state.columnIndex === null || state.columnIndex === undefined) { + return; + } + sortTable(table, state.columnIndex, state.direction); + updateSortableHeaderState(table); + } + + function initSortableTables() { + var tables = Array.prototype.slice.call(document.querySelectorAll('table')); + tables.forEach(function (table) { + var headerRow = table.tHead && table.tHead.rows && table.tHead.rows.length ? table.tHead.rows[0] : null; + if (!headerRow) { + return; + } + + Array.prototype.forEach.call(headerRow.cells, function (headerCell, index) { + if (!isSortableTableColumn(table, index)) { + return; + } + + headerCell.addEventListener('click', function () { + var state = getSortableTableState(table); + var nextDirection = state.columnIndex === index && state.direction === 'asc' ? 'desc' : 'asc'; + state.columnIndex = index; + state.direction = nextDirection; + sortTable(table, index, nextDirection); + updateSortableHeaderState(table); + }); + + headerCell.addEventListener('keydown', function (event) { + if (event.key === 'Enter' || event.key === ' ') { + event.preventDefault(); + headerCell.click(); + } + }); + }); + + updateSortableHeaderState(table); + }); + } + + function renderClientActionCell(client) { + var paused = Boolean(client.paused); + var pauseButtonClass = 'button-link list-action' + (paused ? ' is-paused' : ''); + var pauseButtonLabel = paused ? 'Resume' : 'Pause'; + var blackout = Boolean(client.blackout); + var blackoutButtonClass = 'button-link list-action' + (blackout ? ' is-blackout' : ''); + var blackoutButtonLabel = blackout ? 'Restore' : 'Blackout'; + var reloadConfirmMessage = 'Reloading will restart the player page. Continue?'; + var blackoutCommandValue = blackout ? '0' : '1'; + + return '
    '; + } + + function updateClientActionCell(cell, client) { + if (!cell) { + return; + } + + var pauseButton = cell.querySelector('button[data-action="pause"]'); + if (!pauseButton) { + cell.innerHTML = renderClientActionCell(client); + return; + } + + var paused = Boolean(client.paused); + pauseButton.className = 'button-link list-action' + (paused ? ' is-paused' : ''); + pauseButton.textContent = paused ? 'Resume' : 'Pause'; + + var pauseForm = pauseButton.form; + if (pauseForm) { + var commandInput = pauseForm.querySelector('input[name="command"]'); + if (commandInput) { + commandInput.value = 'pause'; + } + var connectionInput = pauseForm.querySelector('input[name="connectionId"]'); + if (connectionInput) { + connectionInput.value = client.id || ''; + } + pauseForm.action = '/admin/screens/' + encodeURIComponent(client.screen_slug) + '/commands'; + } + + var reloadButton = cell.querySelector('button[data-action="reload"]'); + if (reloadButton) { + reloadButton.textContent = 'Reload'; + reloadButton.className = 'button-link list-action is-danger'; + var reloadForm = reloadButton.form; + if (reloadForm) { + var reloadInput = reloadForm.querySelector('input[name="connectionId"]'); + if (reloadInput) { + reloadInput.value = client.id || ''; + } + reloadForm.action = '/admin/screens/' + encodeURIComponent(client.screen_slug) + '/commands'; + reloadForm.setAttribute('data-confirm-message', 'Reloading will restart the player page. Continue?'); + } + } + + var blackoutButton = cell.querySelector('button[data-action="blackout"]'); + if (!blackoutButton) { + cell.innerHTML = renderClientActionCell(client); + return; + } + + var blackout = Boolean(client.blackout); + blackoutButton.className = 'button-link list-action' + (blackout ? ' is-blackout' : ''); + blackoutButton.textContent = blackout ? 'Restore' : 'Blackout'; + + var blackoutForm = blackoutButton.form; + if (blackoutForm) { + var blackoutCommandInput = blackoutForm.querySelector('input[name="command"]'); + if (blackoutCommandInput) { + blackoutCommandInput.value = 'blackout'; + } + var blackoutStateInput = blackoutForm.querySelector('input[name="blackout"]'); + if (blackoutStateInput) { + blackoutStateInput.value = blackout ? '0' : '1'; + } + var blackoutConnectionInput = blackoutForm.querySelector('input[name="connectionId"]'); + if (blackoutConnectionInput) { + blackoutConnectionInput.value = client.id || ''; + } + blackoutForm.action = '/admin/screens/' + encodeURIComponent(client.screen_slug) + '/commands'; + } + + var previousButton = cell.querySelector('button[data-action="previous"]'); + if (!previousButton) { + cell.innerHTML = renderClientActionCell(client); + return; + } + previousButton.className = 'button-link list-action list-action--nav'; + previousButton.setAttribute('aria-label', 'Previous slide'); + var previousForm = previousButton.form; + if (previousForm) { + var previousCommandInput = previousForm.querySelector('input[name="command"]'); + if (previousCommandInput) { + previousCommandInput.value = 'previous'; + } + var previousConnectionInput = previousForm.querySelector('input[name="connectionId"]'); + if (previousConnectionInput) { + previousConnectionInput.value = client.id || ''; + } + previousForm.action = '/admin/screens/' + encodeURIComponent(client.screen_slug) + '/commands'; + } + + var nextButton = cell.querySelector('button[data-action="next"]'); + if (!nextButton) { + cell.innerHTML = renderClientActionCell(client); + return; + } + nextButton.className = 'button-link list-action list-action--nav'; + nextButton.setAttribute('aria-label', 'Next slide'); + var nextForm = nextButton.form; + if (nextForm) { + var nextCommandInput = nextForm.querySelector('input[name="command"]'); + if (nextCommandInput) { + nextCommandInput.value = 'next'; + } + var nextConnectionInput = nextForm.querySelector('input[name="connectionId"]'); + if (nextConnectionInput) { + nextConnectionInput.value = client.id || ''; + } + nextForm.action = '/admin/screens/' + encodeURIComponent(client.screen_slug) + '/commands'; + } + } + + function renderClientRow(client) { + var connectedAt = client.connectedAt ? '
    ' + escapeHtml(client.connectedAtLabel || formatDashboardDate(client.connectedAt) || client.connectedAt) + '
    ' + (client.lastSeenAt ? '
    ' + escapeHtml(client.lastSeenAtLabel || formatDashboardDate(client.lastSeenAt) || client.lastSeenAt) + '
    ' : '') : 'Unknown'; + var clientIp = client.clientIp ? escapeHtml(client.clientIp) : (client.remoteAddress ? escapeHtml(client.remoteAddress) : 'Unknown'); + var viewport = client.viewport && client.viewport.width && client.viewport.height ? escapeHtml(client.viewport.width + 'x' + client.viewport.height) : 'Unknown'; + var clientId = client.clientId ? escapeHtml(client.clientId) : 'Unknown'; + var connectionId = client.id ? escapeHtml(client.id) : 'Unknown'; + var currentSlide = client.currentSlideTitle ? escapeHtml(client.currentSlideTitle) : 'No slide currently showing'; + + return [ + '', + '
    ' + clientId + '
    ' + connectionId + '
    ', + '' + clientIp + '', + '' + viewport + '', + '' + connectedAt + '', + '
    ' + escapeHtml(client.screen_name) + '
    Showing: ' + currentSlide + '
    ', + '' + renderClientActionCell(client) + '', + '' + ].join(''); + } + + function renderScreenRow(screen) { + var clientCount = Number(screen.player_connection_count || 0); + return [ + '', + '' + escapeHtml(screen.name) + '', + '' + escapeHtml(screen.player_url || '') + '', + '' + escapeHtml(screen.playlist_name || '') + '', + '' + (clientCount ? '
    ' + clientCount + ' connected
    ' : 'No clients connected.') + '', + '' + ].join(''); + } + + function updateStats(state) { + var clientCount = document.getElementById('dashboard-client-count'); + var screenCount = document.getElementById('dashboard-screen-count'); + var slideCount = document.getElementById('dashboard-slide-count'); + var playlistCount = document.getElementById('dashboard-playlist-count'); + + if (playlistCount && Array.isArray(state.playlists)) { + playlistCount.textContent = String(state.playlists.length); + } + if (slideCount && Array.isArray(state.slides)) { + slideCount.textContent = String(state.slides.length); + } + if (screenCount && Array.isArray(state.screens)) { + screenCount.textContent = String(state.screens.length); + } + if (clientCount) { + clientCount.textContent = String(Number(state.connectedClientsCount || 0)); + } + } + + function updateClientTable(state) { + var tbody = document.getElementById('dashboard-clients-table-body'); + if (!tbody || !Array.isArray(state.clients)) { + return; + } + if (!state.clients.length) { + tbody.innerHTML = 'No connected clients yet.'; + return; + } + var existingRows = {}; + Array.prototype.slice.call(tbody.querySelectorAll('tr[data-client-key]')).forEach(function (row) { + existingRows[row.getAttribute('data-client-key')] = row; + }); + + Array.prototype.slice.call(tbody.querySelectorAll('tr')).forEach(function (row) { + if (!row.hasAttribute('data-client-key')) { + row.parentNode.removeChild(row); + } + }); + + state.clients.forEach(function (client, index) { + var rowKey = getClientRowKey(client); + var row = existingRows[rowKey]; + if (!row) { + var tempBody = document.createElement('tbody'); + tempBody.innerHTML = renderClientRow(client); + row = tempBody.firstElementChild; + } + + if (!row) { + return; + } + + row.setAttribute('data-client-key', rowKey); + if (row.cells && row.cells.length >= 6) { + var connectedAt = client.connectedAt ? '
    ' + escapeHtml(client.connectedAtLabel || formatDashboardDate(client.connectedAt) || client.connectedAt) + '
    ' + (client.lastSeenAt ? '
    ' + escapeHtml(client.lastSeenAtLabel || formatDashboardDate(client.lastSeenAt) || client.lastSeenAt) + '
    ' : '') : 'Unknown'; + var clientIp = client.clientIp ? escapeHtml(client.clientIp) : (client.remoteAddress ? escapeHtml(client.remoteAddress) : 'Unknown'); + var viewport = client.viewport && client.viewport.width && client.viewport.height ? escapeHtml(client.viewport.width + 'x' + client.viewport.height) : 'Unknown'; + var clientId = client.clientId ? escapeHtml(client.clientId) : 'Unknown'; + var connectionId = client.id ? escapeHtml(client.id) : 'Unknown'; + var currentSlide = client.currentSlideTitle ? escapeHtml(client.currentSlideTitle) : 'No slide currently showing'; + + row.cells[0].innerHTML = '
    ' + clientId + '
    ' + connectionId + '
    '; + row.cells[1].innerHTML = clientIp; + row.cells[2].innerHTML = viewport; + row.cells[3].innerHTML = connectedAt; + row.cells[4].innerHTML = '
    ' + escapeHtml(client.screen_name) + '
    Showing: ' + currentSlide + '
    '; + updateClientActionCell(row.cells[5], client); + } + + var referenceNode = tbody.children[index] || null; + if (referenceNode !== row) { + tbody.insertBefore(row, referenceNode); + } + }); + + while (tbody.children.length > state.clients.length) { + tbody.removeChild(tbody.lastElementChild); + } + + applyTableSort(document.getElementById('dashboard-clients-table')); + } + + function updateScreenTable(state) { + var table = document.getElementById('dashboard-screens-table'); + if (!table || !Array.isArray(state.screens)) { + return; + } + var tbody = table.tBodies && table.tBodies[0] ? table.tBodies[0] : null; + if (!tbody) { + return; + } + if (!state.screens.length) { + tbody.innerHTML = 'No screens yet.'; + return; + } + tbody.innerHTML = state.screens.map(renderScreenRow).join(''); + applyTableSort(table); + } + + function updateDashboardQuickActions(state) { + var blackoutButton = document.getElementById('dashboard-blackout-all-button'); + if (!blackoutButton || !state || !Array.isArray(state.clients)) { + return; + } + + var hasClients = state.clients.length > 0; + var allBlackout = hasClients && state.clients.every(function (client) { + return Boolean(client && client.blackout); + }); + var label = allBlackout ? 'Restore all clients' : 'Blackout all clients'; + var blackoutForm = blackoutButton.form; + var blackoutInput = blackoutForm ? blackoutForm.querySelector('input[name="blackout"]') : null; + + blackoutButton.textContent = label; + blackoutButton.className = 'button-link list-action' + (allBlackout ? ' is-blackout' : ''); + if (blackoutInput) { + blackoutInput.value = allBlackout ? '0' : '1'; + } + if (blackoutForm) { + blackoutForm.setAttribute('data-confirm-message', allBlackout ? 'Restore all connected clients?' : 'Blackout all connected clients?'); + } + blackoutButton.setAttribute('aria-label', label); + } + + function handleDashboardState(state) { + if (!state) { + return; + } + updateStats(state); + updateScreenTable(state); + updateClientTable(state); + updateDashboardQuickActions(state); + updateSidebarStatus(Boolean(state.playerServiceConnected), 'Live player feed'); + } + + function updateSidebarStatus(status, label) { + var dot = document.getElementById('sidebar-status-dot'); + var text = document.getElementById('sidebar-status-text'); + var pill = document.getElementById('sidebar-status-pill'); + if (!dot && !text && !pill) { + return; + } + + var normalizedStatus = status === 'online' || status === 'offline' || status === 'unknown' + ? status + : (status ? 'online' : 'offline'); + var statusLabel = normalizedStatus === 'online' + ? (label || 'Connected to player feed') + : normalizedStatus === 'offline' + ? 'Disconnected from player feed' + : (label || 'Connecting to player feed'); + var pillLabel = normalizedStatus === 'online' ? 'Connected' : normalizedStatus === 'offline' ? 'Disconnected' : 'Connecting'; + + if (dot) { + dot.classList.remove('status-dot--unknown', 'status-dot--online', 'status-dot--offline'); + dot.classList.add(normalizedStatus === 'online' ? 'status-dot--online' : normalizedStatus === 'offline' ? 'status-dot--offline' : 'status-dot--unknown'); + dot.setAttribute('aria-label', statusLabel); + dot.setAttribute('title', statusLabel); + } + + if (text) { + text.textContent = statusLabel; + } + + if (pill) { + pill.classList.remove('status-pill--unknown', 'status-pill--online', 'status-pill--offline'); + pill.classList.add(normalizedStatus === 'online' ? 'status-pill--online' : normalizedStatus === 'offline' ? 'status-pill--offline' : 'status-pill--unknown'); + pill.textContent = pillLabel; + } + } + + function connectDashboardSocket() { + if (!window.WebSocket) { + return; + } + + var socket = null; + var reconnectTimer = null; + + function scheduleReconnect() { + if (reconnectTimer) { + return; + } + reconnectTimer = window.setTimeout(function () { + reconnectTimer = null; + connect(); + }, 5000); + } + + function connect() { + var protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; + socket = new WebSocket(protocol + '//' + window.location.host + '/ws/admin/dashboard'); + updateSidebarStatus('unknown', 'Connecting to player feed'); + + socket.onmessage = function (event) { + try { + var payload = JSON.parse(String(event.data || '{}')); + if (payload && payload.type === 'dashboard-state') { + handleDashboardState(payload.state); + } + } catch (_error) { + // Ignore malformed dashboard payloads. + } + }; + + socket.onclose = function () { + updateSidebarStatus('offline'); + scheduleReconnect(); + }; + + socket.onerror = function () { + updateSidebarStatus('offline'); + try { + socket.close(); + } catch (_error) { + // ignore close errors + } + }; + + socket.onopen = function () { + updateSidebarStatus('unknown', 'Connecting to player feed'); + }; + } + + connect(); + } + + function dismissToast(toast) { + toast.classList.add('toast-hide'); + window.setTimeout(function () { + if (toast && toast.parentNode) { + toast.parentNode.removeChild(toast); + } + }, 220); + } + + function showToast(message) { + var text = String(message || '').trim(); + if (!text) { + return; + } + + var existingToast = document.getElementById('app-toast'); + var existingBody = existingToast ? existingToast.querySelector('.toast-body') : null; + if (existingToast && existingBody) { + existingBody.textContent = text; + existingToast.classList.remove('toast-hide'); + window.clearTimeout(existingToast._dismissTimer); + existingToast._dismissTimer = window.setTimeout(function () { + dismissToast(existingToast); + }, 4000); + return; + } + + var toast = document.createElement('div'); + toast.className = 'toast'; + toast.setAttribute('role', 'status'); + toast.setAttribute('aria-live', 'polite'); + toast.innerHTML = '
    '; + toast.querySelector('.toast-body').textContent = text; + + var closeButton = toast.querySelector('.toast-close'); + closeButton.addEventListener('click', function () { + dismissToast(toast); + }); + + document.body.appendChild(toast); + toast._dismissTimer = window.setTimeout(function () { + dismissToast(toast); + }, 4000); + } + + function initToast() { + var toast = document.getElementById('app-toast'); + var closeButton = document.getElementById('app-toast-close'); + if (!toast || !closeButton) { + return; + } + + try { + var url = new URL(window.location.href); + if (url.searchParams.has('message')) { + url.searchParams.delete('message'); + window.history.replaceState({}, document.title, url.pathname + url.search + url.hash); + } + } catch (_error) { + // ignore URL cleanup failures + } + + closeButton.addEventListener('click', function () { + dismissToast(toast); + }); + + window.setTimeout(function () { + dismissToast(toast); + }, 4000); + } + + function initConfirmForms() { + document.addEventListener('submit', function (event) { + var form = event.target; + if (!form || !form.getAttribute) { + return; + } + if (form.hasAttribute && form.hasAttribute('data-async-command')) { + return; + } + var message = form.getAttribute('data-confirm-message'); + if (message && !window.confirm(message)) { + event.preventDefault(); + } + }); + } + + function initAsyncCommandForms() { + document.addEventListener('submit', function (event) { + var form = event.target; + if (!form || !form.hasAttribute || !form.hasAttribute('data-async-command')) { + return; + } + + if (form.dataset && form.dataset.busy === 'true') { + event.preventDefault(); + return; + } + + var message = form.getAttribute('data-confirm-message'); + if (message && !window.confirm(message)) { + event.preventDefault(); + return; + } + + event.preventDefault(); + + form.dataset.busy = 'true'; + + var formData = new FormData(form); + var body = new URLSearchParams(); + formData.forEach(function (value, key) { + body.append(key, value); + }); + + fetch(form.action, { + method: (form.method || 'POST').toUpperCase(), + headers: { + 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8', + 'X-Requested-With': 'XMLHttpRequest', + 'Accept': 'application/json, text/plain, */*' + }, + body: body.toString(), + credentials: 'same-origin' + }).finally(function () { + delete form.dataset.busy; + }); + }, true); + } + + function initAsyncSaveForms() { + document.addEventListener('submit', function (event) { + var form = event.target; + if (!form || !form.hasAttribute || !form.hasAttribute('data-async-save')) { + return; + } + + if (form.dataset && form.dataset.busy === 'true') { + event.preventDefault(); + return; + } + + var message = form.getAttribute('data-confirm-message'); + if (message && !window.confirm(message)) { + event.preventDefault(); + return; + } + + event.preventDefault(); + form.dataset.busy = 'true'; + + var formData = new FormData(form); + var hasFileValue = false; + formData.forEach(function (value) { + if (value && typeof value === 'object' && typeof value.name === 'string') { + hasFileValue = true; + } + }); + + var isMultipart = hasFileValue || String(form.enctype || '').toLowerCase() === 'multipart/form-data'; + var body = isMultipart ? formData : new URLSearchParams(); + + if (!isMultipart) { + formData.forEach(function (value, key) { + body.append(key, value); + }); + } + + fetch(form.action, { + method: (form.method || 'POST').toUpperCase(), + headers: Object.assign({ + 'X-Requested-With': 'XMLHttpRequest', + 'Accept': 'text/html, application/json, text/plain, */*' + }, isMultipart ? {} : { + 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8' + }), + body: isMultipart ? body : body.toString(), + credentials: 'same-origin' + }).then(function (response) { + if (!response.ok) { + return response.text().then(function (text) { + throw new Error(text || 'Unable to save changes.'); + }); + } + return response.text().then(function (text) { + var savedMessage = ''; + try { + var doc = new DOMParser().parseFromString(text || '', 'text/html'); + var toastBody = doc.querySelector('.toast-body'); + if (toastBody && toastBody.textContent) { + savedMessage = toastBody.textContent.trim(); + } + } catch (_error) { + savedMessage = ''; + } + showToast(savedMessage || 'Saved.'); + }); + }).catch(function (error) { + window.alert(error.message || 'Unable to save changes.'); + }).finally(function () { + delete form.dataset.busy; + }); + }, true); + } + + function initSubmitOnChange() { + var fields = document.querySelectorAll('[data-submit-on-change]'); + Array.prototype.forEach.call(fields, function (field) { + field.addEventListener('change', function () { + var form = field.form || field.closest('form'); + if (form) { + form.submit(); + } + }); + }); + } + + function initSidebarToggle() { + var body = document.body; + var toggleButtons = Array.prototype.slice.call(document.querySelectorAll('[data-sidebar-toggle]')); + var backdrop = document.querySelector('[data-sidebar-backdrop]'); + + if (!toggleButtons.length || !backdrop) { + return; + } + + function setSidebarOpen(isOpen) { + body.classList.toggle('is-sidebar-open', isOpen); + Array.prototype.forEach.call(toggleButtons, function (toggleButton) { + toggleButton.setAttribute('aria-expanded', isOpen ? 'true' : 'false'); + }); + } + + function toggleSidebar() { + setSidebarOpen(!body.classList.contains('is-sidebar-open')); + } + + Array.prototype.forEach.call(toggleButtons, function (toggleButton) { + toggleButton.addEventListener('click', function () { + toggleSidebar(); + }); + }); + + backdrop.addEventListener('click', function () { + setSidebarOpen(false); + }); + + document.addEventListener('keydown', function (event) { + if (event.key === 'Escape') { + setSidebarOpen(false); + } + }); + } + + function getPreferredTheme() { + if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) { + return 'dark'; + } + return 'light'; + } + + function getStoredTheme() { + try { + var storedTheme = window.localStorage.getItem(THEME_STORAGE_KEY); + if (storedTheme === 'dark' || storedTheme === 'light') { + return storedTheme; + } + } catch (error) { + return null; + } + return null; + } + + function setStoredTheme(theme) { + try { + window.localStorage.setItem(THEME_STORAGE_KEY, theme); + } catch (error) { + return; + } + } + + function applyTheme(theme) { + var normalizedTheme = theme === 'dark' ? 'dark' : 'light'; + var nextTheme = normalizedTheme === 'dark' ? 'light' : 'dark'; + var nextThemeLabel = nextTheme === 'dark' ? 'Dark mode' : 'Light mode'; + + document.documentElement.dataset.theme = normalizedTheme; + document.documentElement.style.colorScheme = normalizedTheme; + + Array.prototype.forEach.call(document.querySelectorAll('[data-theme-toggle]'), function (toggleButton) { + var icon = toggleButton.querySelector('.theme-toggle__icon'); + toggleButton.setAttribute('aria-pressed', normalizedTheme === 'dark' ? 'true' : 'false'); + toggleButton.setAttribute('aria-label', 'Switch to ' + nextThemeLabel.toLowerCase()); + if (icon) { + icon.classList.remove('theme-toggle__icon--moon', 'theme-toggle__icon--sun'); + icon.classList.add(normalizedTheme === 'dark' ? 'theme-toggle__icon--sun' : 'theme-toggle__icon--moon'); + } + }); + + return normalizedTheme; + } + + function initThemeToggle() { + var toggleButtons = Array.prototype.slice.call(document.querySelectorAll('[data-theme-toggle]')); + var storedTheme = getStoredTheme(); + var theme = storedTheme || getPreferredTheme(); + + applyTheme(theme); + + if (!toggleButtons.length) { + return; + } + + Array.prototype.forEach.call(toggleButtons, function (toggleButton) { + toggleButton.addEventListener('click', function () { + var nextTheme = document.documentElement.dataset.theme === 'dark' ? 'light' : 'dark'; + setStoredTheme(nextTheme); + applyTheme(nextTheme); + }); + }); + } + + initToast(); + initConfirmForms(); + initAsyncCommandForms(); + initAsyncSaveForms(); + initSubmitOnChange(); + initThemeToggle(); + initSidebarToggle(); + initSortableTables(); + if (document.getElementById('sidebar-status-pill') || document.getElementById('sidebar-status-dot') || document.getElementById('sidebar-status-text')) { + connectDashboardSocket(); + } +}()); diff --git a/src/webui/public/js/compact/editor.header.min.js b/src/webui/public/js/compact/editor.header.min.js new file mode 100644 index 0000000..3565dfe --- /dev/null +++ b/src/webui/public/js/compact/editor.header.min.js @@ -0,0 +1,15 @@ +/** + * Skipped minification because the original files appears to be already minified. + * Original file: /npm/@editorjs/header@2.8.9/dist/header.umd.js + * + * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files + */ +(function(){"use strict";try{if(typeof document<"u"){var e=document.createElement("style");e.appendChild(document.createTextNode(".ce-header{padding:.6em 0 3px;margin:0;line-height:1.25em;outline:none}.ce-header p,.ce-header div{padding:0!important;margin:0!important}")),document.head.appendChild(e)}}catch(n){console.error("vite-plugin-css-injected-by-js",n)}})(); +(function(o,s){typeof exports=="object"&&typeof module<"u"?module.exports=s():typeof define=="function"&&define.amd?define(s):(o=typeof globalThis<"u"?globalThis:o||self,o.Header=s())})(this,function(){"use strict";const o="",s='',d='',u='',c='',g='',v='',w='';/** + * Header block for the Editor.js. + * + * @author CodeX (team@ifmo.su) + * @copyright CodeX 2018 + * @license MIT + * @version 2.0.0 + */class p{constructor({data:e,config:t,api:i,readOnly:r}){this.api=i,this.readOnly=r,this._config=t??null,this._data=this.normalizeData(e),this._element=this.getTag()}get _CSS(){return{block:this.api.styles.block,wrapper:"ce-header"}}isHeaderData(e){return e.text!==void 0}normalizeData(e){const t={text:"",level:this.defaultLevel.number};return this.isHeaderData(e)&&(t.text=e.text||"",e.level!==void 0&&!isNaN(parseInt(e.level.toString()))&&(t.level=parseInt(e.level.toString()))),t}render(){return this._element}renderSettings(){return this.levels.map(e=>({icon:e.svg,label:this.api.i18n.t(`Heading ${e.number}`),onActivate:()=>this.setLevel(e.number),closeOnActivate:!0,isActive:this.currentLevel.number===e.number,render:()=>document.createElement("div")}))}setLevel(e){this.data={level:e,text:this.data.text}}merge(e){this._element.insertAdjacentHTML("beforeend",e.text)}validate(e){return e.text.trim()!==""}save(e){return{text:e.innerHTML,level:this.currentLevel.number}}static get conversionConfig(){return{export:"text",import:"text"}}static get sanitize(){return{level:!1,text:{}}}static get isReadOnlySupported(){return!0}get data(){return this._data.text=this._element.innerHTML,this._data.level=this.currentLevel.number,this._data}set data(e){if(this._data=this.normalizeData(e),e.level!==void 0&&this._element.parentNode){const t=this.getTag();t.innerHTML=this._element.innerHTML,this._element.parentNode.replaceChild(t,this._element),this._element=t}e.text!==void 0&&(this._element.innerHTML=this._data.text||"")}getTag(){var t;const e=document.createElement(this.currentLevel.tag);return e.innerHTML=this._data.text||"",e.classList.add(this._CSS.wrapper),e.contentEditable=this.readOnly?"false":"true",e.dataset.placeholder=this.api.i18n.t(((t=this._config)==null?void 0:t.placeholder)||""),e}get currentLevel(){let e=this.levels.find(t=>t.number===this._data.level);return e||(e=this.defaultLevel),e}get defaultLevel(){var e;if((e=this._config)!=null&&e.defaultLevel){const t=this.levels.find(i=>{var r;return i.number===((r=this._config)==null?void 0:r.defaultLevel)});if(t)return t;console.warn("(ง'̀-'́)ง Heading Tool: the default level specified was not found in available levels")}return this.levels[1]}get levels(){var t;const e=[{number:1,tag:"H1",svg:s},{number:2,tag:"H2",svg:d},{number:3,tag:"H3",svg:u},{number:4,tag:"H4",svg:c},{number:5,tag:"H5",svg:g},{number:6,tag:"H6",svg:v}];return(t=this._config)!=null&&t.levels?e.filter(i=>{var r;return(r=this._config)==null?void 0:r.levels.includes(i.number)}):e}onPaste(e){var i,r;const t=e.detail;if("data"in t){const l=t.data;let n=this.defaultLevel.number;switch(l.tagName){case"H1":n=1;break;case"H2":n=2;break;case"H3":n=3;break;case"H4":n=4;break;case"H5":n=5;break;case"H6":n=6;break}(i=this._config)!=null&&i.levels&&(n=(r=this._config)==null?void 0:r.levels.reduce((a,h)=>Math.abs(h-n)window.navigator.appVersion.toLowerCase().indexOf(n)!==-1);return t!==void 0&&(e[t]=!0),e}function re(e){return e!=null&&e!==""&&(typeof e!="object"||Object.keys(e).length>0)}function wt(e){return!re(e)}const Pt=()=>typeof window<"u"&&window.navigator!==null&&re(window.navigator.platform)&&(/iP(ad|hone|od)/.test(window.navigator.platform)||window.navigator.platform==="MacIntel"&&window.navigator.maxTouchPoints>1);function jt(e){const t=Re();return e=e.replace(/shift/gi,"⇧").replace(/backspace/gi,"⌫").replace(/enter/gi,"⏎").replace(/up/gi,"↑").replace(/left/gi,"→").replace(/down/gi,"↓").replace(/right/gi,"←").replace(/escape/gi,"⎋").replace(/insert/gi,"Ins").replace(/delete/gi,"␡").replace(/\+/gi,"+"),t.mac?e=e.replace(/ctrl|cmd/gi,"⌘").replace(/alt/gi,"⌥"):e=e.replace(/cmd/gi,"Ctrl").replace(/windows/gi,"WIN"),e}function Tt(e){return e[0].toUpperCase()+e.slice(1)}function Lt(e){const t=document.createElement("div");t.style.position="absolute",t.style.left="-999px",t.style.bottom="-999px",t.innerHTML=e,document.body.appendChild(t);const n=window.getSelection(),r=document.createRange();if(r.selectNode(t),n===null)throw new Error("Cannot copy text to clipboard");n.removeAllRanges(),n.addRange(r),document.execCommand("copy"),document.body.removeChild(t)}function Mt(e,t,n){let r;return(...i)=>{const a=this,l=()=>{r=void 0,n!==!0&&e.apply(a,i)},s=n===!0&&r!==void 0;window.clearTimeout(r),r=window.setTimeout(l,t),s&&e.apply(a,i)}}function C(e){return Object.prototype.toString.call(e).match(/\s([a-zA-Z]+)/)[1].toLowerCase()}function Nt(e){return C(e)==="boolean"}function qe(e){return C(e)==="function"||C(e)==="asyncfunction"}function At(e){return qe(e)&&/^\s*class\s+/.test(e.toString())}function $t(e){return C(e)==="number"}function A(e){return C(e)==="object"}function Bt(e){return Promise.resolve(e)===e}function Wt(e){return C(e)==="string"}function Dt(e){return C(e)==="undefined"}function ie(e,...t){if(!t.length)return e;const n=t.shift();if(A(e)&&A(n))for(const r in n)A(n[r])?(e[r]===void 0&&Object.assign(e,{[r]:{}}),ie(e[r],n[r])):Object.assign(e,{[r]:n[r]});return ie(e,...t)}function Ht(e,t,n){const r=`«${t}» is deprecated and will be removed in the next major release. Please use the «${n}» instead.`;e&&console.warn(r)}function Ft(e){try{return new URL(e).href}catch{}return e.substring(0,2)==="//"?window.location.protocol+e:window.location.origin+e}function Rt(e){return e>47&&e<58||e===32||e===13||e===229||e>64&&e<91||e>95&&e<112||e>185&&e<193||e>218&&e<223}const qt={BACKSPACE:8,TAB:9,ENTER:13,SHIFT:16,CTRL:17,ALT:18,ESC:27,SPACE:32,LEFT:37,UP:38,DOWN:40,RIGHT:39,DELETE:46,META:91,SLASH:191},Ut={LEFT:0,WHEEL:1,RIGHT:2,BACKWARD:3,FORWARD:4};class Kt{constructor(){this.completed=Promise.resolve()}add(t){return new Promise((n,r)=>{this.completed=this.completed.then(t).then(n).catch(r)})}}function zt(e,t,n=void 0){let r,i,a,l=null,s=0;n||(n={});const o=function(){s=n.leading===!1?0:Date.now(),l=null,a=e.apply(r,i),l===null&&(r=i=null)};return function(){const d=Date.now();!s&&n.leading===!1&&(s=d);const u=t-(d-s);return r=this,i=arguments,u<=0||u>t?(l&&(clearTimeout(l),l=null),s=d,a=e.apply(r,i),l===null&&(r=i=null)):!l&&n.trailing!==!1&&(l=setTimeout(o,u)),a}}const ae=gt(Object.freeze(Object.defineProperty({__proto__:null,PromiseQueue:Kt,beautifyShortcut:jt,cacheable:It,capitalize:Tt,copyTextToClipboard:Lt,debounce:Mt,deepMerge:ie,deprecationAssert:Ht,getUserOS:Re,getValidUrl:Ft,isBoolean:Nt,isClass:At,isEmpty:wt,isFunction:qe,isIosDevice:Pt,isNumber:$t,isObject:A,isPrintableKey:Rt,isPromise:Bt,isString:Wt,isUndefined:Dt,keyCodes:qt,mouseButtons:Ut,notEmpty:re,throttle:zt,typeOf:C},Symbol.toStringTag,{value:"Module"})));Object.defineProperty(ne,"__esModule",{value:!0}),ne.containsOnlyInlineElements=Vt;var Xt=ae,Gt=J;function Vt(e){var t;(0,Xt.isString)(e)?(t=document.createElement("div"),t.innerHTML=e):t=e;var n=function(r){return!(0,Gt.blockElements)().includes(r.tagName.toLowerCase())&&Array.from(r.children).every(n)};return Array.from(t.children).every(n)}(function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.containsOnlyInlineElements=void 0;var t=ne;Object.defineProperty(e,"containsOnlyInlineElements",{enumerable:!0,get:function(){return t.containsOnlyInlineElements}})})(N);var Ue={},le={},$={},se={};Object.defineProperty(se,"__esModule",{value:!0}),se.make=Yt;function Yt(e,t,n){var r;t===void 0&&(t=null),n===void 0&&(n={});var i=document.createElement(e);if(Array.isArray(t)){var a=t.filter(function(s){return s!==void 0});(r=i.classList).add.apply(r,a)}else t!==null&&i.classList.add(t);for(var l in n)Object.prototype.hasOwnProperty.call(n,l)&&(i[l]=n[l]);return i}(function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.make=void 0;var t=se;Object.defineProperty(e,"make",{enumerable:!0,get:function(){return t.make}})})($),Object.defineProperty(le,"__esModule",{value:!0}),le.fragmentToString=Qt;var Jt=$;function Qt(e){var t=(0,Jt.make)("div");return t.appendChild(e),t.innerHTML}(function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.fragmentToString=void 0;var t=le;Object.defineProperty(e,"fragmentToString",{enumerable:!0,get:function(){return t.fragmentToString}})})(Ue);var Ke={},oe={};Object.defineProperty(oe,"__esModule",{value:!0}),oe.getContentLength=xt;var Zt=O;function xt(e){var t,n;return(0,Zt.isNativeInput)(e)?e.value.length:e.nodeType===Node.TEXT_NODE?e.length:(n=(t=e.textContent)===null||t===void 0?void 0:t.length)!==null&&n!==void 0?n:0}(function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.getContentLength=void 0;var t=oe;Object.defineProperty(e,"getContentLength",{enumerable:!0,get:function(){return t.getContentLength}})})(Ke);var ue={},ce={},ze=M&&M.__spreadArray||function(e,t,n){if(n||arguments.length===2)for(var r=0,i=t.length,a;r0;){var r=n.shift();if(r){if(e=r,(0,Sn.isLeaf)(e)&&!(0,On.isNodeEmpty)(e,t))return!1;n.push.apply(n,Array.from(e.childNodes))}}return!0}(function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.isEmpty=void 0;var t=be;Object.defineProperty(e,"isEmpty",{enumerable:!0,get:function(){return t.isEmpty}})})(Qe);var Ze={},ke={};Object.defineProperty(ke,"__esModule",{value:!0}),ke.isFragment=En;var _n=ae;function En(e){return(0,_n.isNumber)(e)?!1:!!e&&!!e.nodeType&&e.nodeType===Node.DOCUMENT_FRAGMENT_NODE}(function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.isFragment=void 0;var t=ke;Object.defineProperty(e,"isFragment",{enumerable:!0,get:function(){return t.isFragment}})})(Ze);var xe={},_e={};Object.defineProperty(_e,"__esModule",{value:!0}),_e.isHTMLString=wn;var In=$;function wn(e){var t=(0,In.make)("div");return t.innerHTML=e,t.childElementCount>0}(function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.isHTMLString=void 0;var t=_e;Object.defineProperty(e,"isHTMLString",{enumerable:!0,get:function(){return t.isHTMLString}})})(xe);var et={},Ee={};Object.defineProperty(Ee,"__esModule",{value:!0}),Ee.offset=Pn;function Pn(e){var t=e.getBoundingClientRect(),n=window.pageXOffset||document.documentElement.scrollLeft,r=window.pageYOffset||document.documentElement.scrollTop,i=t.top+r,a=t.left+n;return{top:i,left:a,bottom:i+t.height,right:a+t.width}}(function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.offset=void 0;var t=Ee;Object.defineProperty(e,"offset",{enumerable:!0,get:function(){return t.offset}})})(et);var tt={},Ie={};Object.defineProperty(Ie,"__esModule",{value:!0}),Ie.prepend=jn;function jn(e,t){Array.isArray(t)?(t=t.reverse(),t.forEach(function(n){return e.prepend(n)})):e.prepend(t)}(function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.prepend=void 0;var t=Ie;Object.defineProperty(e,"prepend",{enumerable:!0,get:function(){return t.prepend}})})(tt),function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.prepend=e.offset=e.make=e.isLineBreakTag=e.isSingleTag=e.isNodeEmpty=e.isLeaf=e.isHTMLString=e.isFragment=e.isEmpty=e.isElement=e.isContentEditable=e.isCollapsedWhitespaces=e.findAllInputs=e.isNativeInput=e.allInputsSelector=e.getDeepestNode=e.getDeepestBlockElements=e.getContentLength=e.fragmentToString=e.containsOnlyInlineElements=e.canSetCaret=e.calculateBaseline=e.blockElements=e.append=void 0;var t=X;Object.defineProperty(e,"allInputsSelector",{enumerable:!0,get:function(){return t.allInputsSelector}});var n=O;Object.defineProperty(e,"isNativeInput",{enumerable:!0,get:function(){return n.isNativeInput}});var r=De;Object.defineProperty(e,"append",{enumerable:!0,get:function(){return r.append}});var i=J;Object.defineProperty(e,"blockElements",{enumerable:!0,get:function(){return i.blockElements}});var a=He;Object.defineProperty(e,"calculateBaseline",{enumerable:!0,get:function(){return a.calculateBaseline}});var l=Fe;Object.defineProperty(e,"canSetCaret",{enumerable:!0,get:function(){return l.canSetCaret}});var s=N;Object.defineProperty(e,"containsOnlyInlineElements",{enumerable:!0,get:function(){return s.containsOnlyInlineElements}});var o=Ue;Object.defineProperty(e,"fragmentToString",{enumerable:!0,get:function(){return o.fragmentToString}});var d=Ke;Object.defineProperty(e,"getContentLength",{enumerable:!0,get:function(){return d.getContentLength}});var u=ue;Object.defineProperty(e,"getDeepestBlockElements",{enumerable:!0,get:function(){return u.getDeepestBlockElements}});var h=Ge;Object.defineProperty(e,"getDeepestNode",{enumerable:!0,get:function(){return h.getDeepestNode}});var g=Ye;Object.defineProperty(e,"findAllInputs",{enumerable:!0,get:function(){return g.findAllInputs}});var L=Je;Object.defineProperty(e,"isCollapsedWhitespaces",{enumerable:!0,get:function(){return L.isCollapsedWhitespaces}});var w=ee;Object.defineProperty(e,"isContentEditable",{enumerable:!0,get:function(){return w.isContentEditable}});var nr=ge;Object.defineProperty(e,"isElement",{enumerable:!0,get:function(){return nr.isElement}});var rr=Qe;Object.defineProperty(e,"isEmpty",{enumerable:!0,get:function(){return rr.isEmpty}});var ir=Ze;Object.defineProperty(e,"isFragment",{enumerable:!0,get:function(){return ir.isFragment}});var ar=xe;Object.defineProperty(e,"isHTMLString",{enumerable:!0,get:function(){return ar.isHTMLString}});var lr=ye;Object.defineProperty(e,"isLeaf",{enumerable:!0,get:function(){return lr.isLeaf}});var sr=Se;Object.defineProperty(e,"isNodeEmpty",{enumerable:!0,get:function(){return sr.isNodeEmpty}});var or=B;Object.defineProperty(e,"isLineBreakTag",{enumerable:!0,get:function(){return or.isLineBreakTag}});var ur=W;Object.defineProperty(e,"isSingleTag",{enumerable:!0,get:function(){return ur.isSingleTag}});var cr=$;Object.defineProperty(e,"make",{enumerable:!0,get:function(){return cr.make}});var dr=et;Object.defineProperty(e,"offset",{enumerable:!0,get:function(){return dr.offset}});var fr=tt;Object.defineProperty(e,"prepend",{enumerable:!0,get:function(){return fr.prepend}})}(c);const m="cdx-list",p={wrapper:m,item:`${m}__item`,itemContent:`${m}__item-content`,itemChildren:`${m}__item-children`};class v{static get CSS(){return{...p,orderedList:`${m}-ordered`}}constructor(t,n){this.config=n,this.readOnly=t}renderWrapper(t){let n;return t===!0?n=c.make("ol",[v.CSS.wrapper,v.CSS.orderedList]):n=c.make("ol",[v.CSS.orderedList,v.CSS.itemChildren]),n}renderItem(t,n){const r=c.make("li",v.CSS.item),i=c.make("div",v.CSS.itemContent,{innerHTML:t,contentEditable:(!this.readOnly).toString()});return r.appendChild(i),r}getItemContent(t){const n=t.querySelector(`.${v.CSS.itemContent}`);return!n||c.isEmpty(n)?"":n.innerHTML}getItemMeta(){return{}}composeDefaultMeta(){return{}}}class b{static get CSS(){return{...p,unorderedList:`${m}-unordered`}}constructor(t,n){this.config=n,this.readOnly=t}renderWrapper(t){let n;return t===!0?n=c.make("ul",[b.CSS.wrapper,b.CSS.unorderedList]):n=c.make("ul",[b.CSS.unorderedList,b.CSS.itemChildren]),n}renderItem(t,n){const r=c.make("li",b.CSS.item),i=c.make("div",b.CSS.itemContent,{innerHTML:t,contentEditable:(!this.readOnly).toString()});return r.appendChild(i),r}getItemContent(t){const n=t.querySelector(`.${b.CSS.itemContent}`);return!n||c.isEmpty(n)?"":n.innerHTML}getItemMeta(){return{}}composeDefaultMeta(){return{}}}function k(e){return e.nodeType===Node.ELEMENT_NODE}var j={},we={},H={},F={};Object.defineProperty(F,"__esModule",{value:!0}),F.getContenteditableSlice=Ln;var Tn=c;function Ln(e,t,n,r,i){var a;i===void 0&&(i=!1);var l=document.createRange();if(r==="left"?(l.setStart(e,0),l.setEnd(t,n)):(l.setStart(t,n),l.setEnd(e,e.childNodes.length)),i===!0){var s=l.extractContents();return(0,Tn.fragmentToString)(s)}var o=l.cloneContents(),d=document.createElement("div");d.appendChild(o);var u=(a=d.textContent)!==null&&a!==void 0?a:"";return u}Object.defineProperty(H,"__esModule",{value:!0}),H.checkContenteditableSliceForEmptiness=An;var Mn=c,Nn=F;function An(e,t,n,r){var i=(0,Nn.getContenteditableSlice)(e,t,n,r);return(0,Mn.isCollapsedWhitespaces)(i)}(function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.checkContenteditableSliceForEmptiness=void 0;var t=H;Object.defineProperty(e,"checkContenteditableSliceForEmptiness",{enumerable:!0,get:function(){return t.checkContenteditableSliceForEmptiness}})})(we);var nt={};(function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.getContenteditableSlice=void 0;var t=F;Object.defineProperty(e,"getContenteditableSlice",{enumerable:!0,get:function(){return t.getContenteditableSlice}})})(nt);var rt={},Pe={};Object.defineProperty(Pe,"__esModule",{value:!0}),Pe.focus=Bn;var $n=c;function Bn(e,t){var n,r;if(t===void 0&&(t=!0),(0,$n.isNativeInput)(e)){e.focus();var i=t?0:e.value.length;e.setSelectionRange(i,i)}else{var a=document.createRange(),l=window.getSelection();if(!l)return;var s=function(g,L){L===void 0&&(L=!1);var w=document.createTextNode("");L?g.insertBefore(w,g.firstChild):g.appendChild(w),a.setStart(w,0),a.setEnd(w,0)},o=function(g){return g!=null},d=e.childNodes,u=t?d[0]:d[d.length-1];if(o(u)){for(;o(u)&&u.nodeType!==Node.TEXT_NODE;)u=t?u.firstChild:u.lastChild;if(o(u)&&u.nodeType===Node.TEXT_NODE){var h=(r=(n=u.textContent)===null||n===void 0?void 0:n.length)!==null&&r!==void 0?r:0,i=t?0:h;a.setStart(u,i),a.setEnd(u,i)}else s(e,t)}else s(e);l.removeAllRanges(),l.addRange(a)}}(function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.focus=void 0;var t=Pe;Object.defineProperty(e,"focus",{enumerable:!0,get:function(){return t.focus}})})(rt);var je={},R={};Object.defineProperty(R,"__esModule",{value:!0}),R.getCaretNodeAndOffset=Wn;function Wn(){var e=window.getSelection();if(e===null)return[null,0];var t=e.focusNode,n=e.focusOffset;return t===null?[null,0]:(t.nodeType!==Node.TEXT_NODE&&t.childNodes.length>0&&(t.childNodes[n]!==void 0?(t=t.childNodes[n],n=0):(t=t.childNodes[n-1],t.textContent!==null&&(n=t.textContent.length))),[t,n])}(function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.getCaretNodeAndOffset=void 0;var t=R;Object.defineProperty(e,"getCaretNodeAndOffset",{enumerable:!0,get:function(){return t.getCaretNodeAndOffset}})})(je);var it={},q={};Object.defineProperty(q,"__esModule",{value:!0}),q.getRange=Dn;function Dn(){var e=window.getSelection();return e&&e.rangeCount?e.getRangeAt(0):null}(function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.getRange=void 0;var t=q;Object.defineProperty(e,"getRange",{enumerable:!0,get:function(){return t.getRange}})})(it);var at={},Te={};Object.defineProperty(Te,"__esModule",{value:!0}),Te.isCaretAtEndOfInput=Rn;var lt=c,Hn=je,Fn=we;function Rn(e){var t=(0,lt.getDeepestNode)(e,!0);if(t===null)return!0;if((0,lt.isNativeInput)(t))return t.selectionEnd===t.value.length;var n=(0,Hn.getCaretNodeAndOffset)(),r=n[0],i=n[1];return r===null?!1:(0,Fn.checkContenteditableSliceForEmptiness)(e,r,i,"right")}(function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.isCaretAtEndOfInput=void 0;var t=Te;Object.defineProperty(e,"isCaretAtEndOfInput",{enumerable:!0,get:function(){return t.isCaretAtEndOfInput}})})(at);var st={},Le={};Object.defineProperty(Le,"__esModule",{value:!0}),Le.isCaretAtStartOfInput=Kn;var U=c,qn=R,Un=H;function Kn(e){var t=(0,U.getDeepestNode)(e);if(t===null||(0,U.isEmpty)(e))return!0;if((0,U.isNativeInput)(t))return t.selectionEnd===0;if((0,U.isEmpty)(e))return!0;var n=(0,qn.getCaretNodeAndOffset)(),r=n[0],i=n[1];return r===null?!1:(0,Un.checkContenteditableSliceForEmptiness)(e,r,i,"left")}(function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.isCaretAtStartOfInput=void 0;var t=Le;Object.defineProperty(e,"isCaretAtStartOfInput",{enumerable:!0,get:function(){return t.isCaretAtStartOfInput}})})(st);var ot={},Me={};Object.defineProperty(Me,"__esModule",{value:!0}),Me.save=Gn;var zn=c,Xn=q;function Gn(){var e=(0,Xn.getRange)(),t=(0,zn.make)("span");if(t.id="cursor",t.hidden=!0,!!e)return e.insertNode(t),function(){var r=window.getSelection();r&&(e.setStartAfter(t),e.setEndAfter(t),r.removeAllRanges(),r.addRange(e),setTimeout(function(){t.remove()},150))}}(function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.save=void 0;var t=Me;Object.defineProperty(e,"save",{enumerable:!0,get:function(){return t.save}})})(ot),function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.save=e.isCaretAtStartOfInput=e.isCaretAtEndOfInput=e.getRange=e.getCaretNodeAndOffset=e.focus=e.getContenteditableSlice=e.checkContenteditableSliceForEmptiness=void 0;var t=we;Object.defineProperty(e,"checkContenteditableSliceForEmptiness",{enumerable:!0,get:function(){return t.checkContenteditableSliceForEmptiness}});var n=nt;Object.defineProperty(e,"getContenteditableSlice",{enumerable:!0,get:function(){return n.getContenteditableSlice}});var r=rt;Object.defineProperty(e,"focus",{enumerable:!0,get:function(){return r.focus}});var i=je;Object.defineProperty(e,"getCaretNodeAndOffset",{enumerable:!0,get:function(){return i.getCaretNodeAndOffset}});var a=it;Object.defineProperty(e,"getRange",{enumerable:!0,get:function(){return a.getRange}});var l=at;Object.defineProperty(e,"isCaretAtEndOfInput",{enumerable:!0,get:function(){return l.isCaretAtEndOfInput}});var s=st;Object.defineProperty(e,"isCaretAtStartOfInput",{enumerable:!0,get:function(){return s.isCaretAtStartOfInput}});var o=ot;Object.defineProperty(e,"save",{enumerable:!0,get:function(){return o.save}})}(j);class f{static get CSS(){return{...p,checklist:`${m}-checklist`,itemChecked:`${m}__checkbox--checked`,noHover:`${m}__checkbox--no-hover`,checkbox:`${m}__checkbox-check`,checkboxContainer:`${m}__checkbox`,checkboxCheckDisabled:`${m}__checkbox-check--disabled`}}constructor(t,n){this.config=n,this.readOnly=t}renderWrapper(t){let n;return t===!0?(n=c.make("ul",[f.CSS.wrapper,f.CSS.checklist]),n.addEventListener("click",r=>{const i=r.target;if(i){const a=i.closest(`.${f.CSS.checkboxContainer}`);a&&a.contains(i)&&this.toggleCheckbox(a)}})):n=c.make("ul",[f.CSS.checklist,f.CSS.itemChildren]),n}renderItem(t,n){const r=c.make("li",[f.CSS.item,f.CSS.item]),i=c.make("div",f.CSS.itemContent,{innerHTML:t,contentEditable:(!this.readOnly).toString()}),a=c.make("span",f.CSS.checkbox),l=c.make("div",f.CSS.checkboxContainer);return n.checked===!0&&l.classList.add(f.CSS.itemChecked),this.readOnly&&l.classList.add(f.CSS.checkboxCheckDisabled),a.innerHTML=P,l.appendChild(a),r.appendChild(l),r.appendChild(i),r}getItemContent(t){const n=t.querySelector(`.${f.CSS.itemContent}`);return!n||c.isEmpty(n)?"":n.innerHTML}getItemMeta(t){const n=t.querySelector(`.${f.CSS.checkboxContainer}`);return{checked:n?n.classList.contains(f.CSS.itemChecked):!1}}composeDefaultMeta(){return{checked:!1}}toggleCheckbox(t){t.classList.toggle(f.CSS.itemChecked),t.classList.add(f.CSS.noHover),t.addEventListener("mouseleave",()=>this.removeSpecialHoverBehavior(t),{once:!0})}removeSpecialHoverBehavior(t){t.classList.remove(f.CSS.noHover)}}function Ne(e,t="after"){const n=[];let r;function i(a){switch(t){case"after":return a.nextElementSibling;case"before":return a.previousElementSibling}}for(r=i(e);r!==null;)n.push(r),r=i(r);return n.length!==0?n:null}function y(e,t=!0){let n=e;return e.classList.contains(p.item)&&(n=e.querySelector(`.${p.itemChildren}`)),n===null?[]:t?Array.from(n.querySelectorAll(`:scope > .${p.item}`)):Array.from(n.querySelectorAll(`.${p.item}`))}function Vn(e){return e.nextElementSibling===null}function Yn(e){return e.querySelector(`.${p.itemChildren}`)!==null}function S(e){return e.querySelector(`.${p.itemChildren}`)}function Ae(e){let t=e;e.classList.contains(p.item)&&(t=S(e)),t!==null&&y(t).length===0&&t.remove()}function K(e){return e.querySelector(`.${p.itemContent}`)}function E(e,t=!0){const n=K(e);n&&j.focus(n,t)}class $e{get currentItem(){const t=window.getSelection();if(!t)return null;let n=t.anchorNode;return!n||(k(n)||(n=n.parentNode),!n)||!k(n)?null:n.closest(`.${p.item}`)}get currentItemLevel(){const t=this.currentItem;if(t===null)return null;let n=t.parentNode,r=0;for(;n!==null&&n!==this.listWrapper;)k(n)&&n.classList.contains(p.item)&&(r+=1),n=n.parentNode;return r+1}constructor({data:t,config:n,api:r,readOnly:i,block:a},l){this.config=n,this.data=t,this.readOnly=i,this.api=r,this.block=a,this.renderer=l}render(){return this.listWrapper=this.renderer.renderWrapper(!0),this.data.items.length?this.appendItems(this.data.items,this.listWrapper):this.appendItems([{content:"",meta:{},items:[]}],this.listWrapper),this.readOnly||this.listWrapper.addEventListener("keydown",t=>{switch(t.key){case"Enter":t.shiftKey||this.enterPressed(t);break;case"Backspace":this.backspace(t);break;case"Tab":t.shiftKey?this.shiftTab(t):this.addTab(t);break}},!1),"start"in this.data.meta&&this.data.meta.start!==void 0&&this.changeStartWith(this.data.meta.start),"counterType"in this.data.meta&&this.data.meta.counterType!==void 0&&this.changeCounters(this.data.meta.counterType),this.listWrapper}save(t){const n=t??this.listWrapper,r=l=>y(l).map(o=>{const d=S(o),u=this.renderer.getItemContent(o),h=this.renderer.getItemMeta(o),g=d?r(d):[];return{content:u,meta:h,items:g}}),i=n?r(n):[];let a={style:this.data.style,meta:{},items:i};return this.data.style==="ordered"&&(a.meta={start:this.data.meta.start,counterType:this.data.meta.counterType}),a}static get pasteConfig(){return{tags:["OL","UL","LI"]}}merge(t){const n=this.block.holder.querySelectorAll(`.${p.item}`),r=n[n.length-1],i=K(r);if(r===null||i===null||(i.insertAdjacentHTML("beforeend",t.items[0].content),this.listWrapper===void 0))return;const a=y(this.listWrapper);if(a.length===0)return;const l=a[a.length-1];let s=S(l);const o=t.items.shift();o!==void 0&&(o.items.length!==0&&(s===null&&(s=this.renderer.renderWrapper(!1)),this.appendItems(o.items,s)),t.items.length>0&&this.appendItems(t.items,this.listWrapper))}onPaste(t){const n=t.detail.data;this.data=this.pasteHandler(n);const r=this.listWrapper;r&&r.parentNode&&r.parentNode.replaceChild(this.render(),r)}pasteHandler(t){const{tagName:n}=t;let r="unordered",i;switch(n){case"OL":r="ordered",i="ol";break;case"UL":case"LI":r="unordered",i="ul"}const a={style:r,meta:{},items:[]};r==="ordered"&&(this.data.meta.counterType="numeric",this.data.meta.start=1);const l=s=>Array.from(s.querySelectorAll(":scope > li")).map(d=>{const u=d.querySelector(`:scope > ${i}`),h=u?l(u):[];return{content:d.innerHTML??"",meta:{},items:h}});return a.items=l(t),a}changeStartWith(t){this.listWrapper.style.setProperty("counter-reset",`item ${t-1}`),this.data.meta.start=t}changeCounters(t){this.listWrapper.style.setProperty("--list-counter-type",t),this.data.meta.counterType=t}enterPressed(t){var s;const n=this.currentItem;if(t.stopPropagation(),t.preventDefault(),t.isComposing||n===null)return;const r=((s=this.renderer)==null?void 0:s.getItemContent(n).trim().length)===0,i=n.parentNode===this.listWrapper,a=n.previousElementSibling===null,l=this.api.blocks.getCurrentBlockIndex();if(i&&r)if(Vn(n)&&!Yn(n)){a?this.convertItemToDefaultBlock(l,!0):this.convertItemToDefaultBlock();return}else{this.splitList(n);return}else if(r){this.unshiftItem(n);return}else this.splitItem(n)}backspace(t){var r;const n=this.currentItem;if(n!==null&&j.isCaretAtStartOfInput(n)&&((r=window.getSelection())==null?void 0:r.isCollapsed)!==!1){if(t.stopPropagation(),n.parentNode===this.listWrapper&&n.previousElementSibling===null){this.convertFirstItemToDefaultBlock();return}t.preventDefault(),this.mergeItemWithPrevious(n)}}shiftTab(t){t.stopPropagation(),t.preventDefault(),this.currentItem!==null&&this.unshiftItem(this.currentItem)}unshiftItem(t){if(!t.parentNode||!k(t.parentNode))return;const n=t.parentNode.closest(`.${p.item}`);if(!n)return;let r=S(t);if(t.parentElement===null)return;const i=Ne(t);i!==null&&(r===null&&(r=this.renderer.renderWrapper(!1)),i.forEach(a=>{r.appendChild(a)}),t.appendChild(r)),n.after(t),E(t,!1),Ae(n)}splitList(t){const n=y(t),r=this.block,i=this.api.blocks.getCurrentBlockIndex();if(n.length!==0){const o=n[0];this.unshiftItem(o),E(t,!1)}if(t.previousElementSibling===null&&t.parentNode===this.listWrapper){this.convertItemToDefaultBlock(i);return}const a=Ne(t);if(a===null)return;const l=this.renderer.renderWrapper(!0);a.forEach(o=>{l.appendChild(o)});const s=this.save(l);s.meta.start=this.data.style=="ordered"?1:void 0,this.api.blocks.insert(r==null?void 0:r.name,s,this.config,i+1),this.convertItemToDefaultBlock(i+1),l.remove()}splitItem(t){const[n,r]=j.getCaretNodeAndOffset();if(n===null)return;const i=K(t);let a;i===null?a="":a=j.getContenteditableSlice(i,n,r,"right",!0);const l=S(t),s=this.renderItem(a);t==null||t.after(s),l&&s.appendChild(l),E(s)}mergeItemWithPrevious(t){const n=t.previousElementSibling,r=t.parentNode;if(r===null||!k(r))return;const i=r.closest(`.${p.item}`);if(!n&&!i||n&&!k(n))return;let a;if(n){const h=y(n,!1);h.length!==0&&h.length!==0?a=h[h.length-1]:a=n}else a=i;const l=this.renderer.getItemContent(t);if(!a)return;E(a,!1);const s=K(a);if(s===null)return;s.insertAdjacentHTML("beforeend",l);const o=y(t);if(o.length===0){t.remove(),Ae(a);return}const d=n||i,u=S(d)??this.renderer.renderWrapper(!1);n?o.forEach(h=>{u.appendChild(h)}):o.forEach(h=>{u.prepend(h)}),S(d)===null&&a.appendChild(u),t.remove()}addTab(t){var a;t.stopPropagation(),t.preventDefault();const n=this.currentItem;if(!n)return;if(((a=this.config)==null?void 0:a.maxLevel)!==void 0){const l=this.currentItemLevel;if(l!==null&&l===this.config.maxLevel)return}const r=n.previousSibling;if(r===null||!k(r))return;const i=S(r);if(i)i.appendChild(n),y(n).forEach(s=>{i.appendChild(s)});else{const l=this.renderer.renderWrapper(!1);l.appendChild(n),y(n).forEach(o=>{l.appendChild(o)}),r.appendChild(l)}Ae(n),E(n,!1)}convertItemToDefaultBlock(t,n){let r;const i=this.currentItem,a=i!==null?this.renderer.getItemContent(i):"";n===!0&&this.api.blocks.delete(),t!==void 0?r=this.api.blocks.insert(void 0,{text:a},void 0,t):r=this.api.blocks.insert(),i==null||i.remove(),this.api.caret.setToBlock(r,"start")}convertFirstItemToDefaultBlock(){const t=this.currentItem;if(t===null)return;const n=y(t);if(n.length!==0){const l=n[0];this.unshiftItem(l),E(t)}const r=Ne(t),i=this.api.blocks.getCurrentBlockIndex(),a=r===null;this.convertItemToDefaultBlock(i,a)}renderItem(t,n){const r=n??this.renderer.composeDefaultMeta();switch(!0){case this.renderer instanceof v:return this.renderer.renderItem(t,r);case this.renderer instanceof b:return this.renderer.renderItem(t,r);default:return this.renderer.renderItem(t,r)}}appendItems(t,n){t.forEach(r=>{var a;const i=this.renderItem(r.content,r.meta);if(n.appendChild(i),r.items.length){const l=(a=this.renderer)==null?void 0:a.renderWrapper(!1);this.appendItems(r.items,l),i.appendChild(l)}})}}const I={wrapper:`${m}-start-with-field`,input:`${m}-start-with-field__input`,startWithElementWrapperInvalid:`${m}-start-with-field--invalid`};function Jn(e,{value:t,placeholder:n,attributes:r,sanitize:i}){const a=c.make("div",I.wrapper),l=c.make("input",I.input,{placeholder:n,tabIndex:-1,value:t});for(const s in r)l.setAttribute(s,r[s]);return a.appendChild(l),l.addEventListener("input",()=>{i!==void 0&&(l.value=i(l.value));const s=l.checkValidity();!s&&!a.classList.contains(I.startWithElementWrapperInvalid)&&a.classList.add(I.startWithElementWrapperInvalid),s&&a.classList.contains(I.startWithElementWrapperInvalid)&&a.classList.remove(I.startWithElementWrapperInvalid),s&&e(l.value)}),a}const T=new Map([["Numeric","numeric"],["Lower Roman","lower-roman"],["Upper Roman","upper-roman"],["Lower Alpha","lower-alpha"],["Upper Alpha","upper-alpha"]]),ut=new Map([["numeric",ct],["lower-roman",dt],["upper-roman",ft],["lower-alpha",ht],["upper-alpha",pt]]),hr="",mr="";function Qn(e){return e.replace(/\D+/g,"")}function Zn(e){return typeof e.items[0]=="string"}function xn(e){return!("meta"in e)}function er(e){return typeof e.items[0]!="string"&&"text"in e.items[0]&&"checked"in e.items[0]&&typeof e.items[0].text=="string"&&typeof e.items[0].checked=="boolean"}function tr(e){const t=[];return Zn(e)?(e.items.forEach(n=>{t.push({content:n,meta:{},items:[]})}),{style:e.style,meta:{},items:t}):er(e)?(e.items.forEach(n=>{t.push({content:n.text,meta:{checked:n.checked},items:[]})}),{style:"checklist",meta:{},items:t}):xn(e)?{style:e.style,meta:{},items:e.items}:structuredClone(e)}class z{static get isReadOnlySupported(){return!0}static get enableLineBreaks(){return!0}static get toolbox(){return[{icon:Be,title:"Unordered List",data:{style:"unordered"}},{icon:We,title:"Ordered List",data:{style:"ordered"}},{icon:_,title:"Checklist",data:{style:"checklist"}}]}static get pasteConfig(){return{tags:["OL","UL","LI"]}}static get conversionConfig(){return{export:t=>z.joinRecursive(t),import:(t,n)=>({meta:{},items:[{content:t,meta:{},items:[]}],style:(n==null?void 0:n.defaultStyle)!==void 0?n.defaultStyle:"unordered"})}}get listStyle(){return this.data.style||this.defaultListStyle}set listStyle(t){var r;this.data.style=t,this.changeTabulatorByStyle();const n=this.list.render();(r=this.listElement)==null||r.replaceWith(n),this.listElement=n}constructor({data:t,config:n,api:r,readOnly:i,block:a}){var s;this.api=r,this.readOnly=i,this.config=n,this.block=a,this.defaultListStyle=((s=this.config)==null?void 0:s.defaultStyle)||"unordered",this.defaultCounterTypes=this.config.counterTypes||Array.from(T.values());const l={style:this.defaultListStyle,meta:{},items:[]};this.data=Object.keys(t).length?tr(t):l,this.listStyle==="ordered"&&this.data.meta.counterType===void 0&&(this.data.meta.counterType="numeric"),this.changeTabulatorByStyle()}static joinRecursive(t){return t.items.map(n=>`${n.content} ${z.joinRecursive(n)}`).join("")}render(){return this.listElement=this.list.render(),this.listElement}save(){return this.data=this.list.save(),this.data}merge(t){this.list.merge(t)}renderSettings(){const t=[{label:this.api.i18n.t("Unordered"),icon:Be,closeOnActivate:!0,isActive:this.listStyle=="unordered",onActivate:()=>{this.listStyle="unordered"}},{label:this.api.i18n.t("Ordered"),icon:We,closeOnActivate:!0,isActive:this.listStyle=="ordered",onActivate:()=>{this.listStyle="ordered"}},{label:this.api.i18n.t("Checklist"),icon:_,closeOnActivate:!0,isActive:this.listStyle=="checklist",onActivate:()=>{this.listStyle="checklist"}}];if(this.listStyle==="ordered"){const n=Jn(a=>this.changeStartWith(Number(a)),{value:String(this.data.meta.start??1),placeholder:"",attributes:{required:"true"},sanitize:a=>Qn(a)}),r=[{label:this.api.i18n.t("Start with"),icon:mt,children:{items:[{element:n,type:"html"}]}}],i={label:this.api.i18n.t("Counter type"),icon:ut.get(this.data.meta.counterType),children:{items:[]}};T.forEach((a,l)=>{const s=T.get(l);this.defaultCounterTypes.includes(s)&&i.children.items.push({title:this.api.i18n.t(l),icon:ut.get(s),isActive:this.data.meta.counterType===T.get(l),closeOnActivate:!0,onActivate:()=>{this.changeCounters(T.get(l))}})}),i.children.items.length>1&&r.push(i),t.push({type:"separator"},...r)}return t}onPaste(t){const{tagName:n}=t.detail.data;switch(n){case"OL":this.listStyle="ordered";break;case"UL":case"LI":this.listStyle="unordered"}this.list.onPaste(t)}pasteHandler(t){return this.list.pasteHandler(t)}changeCounters(t){var n;(n=this.list)==null||n.changeCounters(t),this.data.meta.counterType=t}changeStartWith(t){var n;(n=this.list)==null||n.changeStartWith(t),this.data.meta.start=t}changeTabulatorByStyle(){switch(this.listStyle){case"ordered":this.list=new $e({data:this.data,readOnly:this.readOnly,api:this.api,config:this.config,block:this.block},new v(this.readOnly,this.config));break;case"unordered":this.list=new $e({data:this.data,readOnly:this.readOnly,api:this.api,config:this.config,block:this.block},new b(this.readOnly,this.config));break;case"checklist":this.list=new $e({data:this.data,readOnly:this.readOnly,api:this.api,config:this.config,block:this.block},new f(this.readOnly,this.config));break}}}return z}); \ No newline at end of file diff --git a/src/webui/public/js/compact/editor.marker.min.js b/src/webui/public/js/compact/editor.marker.min.js new file mode 100644 index 0000000..cf9968a --- /dev/null +++ b/src/webui/public/js/compact/editor.marker.min.js @@ -0,0 +1,8 @@ +/** + * Skipped minification because the original files appears to be already minified. + * Original file: /npm/@editorjs/marker@1.4.0/dist/marker.umd.js + * + * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files + */ +(function(){"use strict";try{if(typeof document<"u"){var e=document.createElement("style");e.appendChild(document.createTextNode(".cdx-marker{background:rgba(245,235,111,.29);padding:3px 0}")),document.head.appendChild(e)}}catch(d){console.error("vite-plugin-css-injected-by-js",d)}})(); +(function(i,s){typeof exports=="object"&&typeof module<"u"?module.exports=s():typeof define=="function"&&define.amd?define(s):(i=typeof globalThis<"u"?globalThis:i||self,i.Marker=s())})(this,function(){"use strict";const i="",s='';class n{static get CSS(){return"cdx-marker"}constructor({api:t}){this.api=t,this.button=null,this.tag="MARK",this.iconClasses={base:this.api.styles.inlineToolButton,active:this.api.styles.inlineToolButtonActive}}static get isInline(){return!0}render(){return this.button=document.createElement("button"),this.button.type="button",this.button.classList.add(this.iconClasses.base),this.button.innerHTML=this.toolboxIcon,this.button}surround(t){if(!t)return;let e=this.api.selection.findParentTag(this.tag,n.CSS);e?this.unwrap(e):this.wrap(t)}wrap(t){let e=document.createElement(this.tag);e.classList.add(n.CSS),e.appendChild(t.extractContents()),t.insertNode(e),this.api.selection.expandToTag(e)}unwrap(t){this.api.selection.expandToTag(t);let e=window.getSelection(),o=e.getRangeAt(0),a=o.extractContents();t.parentNode.removeChild(t),o.insertNode(a),e.removeAllRanges(),e.addRange(o)}checkState(){const t=this.api.selection.findParentTag(this.tag,n.CSS);this.button.classList.toggle(this.iconClasses.active,!!t)}get toolboxIcon(){return s}static get sanitize(){return{mark:{class:n.CSS}}}}return n}); \ No newline at end of file diff --git a/src/webui/public/js/compact/editor.min.js b/src/webui/public/js/compact/editor.min.js new file mode 100644 index 0000000..e1aed0f --- /dev/null +++ b/src/webui/public/js/compact/editor.min.js @@ -0,0 +1,8 @@ +/** + * Skipped minification because the original files appears to be already minified. + * Original file: /npm/@editorjs/editorjs@2.26.5/dist/editor.js + * + * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files + */ +/*! For license information please see editor.js.LICENSE.txt */ +!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.EditorJS=t():e.EditorJS=t()}(window,(function(){return function(e){var t={};function n(o){if(t[o])return t[o].exports;var r=t[o]={i:o,l:!1,exports:{}};return e[o].call(r.exports,r,r.exports,n),r.l=!0,r.exports}return n.m=e,n.c=t,n.d=function(e,t,o){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:o})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var o=Object.create(null);if(n.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)n.d(o,r,function(t){return e[t]}.bind(null,r));return o},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=187)}([function(e,t,n){var o=n(10),r=n(16),i=n(27),a=n(23),s=n(31),l=function(e,t,n){var c,u,f,d,p=e&l.F,h=e&l.G,v=e&l.S,g=e&l.P,y=e&l.B,k=h?o:v?o[t]||(o[t]={}):(o[t]||{}).prototype,b=h?r:r[t]||(r[t]={}),m=b.prototype||(b.prototype={});for(c in h&&(n=t),n)f=((u=!p&&k&&void 0!==k[c])?k:n)[c],d=y&&u?s(f,o):g&&"function"==typeof f?s(Function.call,f):f,k&&a(k,c,f,e&l.U),b[c]!=f&&i(b,c,d),g&&m[c]!=f&&(m[c]=f)};o.core=r,l.F=1,l.G=2,l.S=4,l.P=8,l.B=16,l.W=32,l.U=64,l.R=128,e.exports=l},function(e,t){e.exports=function(e){return e&&e.__esModule?e:{default:e}},e.exports.__esModule=!0,e.exports.default=e.exports},function(e,t){e.exports=function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")},e.exports.__esModule=!0,e.exports.default=e.exports},function(e,t){function n(e,t){for(var n=0;n2&&void 0!==arguments[2]?arguments[2]:"log",o=arguments.length>3?arguments[3]:void 0,r=arguments.length>4&&void 0!==arguments[4]?arguments[4]:"color: inherit";if("console"in window&&window.console[n]){var i=["info","log","warn","error"].includes(n),a=[];switch(c.logLevel){case s.ERROR:if("error"!==n)return;break;case s.WARN:if(!["error","warn"].includes(n))return;break;case s.INFO:if(!i||e)return}o&&a.push(o);var l="Editor.js ".concat("2.26.5"),u="line-height: 1em;\n color: #006FEA;\n display: inline-block;\n font-size: 11px;\n line-height: 1em;\n background-color: #fff;\n padding: 4px 9px;\n border-radius: 30px;\n border: 1px solid rgba(56, 138, 229, 0.16);\n margin: 4px 5px 4px 0;";e&&(i?(a.unshift(u,r),t="%c".concat(l,"%c ").concat(t)):t="( ".concat(l," )").concat(t));try{if(i)if(o){var f;(f=console)[n].apply(f,["".concat(t," %o")].concat(a))}else{var d;(d=console)[n].apply(d,[t].concat(a))}else console[n](t)}catch(e){}}}Object.defineProperty(e,"__esModule",{value:!0}),e.LogLevels=void 0,e.array=function(e){return Array.prototype.slice.call(e)},e.beautifyShortcut=function(e){var t=y();return e=e.replace(/shift/gi,"⇧").replace(/backspace/gi,"⌫").replace(/enter/gi,"⏎").replace(/up/gi,"↑").replace(/left/gi,"→").replace(/down/gi,"↓").replace(/right/gi,"←").replace(/escape/gi,"⎋").replace(/insert/gi,"Ins").replace(/delete/gi,"␡").replace(/\+/gi," + "),e=t.mac?e.replace(/ctrl|cmd/gi,"⌘").replace(/alt/gi,"⌥"):e.replace(/cmd/gi,"Ctrl").replace(/windows/gi,"WIN")},e.cacheable=function(e,t,n){var o=n.value?"value":"get",r=n[o],i="#".concat(t,"Cache");if(n[o]=function(){if(void 0===this[i]){for(var e=arguments.length,t=new Array(e),n=0;n1?n-1:0),i=1;i0&&void 0!==arguments[0]?arguments[0]:"";return"".concat(e).concat(Math.floor(1e8*Math.random()).toString(16))},e.getFileExtension=function(e){return e.name.split(".").pop()},e.getUserOS=y,e.getValidUrl=function(e){try{return new URL(e).href}catch(e){}return"//"===e.substring(0,2)?window.location.protocol+e:window.location.origin+e},e.isBoolean=function(e){return"boolean"===d(e)},e.isClass=function(e){return p(e)&&/^\s*class\s+/.test(e.toString())},e.isEmpty=function(e){return!e||0===Object.keys(e).length&&e.constructor===Object},e.isFunction=p,e.isIosDevice=void 0,e.isMobileScreen=function(){return window.matchMedia("(max-width: ".concat(650,"px)")).matches},e.isNumber=function(e){return"number"===d(e)},e.isObject=h,e.isPrintableKey=function(e){return e>47&&e<58||32===e||13===e||229===e||e>64&&e<91||e>95&&e<112||e>185&&e<193||e>218&&e<223},e.isPromise=function(e){return Promise.resolve(e)===e},e.isString=function(e){return"string"===d(e)},e.isTouchSupported=void 0,e.isUndefined=v,e.isValidMimeType=function(e){return/^[-\w]+\/([-+\w]+|\*)$/.test(e)},e.mouseButtons=e.mobileScreenBreakpoint=e.logLabeled=e.log=e.keyCodes=void 0,e.openTab=function(e){window.open(e,"_blank")},e.sequence=function(e){return g.apply(this,arguments)},e.setLogLevel=function(e){c.logLevel=e},e.throttle=function(e,t){var n,o,r,i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:void 0,a=null,s=0;i||(i={});var l=function(){s=!1===i.leading?0:Date.now(),a=null,r=e.apply(n,o),a||(n=o=null)};return function(){var c=Date.now();s||!1!==i.leading||(s=c);var u=t-(c-s);return n=this,o=arguments,u<=0||u>t?(a&&(clearTimeout(a),a=null),s=c,r=e.apply(n,o),a||(n=o=null)):a||!1===i.trailing||(a=setTimeout(l,u)),r}},e.typeOf=d,t=l(t),o=l(o),r=l(r),a=l(a),e.LogLevels=s,function(e){e.VERBOSE="VERBOSE",e.INFO="INFO",e.WARN="WARN",e.ERROR="ERROR"}(s||(e.LogLevels=s={})),e.keyCodes={BACKSPACE:8,TAB:9,ENTER:13,SHIFT:16,CTRL:17,ALT:18,ESC:27,SPACE:32,LEFT:37,UP:38,DOWN:40,RIGHT:39,DELETE:46,META:91},e.mouseButtons={LEFT:0,WHEEL:1,RIGHT:2,BACKWARD:3,FORWARD:4},c.logLevel=s.VERBOSE;var u=c.bind(window,!1);e.log=u;var f=c.bind(window,!0);function d(e){return Object.prototype.toString.call(e).match(/\s([a-zA-Z]+)/)[1].toLowerCase()}function p(e){return"function"===d(e)||"asyncfunction"===d(e)}function h(e){return"object"===d(e)}function v(e){return"undefined"===d(e)}function g(){return(g=(0,r.default)(t.default.mark((function e(n){var o,i,a,s,l=arguments;return t.default.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return s=function(){return(s=(0,r.default)(t.default.mark((function e(n,o,r){return t.default.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.prev=0,e.next=3,n.function(n.data);case 3:return e.next=5,o(v(n.data)?{}:n.data);case 5:e.next=10;break;case 7:e.prev=7,e.t0=e.catch(0),r(v(n.data)?{}:n.data);case 10:case"end":return e.stop()}}),e,null,[[0,7]])})))).apply(this,arguments)},a=function(e,t,n){return s.apply(this,arguments)},o=l.length>1&&void 0!==l[1]?l[1]:function(){},i=l.length>2&&void 0!==l[2]?l[2]:function(){},e.abrupt("return",n.reduce(function(){var e=(0,r.default)(t.default.mark((function e(n,r){return t.default.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,n;case 2:return e.abrupt("return",a(r,o,i));case 3:case"end":return e.stop()}}),e)})));return function(t,n){return e.apply(this,arguments)}}(),Promise.resolve()));case 5:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function y(){var e={win:!1,mac:!1,x11:!1,linux:!1},t=Object.keys(e).find((function(e){return-1!==window.navigator.appVersion.toLowerCase().indexOf(e)}));return t?(e[t]=!0,e):e}e.logLabeled=f;var k="ontouchstart"in document.documentElement;e.isTouchSupported=k,e.mobileScreenBreakpoint=650;var b="undefined"!=typeof window&&window.navigator&&window.navigator.platform&&(/iP(ad|hone|od)/.test(window.navigator.platform)||"MacIntel"===window.navigator.platform&&window.navigator.maxTouchPoints>1);e.isIosDevice=b})?o.apply(t,r):o)||(e.exports=i)},function(e,t,n){var o,r,i;"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self&&self,r=[t,n(2),n(3),n(112)],void 0===(i="function"==typeof(o=function(o,r,i,a){"use strict";var s=n(1);function l(e,t){var n="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!n){if(Array.isArray(e)||(n=function(e,t){if(e){if("string"==typeof e)return c(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?c(e,t):void 0}}(e))||t&&e&&"number"==typeof e.length){n&&(e=n);var o=0,r=function(){};return{s:r,n:function(){return o>=e.length?{done:!0}:{done:!1,value:e[o++]}},e:function(e){throw e},f:r}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var i,a=!0,s=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return a=e.done,e},e:function(e){s=!0,i=e},f:function(){try{a||null==n.return||n.return()}finally{if(s)throw i}}}}function c(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,o=new Array(t);n3&&void 0!==arguments[3]&&arguments[3];n.mutableListenerIds.push(n.listeners.on(e,t,o,r))},clearAll:function(){var e,t=l(n.mutableListenerIds);try{for(t.s();!(e=t.n()).done;){var o=e.value;n.listeners.offById(o)}}catch(e){t.e(e)}finally{t.f()}n.mutableListenerIds=[]}},this.mutableListenerIds=[],(this instanceof e?this.constructor:void 0)===e)throw new TypeError("Constructors for abstract class Module are not allowed.");this.config=o,this.eventsDispatcher=i}return(0,i.default)(e,[{key:"state",set:function(e){this.Editor=e}},{key:"removeAllNodes",value:function(){for(var e in this.nodes){var t=this.nodes[e];t instanceof HTMLElement&&t.remove()}}},{key:"isRtl",get:function(){return"rtl"===this.config.i18n.direction}}]),e}();o.default=u,u.displayName="Module",e.exports=t.default})?o.apply(t,r):o)||(e.exports=i)},function(e,t){var n=e.exports="undefined"!=typeof window&&window.Math==Math?window:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")();"number"==typeof __g&&(__g=n)},function(e,t){e.exports=function(e){try{return!!e()}catch(e){return!0}}},function(e,t,n){var o=n(13);e.exports=function(e){if(!o(e))throw TypeError(e+" is not an object!");return e}},function(e,t){e.exports=function(e){return"object"==typeof e?null!==e:"function"==typeof e}},function(e,t,n){var o=n(70)("wks"),r=n(45),i=n(10).Symbol,a="function"==typeof i;(e.exports=function(e){return o[e]||(o[e]=a&&i[e]||(a?i:r)("Symbol."+e))}).store=o},function(e,t,n){var o=n(33),r=Math.min;e.exports=function(e){return e>0?r(o(e),9007199254740991):0}},function(e,t){var n=e.exports={version:"2.6.12"};"number"==typeof __e&&(__e=n)},function(e,t,n){e.exports=!n(11)((function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a}))},function(e,t,n){var o=n(12),r=n(117),i=n(42),a=Object.defineProperty;t.f=n(17)?Object.defineProperty:function(e,t,n){if(o(e),t=i(t,!0),o(n),r)try{return a(e,t,n)}catch(e){}if("get"in n||"set"in n)throw TypeError("Accessors not supported!");return"value"in n&&(e[t]=n.value),e}},function(e,t,n){var o,r,i,a=n(7);"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self&&self,r=[t,n(41),n(2),n(3),n(8)],void 0===(i="function"==typeof(o=function(o,r,i,s,l){"use strict";var c=n(1);function u(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(u=function(e){return e?n:t})(e)}Object.defineProperty(o,"__esModule",{value:!0}),o.default=void 0,r=c(r),i=c(i),s=c(s),l=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!==a(e)&&"function"!=typeof e)return{default:e};var n=u(t);if(n&&n.has(e))return n.get(e);var o={},r=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var i in e)if("default"!==i&&Object.prototype.hasOwnProperty.call(e,i)){var s=r?Object.getOwnPropertyDescriptor(e,i):null;s&&(s.get||s.set)?Object.defineProperty(o,i,s):o[i]=e[i]}return o.default=e,n&&n.set(e,o),o}(l);var f=function(){function e(){(0,i.default)(this,e)}return(0,s.default)(e,null,[{key:"isSingleTag",value:function(e){return e.tagName&&["AREA","BASE","BR","COL","COMMAND","EMBED","HR","IMG","INPUT","KEYGEN","LINK","META","PARAM","SOURCE","TRACK","WBR"].includes(e.tagName)}},{key:"isLineBreakTag",value:function(e){return e&&e.tagName&&["BR","WBR"].includes(e.tagName)}},{key:"make",value:function(e){var t,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,o=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},i=document.createElement(e);for(var a in Array.isArray(n)?(t=i.classList).add.apply(t,(0,r.default)(n)):n&&i.classList.add(n),o)Object.prototype.hasOwnProperty.call(o,a)&&(i[a]=o[a]);return i}},{key:"text",value:function(e){return document.createTextNode(e)}},{key:"append",value:function(e,t){Array.isArray(t)?t.forEach((function(t){return e.appendChild(t)})):e.appendChild(t)}},{key:"prepend",value:function(e,t){Array.isArray(t)?(t=t.reverse()).forEach((function(t){return e.prepend(t)})):e.prepend(t)}},{key:"swap",value:function(e,t){var n=document.createElement("div"),o=e.parentNode;o.insertBefore(n,e),o.insertBefore(e,t),o.insertBefore(t,n),o.removeChild(n)}},{key:"find",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:document,t=arguments.length>1?arguments[1]:void 0;return e.querySelector(t)}},{key:"get",value:function(e){return document.getElementById(e)}},{key:"findAll",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:document,t=arguments.length>1?arguments[1]:void 0;return e.querySelectorAll(t)}},{key:"allInputsSelector",get:function(){return"[contenteditable=true], textarea, input:not([type]), "+["text","password","email","number","search","tel","url"].map((function(e){return'input[type="'.concat(e,'"]')})).join(", ")}},{key:"findAllInputs",value:function(t){return l.array(t.querySelectorAll(e.allInputsSelector)).reduce((function(t,n){return e.isNativeInput(n)||e.containsOnlyInlineElements(n)?[].concat((0,r.default)(t),[n]):[].concat((0,r.default)(t),(0,r.default)(e.getDeepestBlockElements(n)))}),[])}},{key:"getDeepestNode",value:function(t){var n=arguments.length>1&&void 0!==arguments[1]&&arguments[1],o=n?"lastChild":"firstChild",r=n?"previousSibling":"nextSibling";if(t&&t.nodeType===Node.ELEMENT_NODE&&t[o]){var i=t[o];if(e.isSingleTag(i)&&!e.isNativeInput(i)&&!e.isLineBreakTag(i))if(i[r])i=i[r];else{if(!i.parentNode[r])return i.parentNode;i=i.parentNode[r]}return this.getDeepestNode(i,n)}return t}},{key:"isElement",value:function(e){return!l.isNumber(e)&&e&&e.nodeType&&e.nodeType===Node.ELEMENT_NODE}},{key:"isFragment",value:function(e){return!l.isNumber(e)&&e&&e.nodeType&&e.nodeType===Node.DOCUMENT_FRAGMENT_NODE}},{key:"isContentEditable",value:function(e){return"true"===e.contentEditable}},{key:"isNativeInput",value:function(e){return!(!e||!e.tagName)&&["INPUT","TEXTAREA"].includes(e.tagName)}},{key:"canSetCaret",value:function(t){var n=!0;if(e.isNativeInput(t))switch(t.type){case"file":case"checkbox":case"radio":case"hidden":case"submit":case"button":case"image":case"reset":n=!1}else n=e.isContentEditable(t);return n}},{key:"isNodeEmpty",value:function(e){return!(this.isSingleTag(e)&&!this.isLineBreakTag(e))&&0===(this.isElement(e)&&this.isNativeInput(e)?e.value:e.textContent.replace("​","")).trim().length}},{key:"isLeaf",value:function(e){return!!e&&0===e.childNodes.length}},{key:"isEmpty",value:function(e){e.normalize();for(var t=[e];t.length>0;)if(e=t.shift()){if(this.isLeaf(e)&&!this.isNodeEmpty(e))return!1;e.childNodes&&t.push.apply(t,(0,r.default)(Array.from(e.childNodes)))}return!0}},{key:"isHTMLString",value:function(t){var n=e.make("div");return n.innerHTML=t,n.childElementCount>0}},{key:"getContentLength",value:function(t){return e.isNativeInput(t)?t.value.length:t.nodeType===Node.TEXT_NODE?t.length:t.textContent.length}},{key:"blockElements",get:function(){return["address","article","aside","blockquote","canvas","div","dl","dt","fieldset","figcaption","figure","footer","form","h1","h2","h3","h4","h5","h6","header","hgroup","hr","li","main","nav","noscript","ol","output","p","pre","ruby","section","table","tbody","thead","tr","tfoot","ul","video"]}},{key:"containsOnlyInlineElements",value:function(t){var n;return l.isString(t)?(n=document.createElement("div")).innerHTML=t:n=t,Array.from(n.children).every((function t(n){return!e.blockElements.includes(n.tagName.toLowerCase())&&Array.from(n.children).every(t)}))}},{key:"getDeepestBlockElements",value:function(t){return e.containsOnlyInlineElements(t)?[t]:Array.from(t.children).reduce((function(t,n){return[].concat((0,r.default)(t),(0,r.default)(e.getDeepestBlockElements(n)))}),[])}},{key:"getHolder",value:function(e){return l.isString(e)?document.getElementById(e):e}},{key:"isExtensionNode",value:function(e){return e&&["GRAMMARLY-EXTENSION"].includes(e.nodeName)}},{key:"isAnchor",value:function(e){return"a"===e.tagName.toLowerCase()}},{key:"offset",value:function(e){var t=e.getBoundingClientRect(),n=window.pageXOffset||document.documentElement.scrollLeft,o=window.pageYOffset||document.documentElement.scrollTop,r=t.top+o,i=t.left+n;return{top:r,left:i,bottom:r+t.height,right:i+t.width}}}]),e}();o.default=f,f.displayName="Dom",e.exports=t.default})?o.apply(t,r):o)||(e.exports=i)},function(e,t,n){var o=n(368)();e.exports=o;try{regeneratorRuntime=o}catch(e){"object"==typeof globalThis?globalThis.regeneratorRuntime=o:Function("r","regeneratorRuntime = r")(o)}},function(e,t){function n(e,t,n,o,r,i,a){try{var s=e[i](a),l=s.value}catch(e){return void n(e)}s.done?t(l):Promise.resolve(l).then(o,r)}e.exports=function(e){return function(){var t=this,o=arguments;return new Promise((function(r,i){var a=e.apply(t,o);function s(e){n(a,r,i,s,l,"next",e)}function l(e){n(a,r,i,s,l,"throw",e)}s(void 0)}))}},e.exports.__esModule=!0,e.exports.default=e.exports},function(e,t,n){var o=n(39);e.exports=function(e){return Object(o(e))}},function(e,t,n){var o=n(10),r=n(27),i=n(26),a=n(45)("src"),s=n(192),l=(""+s).split("toString");n(16).inspectSource=function(e){return s.call(e)},(e.exports=function(e,t,n,s){var c="function"==typeof n;c&&(i(n,"name")||r(n,"name",t)),e[t]!==n&&(c&&(i(n,a)||r(n,a,e[t]?""+e[t]:l.join(String(t)))),e===o?e[t]=n:s?e[t]?e[t]=n:r(e,t,n):(delete e[t],r(e,t,n)))})(Function.prototype,"toString",(function(){return"function"==typeof this&&this[a]||s.call(this)}))},function(e,t,n){var o=n(0),r=n(11),i=n(39),a=/"/g,s=function(e,t,n,o){var r=String(i(e)),s="<"+t;return""!==n&&(s+=" "+n+'="'+String(o).replace(a,""")+'"'),s+">"+r+""};e.exports=function(e,t){var n={};n[e]=t(s),o(o.P+o.F*r((function(){var t=""[e]('"');return t!==t.toLowerCase()||t.split('"').length>3})),"String",n)}},function(e,t,n){var o,r,i,a=n(7);"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self&&self,r=[t,n(2),n(3),n(8),n(19)],void 0===(i="function"==typeof(o=function(o,r,i,s,l){"use strict";var c=n(1);function u(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(u=function(e){return e?n:t})(e)}Object.defineProperty(o,"__esModule",{value:!0}),o.default=void 0,r=c(r),i=c(i),s=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!==a(e)&&"function"!=typeof e)return{default:e};var n=u(t);if(n&&n.has(e))return n.get(e);var o={},r=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var i in e)if("default"!==i&&Object.prototype.hasOwnProperty.call(e,i)){var s=r?Object.getOwnPropertyDescriptor(e,i):null;s&&(s.get||s.set)?Object.defineProperty(o,i,s):o[i]=e[i]}return o.default=e,n&&n.set(e,o),o}(s),l=c(l);var f=function(){function e(){(0,r.default)(this,e),this.instance=null,this.selection=null,this.savedSelectionRange=null,this.isFakeBackgroundEnabled=!1,this.commandBackground="backColor",this.commandRemoveFormat="removeFormat"}return(0,i.default)(e,[{key:"removeFakeBackground",value:function(){this.isFakeBackgroundEnabled&&(this.isFakeBackgroundEnabled=!1,document.execCommand(this.commandRemoveFormat))}},{key:"setFakeBackground",value:function(){document.execCommand(this.commandBackground,!1,"#a8d6ff"),this.isFakeBackgroundEnabled=!0}},{key:"save",value:function(){this.savedSelectionRange=e.range}},{key:"restore",value:function(){if(this.savedSelectionRange){var e=window.getSelection();e.removeAllRanges(),e.addRange(this.savedSelectionRange)}}},{key:"clearSaved",value:function(){this.savedSelectionRange=null}},{key:"collapseToEnd",value:function(){var e=window.getSelection(),t=document.createRange();t.selectNodeContents(e.focusNode),t.collapse(!1),e.removeAllRanges(),e.addRange(t)}},{key:"findParentTag",value:function(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:10,o=window.getSelection(),r=null;if(!o||!o.anchorNode||!o.focusNode)return null;var i=[o.anchorNode,o.focusNode];return i.forEach((function(o){for(var i=n;i>0&&o.parentNode&&(o.tagName!==e||(r=o,t&&o.classList&&!o.classList.contains(t)&&(r=null),!r));)o=o.parentNode,i--})),r}},{key:"expandToTag",value:function(e){var t=window.getSelection();t.removeAllRanges();var n=document.createRange();n.selectNodeContents(e),t.addRange(n)}}],[{key:"CSS",get:function(){return{editorWrapper:"codex-editor",editorZone:"codex-editor__redactor"}}},{key:"anchorNode",get:function(){var e=window.getSelection();return e?e.anchorNode:null}},{key:"anchorElement",get:function(){var e=window.getSelection();if(!e)return null;var t=e.anchorNode;return t?l.default.isElement(t)?t:t.parentElement:null}},{key:"anchorOffset",get:function(){var e=window.getSelection();return e?e.anchorOffset:null}},{key:"isCollapsed",get:function(){var e=window.getSelection();return e?e.isCollapsed:null}},{key:"isAtEditor",get:function(){return this.isSelectionAtEditor(e.get())}},{key:"isSelectionAtEditor",value:function(t){if(!t)return!1;var n=t.anchorNode||t.focusNode;n&&n.nodeType===Node.TEXT_NODE&&(n=n.parentNode);var o=null;return n&&n instanceof Element&&(o=n.closest(".".concat(e.CSS.editorZone))),!!o&&o.nodeType===Node.ELEMENT_NODE}},{key:"isRangeAtEditor",value:function(t){if(t){var n=t.startContainer;n&&n.nodeType===Node.TEXT_NODE&&(n=n.parentNode);var o=null;return n&&n instanceof Element&&(o=n.closest(".".concat(e.CSS.editorZone))),!!o&&o.nodeType===Node.ELEMENT_NODE}}},{key:"isSelectionExists",get:function(){return!!e.get().anchorNode}},{key:"range",get:function(){return this.getRangeFromSelection(this.get())}},{key:"getRangeFromSelection",value:function(e){return e&&e.rangeCount?e.getRangeAt(0):null}},{key:"rect",get:function(){var e,t=document.selection,n={x:0,y:0,width:0,height:0};if(t&&"Control"!==t.type)return e=(t=t).createRange(),n.x=e.boundingLeft,n.y=e.boundingTop,n.width=e.boundingWidth,n.height=e.boundingHeight,n;if(!window.getSelection)return s.log("Method window.getSelection is not supported","warn"),n;if(null===(t=window.getSelection()).rangeCount||isNaN(t.rangeCount))return s.log("Method SelectionUtils.rangeCount is not supported","warn"),n;if(0===t.rangeCount)return n;if((e=t.getRangeAt(0).cloneRange()).getBoundingClientRect&&(n=e.getBoundingClientRect()),0===n.x&&0===n.y){var o=document.createElement("span");if(o.getBoundingClientRect){o.appendChild(document.createTextNode("​")),e.insertNode(o),n=o.getBoundingClientRect();var r=o.parentNode;r.removeChild(o),r.normalize()}}return n}},{key:"text",get:function(){return window.getSelection?window.getSelection().toString():""}},{key:"get",value:function(){return window.getSelection()}},{key:"setCursor",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,n=document.createRange(),o=window.getSelection();if(l.default.isNativeInput(e)){if(!l.default.canSetCaret(e))return;return e.focus(),e.selectionStart=e.selectionEnd=t,e.getBoundingClientRect()}return n.setStart(e,t),n.setEnd(e,t),o.removeAllRanges(),o.addRange(n),n.getBoundingClientRect()}},{key:"addFakeCursor",value:function(t){var n=e.range,o=l.default.make("span","codex-editor__fake-cursor");o.dataset.mutationFree="true",!n||t&&!t.contains(n.startContainer)||(n.collapse(),n.insertNode(o))}},{key:"removeFakeCursor",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:document.body,t=l.default.find(e,".codex-editor__fake-cursor");t&&t.remove()}}]),e}();o.default=f,f.displayName="SelectionUtils",e.exports=t.default})?o.apply(t,r):o)||(e.exports=i)},function(e,t){var n={}.hasOwnProperty;e.exports=function(e,t){return n.call(e,t)}},function(e,t,n){var o=n(18),r=n(44);e.exports=n(17)?function(e,t,n){return o.f(e,t,r(1,n))}:function(e,t,n){return e[t]=n,e}},function(e,t,n){var o=n(62),r=n(39);e.exports=function(e){return o(r(e))}},function(e,t,n){"use strict";var o=n(11);e.exports=function(e,t){return!!e&&o((function(){t?e.call(null,(function(){}),1):e.call(null)}))}},function(e,t,n){var o=n(362),r=n(363),i=n(148),a=n(364);e.exports=function(e,t){return o(e)||r(e,t)||i(e,t)||a()},e.exports.__esModule=!0,e.exports.default=e.exports},function(e,t,n){var o=n(32);e.exports=function(e,t,n){if(o(e),void 0===t)return e;switch(n){case 1:return function(n){return e.call(t,n)};case 2:return function(n,o){return e.call(t,n,o)};case 3:return function(n,o,r){return e.call(t,n,o,r)}}return function(){return e.apply(t,arguments)}}},function(e,t){e.exports=function(e){if("function"!=typeof e)throw TypeError(e+" is not a function!");return e}},function(e,t){var n=Math.ceil,o=Math.floor;e.exports=function(e){return isNaN(e=+e)?0:(e>0?o:n)(e)}},function(e,t,n){var o=n(63),r=n(44),i=n(28),a=n(42),s=n(26),l=n(117),c=Object.getOwnPropertyDescriptor;t.f=n(17)?c:function(e,t){if(e=i(e),t=a(t,!0),l)try{return c(e,t)}catch(e){}if(s(e,t))return r(!o.f.call(e,t),e[t])}},function(e,t,n){var o=n(0),r=n(16),i=n(11);e.exports=function(e,t){var n=(r.Object||{})[e]||Object[e],a={};a[e]=t(n),o(o.S+o.F*i((function(){n(1)})),"Object",a)}},function(e,t,n){var o=n(31),r=n(62),i=n(22),a=n(15),s=n(133);e.exports=function(e,t){var n=1==e,l=2==e,c=3==e,u=4==e,f=6==e,d=5==e||f,p=t||s;return function(t,s,h){for(var v,g,y=i(t),k=r(y),b=o(s,h,3),m=a(k.length),w=0,x=n?p(t,m):l?p(t,0):void 0;m>w;w++)if((d||w in k)&&(g=b(v=k[w],w,y),e))if(n)x[w]=g;else if(g)switch(e){case 3:return!0;case 5:return v;case 6:return w;case 2:x.push(v)}else if(u)return!1;return f?-1:c||u?u:x}}},function(e,t,n){"use strict";n.r(t),n.d(t,"IconAddBackground",(function(){return c})),n.d(t,"IconAddBorder",(function(){return u})),n.d(t,"IconAlignCenter",(function(){return f})),n.d(t,"IconAlignJustify",(function(){return d})),n.d(t,"IconAlignLeft",(function(){return p})),n.d(t,"IconAlignRight",(function(){return h})),n.d(t,"IconBold",(function(){return v})),n.d(t,"IconBrackets",(function(){return g})),n.d(t,"IconChecklist",(function(){return y})),n.d(t,"IconChevronDown",(function(){return k})),n.d(t,"IconChevronLeft",(function(){return b})),n.d(t,"IconChevronRight",(function(){return m})),n.d(t,"IconChevronUp",(function(){return w})),n.d(t,"IconClipboard",(function(){return x})),n.d(t,"IconCollapse",(function(){return C})),n.d(t,"IconColor",(function(){return S})),n.d(t,"IconCopy",(function(){return T})),n.d(t,"IconCross",(function(){return E})),n.d(t,"IconCurlyBrackets",(function(){return B})),n.d(t,"IconDelimiter",(function(){return M})),n.d(t,"IconDirectionDownRight",(function(){return _})),n.d(t,"IconDirectionLeftDown",(function(){return O})),n.d(t,"IconDirectionRightDown",(function(){return I})),n.d(t,"IconDirectionUpRight",(function(){return L})),n.d(t,"IconDotCircle",(function(){return P})),n.d(t,"IconEtcHorisontal",(function(){return j})),n.d(t,"IconEtcVertical",(function(){return R})),n.d(t,"IconFile",(function(){return A})),n.d(t,"IconGift",(function(){return N})),n.d(t,"IconGlobe",(function(){return D})),n.d(t,"IconH1",(function(){return o})),n.d(t,"IconH2",(function(){return r})),n.d(t,"IconH3",(function(){return i})),n.d(t,"IconH4",(function(){return a})),n.d(t,"IconH5",(function(){return s})),n.d(t,"IconH6",(function(){return l})),n.d(t,"IconHeading",(function(){return F})),n.d(t,"IconHeart",(function(){return H})),n.d(t,"IconHidden",(function(){return W})),n.d(t,"IconHtml",(function(){return U})),n.d(t,"IconInstagram",(function(){return z})),n.d(t,"IconItalic",(function(){return V})),n.d(t,"IconLink",(function(){return Y})),n.d(t,"IconLinkedin",(function(){return X})),n.d(t,"IconListBulleted",(function(){return G})),n.d(t,"IconListNumbered",(function(){return K})),n.d(t,"IconMarker",(function(){return Z})),n.d(t,"IconMenu",(function(){return J})),n.d(t,"IconMenuSmall",(function(){return q})),n.d(t,"IconPicture",(function(){return $})),n.d(t,"IconPlay",(function(){return Q})),n.d(t,"IconPlus",(function(){return ee})),n.d(t,"IconQuestion",(function(){return te})),n.d(t,"IconQuote",(function(){return ne})),n.d(t,"IconRedo",(function(){return oe})),n.d(t,"IconRemoveBackground",(function(){return re})),n.d(t,"IconReplace",(function(){return ie})),n.d(t,"IconSave",(function(){return ae})),n.d(t,"IconSearch",(function(){return se})),n.d(t,"IconStar",(function(){return le})),n.d(t,"IconStretch",(function(){return ce})),n.d(t,"IconStrikethrough",(function(){return ue})),n.d(t,"IconTable",(function(){return pe})),n.d(t,"IconTableWithHeadings",(function(){return fe})),n.d(t,"IconTableWithoutHeadings",(function(){return de})),n.d(t,"IconText",(function(){return he})),n.d(t,"IconTranslate",(function(){return ve})),n.d(t,"IconTrash",(function(){return ge})),n.d(t,"IconTwitter",(function(){return ye})),n.d(t,"IconUnderline",(function(){return ke})),n.d(t,"IconUndo",(function(){return be})),n.d(t,"IconUnlink",(function(){return me})),n.d(t,"IconUser",(function(){return we})),n.d(t,"IconUsersGroup",(function(){return xe})),n.d(t,"IconWarning",(function(){return Ce}));const o='',r='',i='',a='',s='',l='',c='',u='',f='',d='',p='',h='',v='',g='',y='',k='',b='',m='',w='',x='',C='',S='',T='',E='',B='',M='',_='',O='',I='',L='',P='',j='',R='',A='',N='',D='',F='',H='',W='',U='',z='',V='',Y='',X='',G='',K='',Z='',q='',J='',$='',Q='',ee='',te='',ne='',oe='',re='',ie='',ae='',se='',le='',ce='',ue='',fe='',de='',pe='',he='',ve='',ge='',ye='',ke='',be='',me='',we='',xe='',Ce=''},function(e,t){var n={}.toString;e.exports=function(e){return n.call(e).slice(8,-1)}},function(e,t){e.exports=function(e){if(null==e)throw TypeError("Can't call method on "+e);return e}},function(e,t,n){"use strict";if(n(17)){var o=n(46),r=n(10),i=n(11),a=n(0),s=n(81),l=n(110),c=n(31),u=n(59),f=n(44),d=n(27),p=n(60),h=n(33),v=n(15),g=n(144),y=n(48),k=n(42),b=n(26),m=n(64),w=n(13),x=n(22),C=n(102),S=n(49),T=n(51),E=n(50).f,B=n(104),M=n(45),_=n(14),O=n(36),I=n(71),L=n(65),P=n(106),j=n(57),R=n(74),A=n(58),N=n(105),D=n(135),F=n(18),H=n(34),W=F.f,U=H.f,z=r.RangeError,V=r.TypeError,Y=r.Uint8Array,X=Array.prototype,G=l.ArrayBuffer,K=l.DataView,Z=O(0),q=O(2),J=O(3),$=O(4),Q=O(5),ee=O(6),te=I(!0),ne=I(!1),oe=P.values,re=P.keys,ie=P.entries,ae=X.lastIndexOf,se=X.reduce,le=X.reduceRight,ce=X.join,ue=X.sort,fe=X.slice,de=X.toString,pe=X.toLocaleString,he=_("iterator"),ve=_("toStringTag"),ge=M("typed_constructor"),ye=M("def_constructor"),ke=s.CONSTR,be=s.TYPED,me=s.VIEW,we=O(1,(function(e,t){return Ee(L(e,e[ye]),t)})),xe=i((function(){return 1===new Y(new Uint16Array([1]).buffer)[0]})),Ce=!!Y&&!!Y.prototype.set&&i((function(){new Y(1).set({})})),Se=function(e,t){var n=h(e);if(n<0||n%t)throw z("Wrong offset!");return n},Te=function(e){if(w(e)&&be in e)return e;throw V(e+" is not a typed array!")},Ee=function(e,t){if(!w(e)||!(ge in e))throw V("It is not a typed array constructor!");return new e(t)},Be=function(e,t){return Me(L(e,e[ye]),t)},Me=function(e,t){for(var n=0,o=t.length,r=Ee(e,o);o>n;)r[n]=t[n++];return r},_e=function(e,t,n){W(e,t,{get:function(){return this._d[n]}})},Oe=function(e){var t,n,o,r,i,a,s=x(e),l=arguments.length,u=l>1?arguments[1]:void 0,f=void 0!==u,d=B(s);if(null!=d&&!C(d)){for(a=d.call(s),o=[],t=0;!(i=a.next()).done;t++)o.push(i.value);s=o}for(f&&l>2&&(u=c(u,arguments[2],2)),t=0,n=v(s.length),r=Ee(this,n);n>t;t++)r[t]=f?u(s[t],t):s[t];return r},Ie=function(){for(var e=0,t=arguments.length,n=Ee(this,t);t>e;)n[e]=arguments[e++];return n},Le=!!Y&&i((function(){pe.call(new Y(1))})),Pe=function(){return pe.apply(Le?fe.call(Te(this)):Te(this),arguments)},je={copyWithin:function(e,t){return D.call(Te(this),e,t,arguments.length>2?arguments[2]:void 0)},every:function(e){return $(Te(this),e,arguments.length>1?arguments[1]:void 0)},fill:function(e){return N.apply(Te(this),arguments)},filter:function(e){return Be(this,q(Te(this),e,arguments.length>1?arguments[1]:void 0))},find:function(e){return Q(Te(this),e,arguments.length>1?arguments[1]:void 0)},findIndex:function(e){return ee(Te(this),e,arguments.length>1?arguments[1]:void 0)},forEach:function(e){Z(Te(this),e,arguments.length>1?arguments[1]:void 0)},indexOf:function(e){return ne(Te(this),e,arguments.length>1?arguments[1]:void 0)},includes:function(e){return te(Te(this),e,arguments.length>1?arguments[1]:void 0)},join:function(e){return ce.apply(Te(this),arguments)},lastIndexOf:function(e){return ae.apply(Te(this),arguments)},map:function(e){return we(Te(this),e,arguments.length>1?arguments[1]:void 0)},reduce:function(e){return se.apply(Te(this),arguments)},reduceRight:function(e){return le.apply(Te(this),arguments)},reverse:function(){for(var e,t=Te(this).length,n=Math.floor(t/2),o=0;o1?arguments[1]:void 0)},sort:function(e){return ue.call(Te(this),e)},subarray:function(e,t){var n=Te(this),o=n.length,r=y(e,o);return new(L(n,n[ye]))(n.buffer,n.byteOffset+r*n.BYTES_PER_ELEMENT,v((void 0===t?o:y(t,o))-r))}},Re=function(e,t){return Be(this,fe.call(Te(this),e,t))},Ae=function(e){Te(this);var t=Se(arguments[1],1),n=this.length,o=x(e),r=v(o.length),i=0;if(r+t>n)throw z("Wrong length!");for(;i255?255:255&o),r.v[p](n*t+r.o,o,xe)}(this,n,e)},enumerable:!0})};b?(h=n((function(e,n,o,r){u(e,h,c,"_d");var i,a,s,l,f=0,p=0;if(w(n)){if(!(n instanceof G||"ArrayBuffer"==(l=m(n))||"SharedArrayBuffer"==l))return be in n?Me(h,n):Oe.call(h,n);i=n,p=Se(o,t);var y=n.byteLength;if(void 0===r){if(y%t)throw z("Wrong length!");if((a=y-p)<0)throw z("Wrong length!")}else if((a=v(r)*t)+p>y)throw z("Wrong length!");s=a/t}else s=g(n),i=new G(a=s*t);for(d(e,"_d",{b:i,o:p,l:a,e:s,v:new K(i)});fdocument.F=Object<\/script>"),e.close(),l=e.F;o--;)delete l.prototype[i[o]];return l()};e.exports=Object.create||function(e,t){var n;return null!==e?(s.prototype=o(e),n=new s,s.prototype=null,n[a]=e):n=l(),void 0===t?n:r(n,t)}},function(e,t,n){var o=n(119),r=n(89).concat("length","prototype");t.f=Object.getOwnPropertyNames||function(e){return o(e,r)}},function(e,t,n){var o=n(26),r=n(22),i=n(88)("IE_PROTO"),a=Object.prototype;e.exports=Object.getPrototypeOf||function(e){return e=r(e),o(e,i)?e[i]:"function"==typeof e.constructor&&e instanceof e.constructor?e.constructor.prototype:e instanceof Object?a:null}},function(e,t,n){var o=n(14)("unscopables"),r=Array.prototype;null==r[o]&&n(27)(r,o,{}),e.exports=function(e){r[o][e]=!0}},function(e,t,n){var o=n(13);e.exports=function(e,t){if(!o(e)||e._t!==t)throw TypeError("Incompatible receiver, "+t+" required!");return e}},function(e,t,n){var o,r,i;"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self&&self,r=[t,n(2),n(3),n(150)],void 0===(i="function"==typeof(o=function(o,r,i,a){"use strict";var s=n(1);Object.defineProperty(o,"__esModule",{value:!0}),o.default=void 0,r=s(r),i=s(i),a=s(a);var l=function(){function e(){(0,r.default)(this,e)}return(0,i.default)(e,null,[{key:"ui",value:function(t,n){return e._t(t,n)}},{key:"t",value:function(t,n){return e._t(t,n)}},{key:"setDictionary",value:function(t){e.currentDictionary=t}},{key:"_t",value:function(t,n){var o=e.getNamespace(t);return o&&o[n]?o[n]:n}},{key:"getNamespace",value:function(t){return t.split(".").reduce((function(e,t){return e&&Object.keys(e).length?e[t]:{}}),e.currentDictionary)}}]),e}();o.default=l,l.displayName="I18n",l.currentDictionary=a.default,e.exports=t.default})?o.apply(t,r):o)||(e.exports=i)},function(e,t,n){var o=n(18).f,r=n(26),i=n(14)("toStringTag");e.exports=function(e,t,n){e&&!r(e=n?e:e.prototype,i)&&o(e,i,{configurable:!0,value:t})}},function(e,t,n){var o=n(0),r=n(39),i=n(11),a=n(92),s="["+a+"]",l=RegExp("^"+s+s+"*"),c=RegExp(s+s+"*$"),u=function(e,t,n){var r={},s=i((function(){return!!a[e]()||"​…"!="​…"[e]()})),l=r[e]=s?t(f):a[e];n&&(r[n]=l),o(o.P+o.F*s,"String",r)},f=u.trim=function(e,t){return e=String(r(e)),1&t&&(e=e.replace(l,"")),2&t&&(e=e.replace(c,"")),e};e.exports=u},function(e,t){e.exports={}},function(e,t,n){"use strict";var o=n(10),r=n(18),i=n(17),a=n(14)("species");e.exports=function(e){var t=o[e];i&&t&&!t[a]&&r.f(t,a,{configurable:!0,get:function(){return this}})}},function(e,t){e.exports=function(e,t,n,o){if(!(e instanceof t)||void 0!==o&&o in e)throw TypeError(n+": incorrect invocation!");return e}},function(e,t,n){var o=n(23);e.exports=function(e,t,n){for(var r in t)o(e,r,t[r],n);return e}},function(e,t,n){var o,r,i,a=n(7);"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self&&self,r=[t,n(20),n(30),n(21),n(41),n(2),n(3),n(152),n(114),n(5),n(6),n(4),n(19),n(8),n(113),n(25),n(82)],void 0===(i="function"==typeof(o=function(e,t,o,r,i,s,l,c,u,f,d,p,h,v,g,y,k){"use strict";var b,m=n(1);function w(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(w=function(e){return e?n:t})(e)}function x(e){var t=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}();return function(){var n,o=(0,p.default)(e);if(t){var r=(0,p.default)(this).constructor;n=Reflect.construct(o,arguments,r)}else n=o.apply(this,arguments);return(0,d.default)(this,n)}}Object.defineProperty(e,"__esModule",{value:!0}),e.default=e.BlockToolAPI=void 0,t=m(t),o=m(o),r=m(r),i=m(i),s=m(s),l=m(l),c=m(c),u=m(u),f=m(f),d=m(d),p=m(p),h=m(h),v=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!==a(e)&&"function"!=typeof e)return{default:e};var n=w(t);if(n&&n.has(e))return n.get(e);var o={},r=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var i in e)if("default"!==i&&Object.prototype.hasOwnProperty.call(e,i)){var s=r?Object.getOwnPropertyDescriptor(e,i):null;s&&(s.get||s.set)?Object.defineProperty(o,i,s):o[i]=e[i]}return o.default=e,n&&n.set(e,o),o}(v),g=m(g),y=m(y),k=m(k),e.BlockToolAPI=b,function(e){e.APPEND_CALLBACK="appendCallback",e.RENDERED="rendered",e.MOVED="moved",e.UPDATED="updated",e.REMOVED="removed",e.ON_PASTE="onPaste"}(b||(e.BlockToolAPI=b={}));var C=function(e){(0,f.default)(w,e);var n,a,d,k,m=x(w);function w(e){var t,n=e.id,o=void 0===n?v.generateBlockId():n,r=e.data,a=e.tool,l=e.api,u=e.readOnly,f=e.tunesData;return(0,s.default)(this,w),(t=m.call(this)).cachedInputs=[],t.tunesInstances=new Map,t.defaultTunesInstances=new Map,t.unavailableTunesData={},t.inputIndex=0,t.modificationDebounceTimer=450,t.didMutated=v.debounce((function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[],n=e instanceof InputEvent||!e.some((function(e){var t=e.addedNodes,n=void 0===t?[]:t,o=e.removedNodes;return[].concat((0,i.default)(Array.from(n)),(0,i.default)(Array.from(o))).some((function(e){return h.default.isElement(e)&&"true"===e.dataset.mutationFree}))}));n&&(t.cachedInputs=[],t.updateCurrentInput(),t.call(b.UPDATED),t.emit("didMutated",(0,c.default)(t)))}),t.modificationDebounceTimer),t.handleFocus=function(){t.cachedInputs=[],t.updateCurrentInput()},t.name=a.name,t.id=o,t.settings=a.settings,t.config=a.settings.config||{},t.api=l,t.blockAPI=new g.default((0,c.default)(t)),t.mutationObserver=new MutationObserver(t.didMutated),t.tool=a,t.toolInstance=a.create(r,t.blockAPI,u),t.tunes=a.tunes,t.composeTunes(f),t.holder=t.compose(),t}return(0,l.default)(w,[{key:"inputs",get:function(){if(0!==this.cachedInputs.length)return this.cachedInputs;var e=h.default.findAllInputs(this.holder);return this.inputIndex>e.length-1&&(this.inputIndex=e.length-1),this.cachedInputs=e,e}},{key:"currentInput",get:function(){return this.inputs[this.inputIndex]},set:function(e){var t=this.inputs.findIndex((function(t){return t===e||t.contains(e)}));-1!==t&&(this.inputIndex=t)}},{key:"firstInput",get:function(){return this.inputs[0]}},{key:"lastInput",get:function(){var e=this.inputs;return e[e.length-1]}},{key:"nextInput",get:function(){return this.inputs[this.inputIndex+1]}},{key:"previousInput",get:function(){return this.inputs[this.inputIndex-1]}},{key:"data",get:function(){return this.save().then((function(e){return e&&!v.isEmpty(e.data)?e.data:{}}))}},{key:"sanitize",get:function(){return this.tool.sanitizeConfig}},{key:"mergeable",get:function(){return v.isFunction(this.toolInstance.merge)}},{key:"isEmpty",get:function(){var e=h.default.isEmpty(this.pluginsContent),t=!this.hasMedia;return e&&t}},{key:"hasMedia",get:function(){return!!this.holder.querySelector(["img","iframe","video","audio","source","input","textarea","twitterwidget"].join(","))}},{key:"focused",get:function(){return this.holder.classList.contains(w.CSS.focused)},set:function(e){this.holder.classList.toggle(w.CSS.focused,e)}},{key:"selected",get:function(){return this.holder.classList.contains(w.CSS.selected)},set:function(e){e?(this.holder.classList.add(w.CSS.selected),y.default.addFakeCursor(this.holder)):(this.holder.classList.remove(w.CSS.selected),y.default.removeFakeCursor(this.holder))}},{key:"stretched",get:function(){return this.holder.classList.contains(w.CSS.wrapperStretched)},set:function(e){this.holder.classList.toggle(w.CSS.wrapperStretched,e)}},{key:"dropTarget",set:function(e){this.holder.classList.toggle(w.CSS.dropTarget,e)}},{key:"pluginsContent",get:function(){var e=this.holder.querySelector(".".concat(w.CSS.content));if(e&&e.childNodes.length)for(var t=e.childNodes.length-1;t>=0;t--){var n=e.childNodes[t];if(!h.default.isExtensionNode(n))return n}return null}},{key:"call",value:function(e,t){if(v.isFunction(this.toolInstance[e])){e===b.APPEND_CALLBACK&&v.log("`appendCallback` hook is deprecated and will be removed in the next major release. Use `rendered` hook instead","warn");try{this.toolInstance[e].call(this.toolInstance,t)}catch(t){v.log("Error during '".concat(e,"' call: ").concat(t.message),"error")}}}},{key:"mergeWith",value:(k=(0,r.default)(t.default.mark((function e(n){return t.default.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,this.toolInstance.merge(n);case 2:case"end":return e.stop()}}),e,this)}))),function(e){return k.apply(this,arguments)})},{key:"save",value:(d=(0,r.default)(t.default.mark((function e(){var n,r,a,s,l=this;return t.default.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,this.toolInstance.save(this.pluginsContent);case 2:return n=e.sent,r=this.unavailableTunesData,[].concat((0,i.default)(this.tunesInstances.entries()),(0,i.default)(this.defaultTunesInstances.entries())).forEach((function(e){var t=(0,o.default)(e,2),n=t[0],i=t[1];if(v.isFunction(i.save))try{r[n]=i.save()}catch(e){v.log("Tune ".concat(i.constructor.name," save method throws an Error %o"),"warn",e)}})),a=window.performance.now(),e.abrupt("return",Promise.resolve(n).then((function(e){return s=window.performance.now(),{id:l.id,tool:l.name,data:e,tunes:r,time:s-a}})).catch((function(e){v.log("Saving process for ".concat(l.name," tool failed due to the ").concat(e),"log","red")})));case 7:case"end":return e.stop()}}),e,this)}))),function(){return d.apply(this,arguments)})},{key:"validate",value:(a=(0,r.default)(t.default.mark((function e(n){var o;return t.default.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(o=!0,!(this.toolInstance.validate instanceof Function)){e.next=5;break}return e.next=4,this.toolInstance.validate(n);case 4:o=e.sent;case 5:return e.abrupt("return",o);case 6:case"end":return e.stop()}}),e,this)}))),function(e){return a.apply(this,arguments)})},{key:"getTunes",value:function(){var e=document.createElement("div"),t=[];return["function"==typeof this.toolInstance.renderSettings?this.toolInstance.renderSettings():[],[].concat((0,i.default)(this.tunesInstances.values()),(0,i.default)(this.defaultTunesInstances.values())).map((function(e){return e.render()}))].flat().forEach((function(n){h.default.isElement(n)?e.appendChild(n):Array.isArray(n)?t.push.apply(t,(0,i.default)(n)):t.push(n)})),[t,e]}},{key:"updateCurrentInput",value:function(){this.currentInput=h.default.isNativeInput(document.activeElement)||!y.default.anchorNode?document.activeElement:y.default.anchorNode}},{key:"willSelect",value:function(){this.mutationObserver.observe(this.holder.firstElementChild,{childList:!0,subtree:!0,characterData:!0,attributes:!0}),this.addInputEvents()}},{key:"willUnselect",value:function(){this.mutationObserver.disconnect(),this.removeInputEvents()}},{key:"dispatchChange",value:function(){this.didMutated()}},{key:"destroy",value:function(){(0,u.default)((0,p.default)(w.prototype),"destroy",this).call(this),v.isFunction(this.toolInstance.destroy)&&this.toolInstance.destroy()}},{key:"getActiveToolboxEntry",value:(n=(0,r.default)(t.default.mark((function e(){var n,r,i;return t.default.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(1!==(n=this.tool.toolbox).length){e.next=3;break}return e.abrupt("return",Promise.resolve(this.tool.toolbox[0]));case 3:return e.next=5,this.data;case 5:return r=e.sent,i=n,e.abrupt("return",i.find((function(e){return Object.entries(e.data).some((function(e){var t=(0,o.default)(e,2),n=t[0],i=t[1];return r[n]&&v.equals(r[n],i)}))})));case 8:case"end":return e.stop()}}),e,this)}))),function(){return n.apply(this,arguments)})},{key:"compose",value:function(){var e=h.default.make("div",w.CSS.wrapper),t=h.default.make("div",w.CSS.content),n=this.toolInstance.render();t.appendChild(n);var o=t;return[].concat((0,i.default)(this.tunesInstances.values()),(0,i.default)(this.defaultTunesInstances.values())).forEach((function(e){if(v.isFunction(e.wrap))try{o=e.wrap(o)}catch(t){v.log("Tune ".concat(e.constructor.name," wrap method throws an Error %o"),"warn",t)}})),e.appendChild(o),e}},{key:"composeTunes",value:function(e){var t=this;Array.from(this.tunes.values()).forEach((function(n){(n.isInternal?t.defaultTunesInstances:t.tunesInstances).set(n.name,n.create(e[n.name],t.blockAPI))})),Object.entries(e).forEach((function(e){var n=(0,o.default)(e,2),r=n[0],i=n[1];t.tunesInstances.has(r)||(t.unavailableTunesData[r]=i)}))}},{key:"addInputEvents",value:function(){var e=this;this.inputs.forEach((function(t){t.addEventListener("focus",e.handleFocus),h.default.isNativeInput(t)&&t.addEventListener("input",e.didMutated)}))}},{key:"removeInputEvents",value:function(){var e=this;this.inputs.forEach((function(t){t.removeEventListener("focus",e.handleFocus),h.default.isNativeInput(t)&&t.removeEventListener("input",e.didMutated)}))}}],[{key:"CSS",get:function(){return{wrapper:"ce-block",wrapperStretched:"ce-block--stretched",content:"ce-block__content",focused:"ce-block--focused",selected:"ce-block--selected",dropTarget:"ce-block--drop-target"}}}]),w}(k.default);e.default=C,C.displayName="Block"})?o.apply(t,r):o)||(e.exports=i)},function(e,t,n){var o=n(38);e.exports=Object("z").propertyIsEnumerable(0)?Object:function(e){return"String"==o(e)?e.split(""):Object(e)}},function(e,t){t.f={}.propertyIsEnumerable},function(e,t,n){var o=n(38),r=n(14)("toStringTag"),i="Arguments"==o(function(){return arguments}());e.exports=function(e){var t,n,a;return void 0===e?"Undefined":null===e?"Null":"string"==typeof(n=function(e,t){try{return e[t]}catch(e){}}(t=Object(e),r))?n:i?o(t):"Object"==(a=o(t))&&"function"==typeof t.callee?"Arguments":a}},function(e,t,n){var o=n(12),r=n(32),i=n(14)("species");e.exports=function(e,t){var n,a=o(e).constructor;return void 0===a||null==(n=o(a)[i])?t:r(n)}},function(e,t,n){var o,r,i,a=n(7);"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self&&self,r=[t,n(8),n(380)],void 0===(i="function"==typeof(o=function(e,t,o){"use strict";var r=n(1);function i(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(i=function(e){return e?n:t})(e)}function s(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n={tags:t},r=new o.default(n);return r.clean(e)}function l(e,n){return Array.isArray(e)?(i=n,e.map((function(e){return l(e,i)}))):t.isObject(e)?function(e,n){var o,r={};for(var i in e)if(Object.prototype.hasOwnProperty.call(e,i)){var a=e[i],s=(o=n[i],t.isObject(o)||t.isBoolean(o)||t.isFunction(o)?n[i]:n);r[i]=l(a,s)}return r}(e,n):t.isString(e)?(o=e,r=n,t.isObject(r)?s(o,r):!1===r?s(o,{}):o):e;var o,r,i}Object.defineProperty(e,"__esModule",{value:!0}),e.clean=s,e.sanitizeBlocks=function(e,n){return e.map((function(e){var o=t.isFunction(n)?n(e.tool):n;return t.isEmpty(o)||(e.data=l(e.data,o)),e}))},t=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!==a(e)&&"function"!=typeof e)return{default:e};var n=i(t);if(n&&n.has(e))return n.get(e);var o={},r=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var s in e)if("default"!==s&&Object.prototype.hasOwnProperty.call(e,s)){var l=r?Object.getOwnPropertyDescriptor(e,s):null;l&&(l.get||l.set)?Object.defineProperty(o,s,l):o[s]=e[s]}return o.default=e,n&&n.set(e,o),o}(t),o=r(o)})?o.apply(t,r):o)||(e.exports=i)},function(e,t,n){var o,r,i,a=n(7);"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self&&self,r=[t,n(2),n(3),n(382),n(8)],void 0===(i="function"==typeof(o=function(o,r,i,s,l){"use strict";var c=n(1);function u(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(u=function(e){return e?n:t})(e)}Object.defineProperty(o,"__esModule",{value:!0}),o.default=void 0,r=c(r),i=c(i),s=c(s),l=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!==a(e)&&"function"!=typeof e)return{default:e};var n=u(t);if(n&&n.has(e))return n.get(e);var o={},r=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var i in e)if("default"!==i&&Object.prototype.hasOwnProperty.call(e,i)){var s=r?Object.getOwnPropertyDescriptor(e,i):null;s&&(s.get||s.set)?Object.defineProperty(o,i,s):o[i]=e[i]}return o.default=e,n&&n.set(e,o),o}(l);var f=function(){function e(t){var n=this;(0,r.default)(this,e),this.iterator=null,this.activated=!1,this.flipCallbacks=[],this.onKeyDown=function(t){if(n.isEventReadyForHandling(t))switch(e.usedKeys.includes(t.keyCode)&&t.preventDefault(),t.keyCode){case l.keyCodes.TAB:n.handleTabPress(t);break;case l.keyCodes.LEFT:case l.keyCodes.UP:n.flipLeft();break;case l.keyCodes.RIGHT:case l.keyCodes.DOWN:n.flipRight();break;case l.keyCodes.ENTER:n.handleEnterPress(t)}},this.iterator=new s.default(t.items,t.focusedItemClass),this.activateCallback=t.activateCallback,this.allowedKeys=t.allowedKeys||e.usedKeys}return(0,i.default)(e,[{key:"isActivated",get:function(){return this.activated}},{key:"activate",value:function(e,t){this.activated=!0,e&&this.iterator.setItems(e),void 0!==t&&this.iterator.setCursor(t),document.addEventListener("keydown",this.onKeyDown,!0)}},{key:"deactivate",value:function(){this.activated=!1,this.dropCursor(),document.removeEventListener("keydown",this.onKeyDown)}},{key:"focusFirst",value:function(){this.dropCursor(),this.flipRight()}},{key:"flipLeft",value:function(){this.iterator.previous(),this.flipCallback()}},{key:"flipRight",value:function(){this.iterator.next(),this.flipCallback()}},{key:"hasFocus",value:function(){return!!this.iterator.currentItem}},{key:"onFlip",value:function(e){this.flipCallbacks.push(e)}},{key:"removeOnFlip",value:function(e){this.flipCallbacks=this.flipCallbacks.filter((function(t){return t!==e}))}},{key:"dropCursor",value:function(){this.iterator.dropCursor()}},{key:"isEventReadyForHandling",value:function(e){return this.activated&&this.allowedKeys.includes(e.keyCode)}},{key:"handleTabPress",value:function(e){switch(e.shiftKey?s.default.directions.LEFT:s.default.directions.RIGHT){case s.default.directions.RIGHT:this.flipRight();break;case s.default.directions.LEFT:this.flipLeft()}}},{key:"handleEnterPress",value:function(e){this.activated&&(this.iterator.currentItem&&(e.stopPropagation(),e.preventDefault(),this.iterator.currentItem.click()),l.isFunction(this.activateCallback)&&this.activateCallback(this.iterator.currentItem))}},{key:"flipCallback",value:function(){this.iterator.currentItem&&this.iterator.currentItem.scrollIntoViewIfNeeded(),this.flipCallbacks.forEach((function(e){return e()}))}}],[{key:"usedKeys",get:function(){return[l.keyCodes.TAB,l.keyCodes.LEFT,l.keyCodes.RIGHT,l.keyCodes.ENTER,l.keyCodes.UP,l.keyCodes.DOWN]}}]),e}();o.default=f,f.displayName="Flipper",e.exports=t.default})?o.apply(t,r):o)||(e.exports=i)},function(e,t,n){var o,r,i;"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self&&self,r=[t,n(30),n(150),n(8)],void 0===(i="function"==typeof(o=function(e,t,o,r){"use strict";var i=n(1);Object.defineProperty(e,"__esModule",{value:!0}),e.I18nInternalNS=void 0,t=i(t);var a=function e(n,o){var i={};return Object.entries(n).forEach((function(n){var a=(0,t.default)(n,2),s=a[0],l=a[1];if((0,r.isObject)(l)){var c=o?"".concat(o,".").concat(s):s,u=Object.values(l).every((function(e){return(0,r.isString)(e)}));i[s]=u?c:e(l,c)}else i[s]=l})),i}((o=i(o)).default);e.I18nInternalNS=a})?o.apply(t,r):o)||(e.exports=i)},function(e,t,n){var o,r,i,a=n(7);"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self&&self,r=[t,n(2),n(3),n(8)],void 0===(i="function"==typeof(o=function(e,t,o,r){"use strict";var i,s,l,c,u,f,d=n(1);function p(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(p=function(e){return e?n:t})(e)}Object.defineProperty(e,"__esModule",{value:!0}),e.default=e.UserSettings=e.ToolType=e.InternalTuneSettings=e.InternalInlineToolSettings=e.InternalBlockToolSettings=e.CommonInternalSettings=void 0,t=d(t),o=d(o),r=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!==a(e)&&"function"!=typeof e)return{default:e};var n=p(t);if(n&&n.has(e))return n.get(e);var o={},r=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var i in e)if("default"!==i&&Object.prototype.hasOwnProperty.call(e,i)){var s=r?Object.getOwnPropertyDescriptor(e,i):null;s&&(s.get||s.set)?Object.defineProperty(o,i,s):o[i]=e[i]}return o.default=e,n&&n.set(e,o),o}(r),e.ToolType=i,function(e){e[e.Block=0]="Block",e[e.Inline=1]="Inline",e[e.Tune=2]="Tune"}(i||(e.ToolType=i={})),e.UserSettings=s,function(e){e.Shortcut="shortcut",e.Toolbox="toolbox",e.EnabledInlineTools="inlineToolbar",e.EnabledBlockTunes="tunes",e.Config="config"}(s||(e.UserSettings=s={})),e.CommonInternalSettings=l,function(e){e.Shortcut="shortcut",e.SanitizeConfig="sanitize"}(l||(e.CommonInternalSettings=l={})),e.InternalBlockToolSettings=c,function(e){e.IsEnabledLineBreaks="enableLineBreaks",e.Toolbox="toolbox",e.ConversionConfig="conversionConfig",e.IsReadOnlySupported="isReadOnlySupported",e.PasteConfig="pasteConfig"}(c||(e.InternalBlockToolSettings=c={})),e.InternalInlineToolSettings=u,function(e){e.IsInline="isInline",e.Title="title"}(u||(e.InternalInlineToolSettings=u={})),e.InternalTuneSettings=f,function(e){e.IsTune="isTune"}(f||(e.InternalTuneSettings=f={}));var h=function(){function e(n){var o=n.name,r=n.constructable,i=n.config,a=n.api,s=n.isDefault,l=n.isInternal,c=void 0!==l&&l,u=n.defaultPlaceholder;(0,t.default)(this,e),this.api=a,this.name=o,this.constructable=r,this.config=i,this.isDefault=s,this.isInternal=c,this.defaultPlaceholder=u}return(0,o.default)(e,[{key:"settings",get:function(){var e=this.config[s.Config]||{};return this.isDefault&&!("placeholder"in e)&&this.defaultPlaceholder&&(e.placeholder=this.defaultPlaceholder),e}},{key:"reset",value:function(){if(r.isFunction(this.constructable.reset))return this.constructable.reset()}},{key:"prepare",value:function(){if(r.isFunction(this.constructable.prepare))return this.constructable.prepare({toolName:this.name,config:this.settings})}},{key:"shortcut",get:function(){var e=this.constructable[l.Shortcut];return this.config[s.Shortcut]||e}},{key:"sanitizeConfig",get:function(){return this.constructable[l.SanitizeConfig]||{}}},{key:"isInline",value:function(){return this.type===i.Inline}},{key:"isBlock",value:function(){return this.type===i.Block}},{key:"isTune",value:function(){return this.type===i.Tune}}]),e}();e.default=h,h.displayName="BaseTool"})?o.apply(t,r):o)||(e.exports=i)},function(e,t,n){var o=n(16),r=n(10),i=r["__core-js_shared__"]||(r["__core-js_shared__"]={});(e.exports=function(e,t){return i[e]||(i[e]=void 0!==t?t:{})})("versions",[]).push({version:o.version,mode:n(46)?"pure":"global",copyright:"© 2020 Denis Pushkarev (zloirock.ru)"})},function(e,t,n){var o=n(28),r=n(15),i=n(48);e.exports=function(e){return function(t,n,a){var s,l=o(t),c=r(l.length),u=i(a,c);if(e&&n!=n){for(;c>u;)if((s=l[u++])!=s)return!0}else for(;c>u;u++)if((e||u in l)&&l[u]===n)return e||u||0;return!e&&-1}}},function(e,t){t.f=Object.getOwnPropertySymbols},function(e,t,n){var o=n(38);e.exports=Array.isArray||function(e){return"Array"==o(e)}},function(e,t,n){var o=n(14)("iterator"),r=!1;try{var i=[7][o]();i.return=function(){r=!0},Array.from(i,(function(){throw 2}))}catch(e){}e.exports=function(e,t){if(!t&&!r)return!1;var n=!1;try{var i=[7],a=i[o]();a.next=function(){return{done:n=!0}},i[o]=function(){return a},e(i)}catch(e){}return n}},function(e,t,n){"use strict";var o=n(12);e.exports=function(){var e=o(this),t="";return e.global&&(t+="g"),e.ignoreCase&&(t+="i"),e.multiline&&(t+="m"),e.unicode&&(t+="u"),e.sticky&&(t+="y"),t}},function(e,t,n){"use strict";var o=n(64),r=RegExp.prototype.exec;e.exports=function(e,t){var n=e.exec;if("function"==typeof n){var i=n.call(e,t);if("object"!=typeof i)throw new TypeError("RegExp exec method returned something other than an Object or null");return i}if("RegExp"!==o(e))throw new TypeError("RegExp#exec called on incompatible receiver");return r.call(e,t)}},function(e,t,n){"use strict";n(137);var o=n(23),r=n(27),i=n(11),a=n(39),s=n(14),l=n(107),c=s("species"),u=!i((function(){var e=/./;return e.exec=function(){var e=[];return e.groups={a:"7"},e},"7"!=="".replace(e,"$")})),f=function(){var e=/(?:)/,t=e.exec;e.exec=function(){return t.apply(this,arguments)};var n="ab".split(e);return 2===n.length&&"a"===n[0]&&"b"===n[1]}();e.exports=function(e,t,n){var d=s(e),p=!i((function(){var t={};return t[d]=function(){return 7},7!=""[e](t)})),h=p?!i((function(){var t=!1,n=/a/;return n.exec=function(){return t=!0,null},"split"===e&&(n.constructor={},n.constructor[c]=function(){return n}),n[d](""),!t})):void 0;if(!p||!h||"replace"===e&&!u||"split"===e&&!f){var v=/./[d],g=n(a,d,""[e],(function(e,t,n,o,r){return t.exec===l?p&&!r?{done:!0,value:v.call(t,n,o)}:{done:!0,value:e.call(n,t,o)}:{done:!1}})),y=g[0],k=g[1];o(String.prototype,e,y),r(RegExp.prototype,d,2==t?function(e,t){return k.call(e,this,t)}:function(e){return k.call(e,this)})}}},function(e,t,n){var o=n(31),r=n(132),i=n(102),a=n(12),s=n(15),l=n(104),c={},u={};(t=e.exports=function(e,t,n,f,d){var p,h,v,g,y=d?function(){return e}:l(e),k=o(n,f,t?2:1),b=0;if("function"!=typeof y)throw TypeError(e+" is not iterable!");if(i(y)){for(p=s(e.length);p>b;b++)if((g=t?k(a(h=e[b])[0],h[1]):k(e[b]))===c||g===u)return g}else for(v=y.call(e);!(h=v.next()).done;)if((g=r(v,k,h.value,t))===c||g===u)return g}).BREAK=c,t.RETURN=u},function(e,t,n){var o=n(10).navigator;e.exports=o&&o.userAgent||""},function(e,t,n){"use strict";var o=n(10),r=n(0),i=n(23),a=n(60),s=n(43),l=n(78),c=n(59),u=n(13),f=n(11),d=n(74),p=n(55),h=n(93);e.exports=function(e,t,n,v,g,y){var k=o[e],b=k,m=g?"set":"add",w=b&&b.prototype,x={},C=function(e){var t=w[e];i(w,e,"delete"==e||"has"==e?function(e){return!(y&&!u(e))&&t.call(this,0===e?0:e)}:"get"==e?function(e){return y&&!u(e)?void 0:t.call(this,0===e?0:e)}:"add"==e?function(e){return t.call(this,0===e?0:e),this}:function(e,n){return t.call(this,0===e?0:e,n),this})};if("function"==typeof b&&(y||w.forEach&&!f((function(){(new b).entries().next()})))){var S=new b,T=S[m](y?{}:-0,1)!=S,E=f((function(){S.has(1)})),B=d((function(e){new b(e)})),M=!y&&f((function(){for(var e=new b,t=5;t--;)e[m](t,t);return!e.has(-0)}));B||((b=t((function(t,n){c(t,b,e);var o=h(new k,t,b);return null!=n&&l(n,g,o[m],o),o}))).prototype=w,w.constructor=b),(E||M)&&(C("delete"),C("has"),g&&C("get")),(M||T)&&C(m),y&&w.clear&&delete w.clear}else b=v.getConstructor(t,e,g,m),a(b.prototype,n),s.NEED=!0;return p(b,e),x[e]=b,r(r.G+r.W+r.F*(b!=k),x),y||v.setStrong(b,e,g),b}},function(e,t,n){for(var o,r=n(10),i=n(27),a=n(45),s=a("typed_array"),l=a("view"),c=!(!r.ArrayBuffer||!r.DataView),u=c,f=0,d="Int8Array,Uint8Array,Uint8ClampedArray,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array".split(",");f<9;)(o=r[d[f++]])?(i(o.prototype,s,!0),i(o.prototype,l,!0)):u=!1;e.exports={ABV:c,CONSTR:u,TYPED:s,VIEW:l}},function(e,t,n){var o,r,i;"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self&&self,r=[t,n(2),n(3),n(8)],void 0===(i="function"==typeof(o=function(o,r,i,a){"use strict";var s=n(1);Object.defineProperty(o,"__esModule",{value:!0}),o.default=void 0,r=s(r),i=s(i);var l=function(){function e(){(0,r.default)(this,e),this.subscribers={}}return(0,i.default)(e,[{key:"on",value:function(e,t){e in this.subscribers||(this.subscribers[e]=[]),this.subscribers[e].push(t)}},{key:"once",value:function(e,t){var n=this;e in this.subscribers||(this.subscribers[e]=[]),this.subscribers[e].push((function o(r){var i=t(r),a=n.subscribers[e].indexOf(o);return-1!==a&&n.subscribers[e].splice(a,1),i}))}},{key:"emit",value:function(e,t){!(0,a.isEmpty)(this.subscribers)&&this.subscribers[e]&&this.subscribers[e].reduce((function(e,t){return t(e)||e}),t)}},{key:"off",value:function(e,t){for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:this.Editor.BlockManager.currentBlock;if(this.toolboxInstance.close(),this.Editor.BlockSettings.close(),e){this.hoveredBlock=e;var t,n=e.holder,o=this.Editor.UI.isMobile,r=e.pluginsContent,i=window.getComputedStyle(r),a=parseInt(i.paddingTop,10),s=n.offsetHeight;t=o?n.offsetTop+s:n.offsetTop+a,this.nodes.wrapper.style.top="".concat(Math.floor(t),"px"),1===this.Editor.BlockManager.blocks.length&&e.isEmpty?this.blockTunesToggler.hide():this.blockTunesToggler.show(),this.open()}}},{key:"close",value:function(){this.Editor.ReadOnly.isEnabled||(this.nodes.wrapper.classList.remove(this.CSS.toolbarOpened),this.blockActions.hide(),this.toolboxInstance.close(),this.Editor.BlockSettings.close())}},{key:"open",value:function(){var e=this,t=!(arguments.length>0&&void 0!==arguments[0])||arguments[0];d.delay((function(){e.nodes.wrapper.classList.add(e.CSS.toolbarOpened),t?e.blockActions.show():e.blockActions.hide()}),50)()}},{key:"make",value:function(){var e=this;this.nodes.wrapper=f.default.make("div",this.CSS.toolbar),["content","actions"].forEach((function(t){e.nodes[t]=f.default.make("div",e.CSS[t])})),f.default.append(this.nodes.wrapper,this.nodes.content),f.default.append(this.nodes.content,this.nodes.actions),this.nodes.plusButton=f.default.make("div",this.CSS.plusButton,{innerHTML:y.IconPlus}),f.default.append(this.nodes.actions,this.nodes.plusButton),this.readOnlyMutableListeners.on(this.nodes.plusButton,"click",(function(){e.tooltip.hide(!0),e.plusButtonClicked()}),!1);var t=f.default.make("div");t.appendChild(document.createTextNode(p.default.ui(h.I18nInternalNS.ui.toolbar.toolbox,"Add"))),t.appendChild(f.default.make("div",this.CSS.plusButtonShortcut,{textContent:"⇥ Tab"})),this.tooltip.onHover(this.nodes.plusButton,t,{hidingDelay:400}),this.nodes.settingsToggler=f.default.make("span",this.CSS.settingsToggler,{innerHTML:y.IconMenu}),f.default.append(this.nodes.actions,this.nodes.settingsToggler),this.tooltip.onHover(this.nodes.settingsToggler,p.default.ui(h.I18nInternalNS.ui.blockTunes.toggler,"Click to tune"),{hidingDelay:400}),f.default.append(this.nodes.actions,this.makeToolbox()),f.default.append(this.nodes.actions,this.Editor.BlockSettings.getElement()),f.default.append(this.Editor.UI.nodes.wrapper,this.nodes.wrapper)}},{key:"makeToolbox",value:function(){var e=this;return this.toolboxInstance=new g.default({api:this.Editor.API.methods,tools:this.Editor.Tools.blockTools,i18nLabels:{filter:p.default.ui(h.I18nInternalNS.ui.popover,"Filter"),nothingFound:p.default.ui(h.I18nInternalNS.ui.popover,"Nothing found")}}),this.toolboxInstance.on(g.ToolboxEvent.Opened,(function(){e.Editor.UI.nodes.wrapper.classList.add(e.CSS.openedToolboxHolderModifier)})),this.toolboxInstance.on(g.ToolboxEvent.Closed,(function(){e.Editor.UI.nodes.wrapper.classList.remove(e.CSS.openedToolboxHolderModifier)})),this.toolboxInstance.on(g.ToolboxEvent.BlockAdded,(function(t){var n=t.block,o=e.Editor,r=o.BlockManager,i=o.Caret,a=r.getBlockById(n.id);0===a.inputs.length&&(a===r.lastBlock?(r.insertAtEnd(),i.setToBlock(r.lastBlock)):i.setToBlock(r.nextBlock))})),this.toolboxInstance.make()}},{key:"plusButtonClicked",value:function(){this.Editor.BlockManager.currentBlock=this.hoveredBlock,this.toolboxInstance.toggle()}},{key:"enableModuleBindings",value:function(){var e=this;this.readOnlyMutableListeners.on(this.nodes.settingsToggler,"mousedown",(function(t){t.stopPropagation(),e.settingsTogglerClicked(),e.toolboxInstance.close(),e.tooltip.hide(!0)}),!0),d.isMobileScreen()||this.eventsDispatcher.on(this.Editor.UI.events.blockHovered,(function(t){e.Editor.BlockSettings.opened||e.toolboxInstance.opened||e.moveAndOpen(t.block)}))}},{key:"disableModuleBindings",value:function(){this.readOnlyMutableListeners.clearAll()}},{key:"settingsTogglerClicked",value:function(){this.Editor.BlockManager.currentBlock=this.hoveredBlock,this.Editor.BlockSettings.opened?this.Editor.BlockSettings.close():this.Editor.BlockSettings.open(this.hoveredBlock)}},{key:"drawUI",value:function(){this.Editor.BlockSettings.make(),this.make()}},{key:"destroy",value:function(){this.removeAllNodes(),this.toolboxInstance&&this.toolboxInstance.destroy(),this.tooltip.destroy()}}]),n}(u.default);o.default=x,x.displayName="Toolbar",e.exports=t.default})?o.apply(t,r):o)||(e.exports=i)},function(e,t,n){var o,r,i;"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self&&self,r=[t,n(41),n(2),n(3),n(5),n(6),n(4),n(7),n(19),n(112),n(67),n(387),n(82),n(8),n(388),n(37)],void 0===(i="function"==typeof(o=function(e,t,o,r,i,a,s,l,c,u,f,d,p,h,v,g){"use strict";var y=n(1);function k(e){var t=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}();return function(){var n,o=(0,s.default)(e);if(t){var r=(0,s.default)(this).constructor;n=Reflect.construct(o,arguments,r)}else n=o.apply(this,arguments);return(0,a.default)(this,n)}}Object.defineProperty(e,"__esModule",{value:!0}),e.default=e.PopoverEvent=void 0,t=y(t),o=y(o),r=y(r),i=y(i),a=y(a),s=y(s),l=y(l),c=y(c),u=y(u),f=y(f),d=y(d),p=y(p),v=y(v);var b,m=function(e,t,n,o){var r,i=arguments.length,a=i<3?t:null===o?o=Object.getOwnPropertyDescriptor(t,n):o;if("object"===("undefined"==typeof Reflect?"undefined":(0,l.default)(Reflect))&&"function"==typeof Reflect.decorate)a=Reflect.decorate(e,t,n,o);else for(var s=e.length-1;s>=0;s--)(r=e[s])&&(a=(i<3?r(a):i>3?r(t,n,a):r(t,n))||a);return i>3&&a&&Object.defineProperty(t,n,a),a};e.PopoverEvent=b,function(e){e.OverlayClicked="overlay-clicked",e.Close="close"}(b||(e.PopoverEvent=b={}));var w=function(e){(0,i.default)(a,e);var n=k(a);function a(e){var t,r=e.items,i=e.className,s=e.searchable,l=e.filterLabel,c=e.nothingFoundLabel,f=e.customContent,d=e.customContentFlippableItems,p=e.scopeElement;return(0,o.default)(this,a),(t=n.call(this)).customContentFlippableItems=[],t.isShown=!1,t.nodes={wrapper:null,popover:null,items:null,nothingFound:null,overlay:null},t.scrollLocker=new v.default,t.itemsRequiringConfirmation={},t.removeSpecialHoverBehavior=function(){var e=t.nodes.items.querySelector(".".concat(a.CSS.itemNoHover));e&&e.classList.remove(a.CSS.itemNoHover)},t.onFlip=function(){t.disableSpecialHoverAndFocusBehavior()},t.items=r,t.customContent=f,t.customContentFlippableItems=d,t.className=i||"",t.searchable=s,t.listeners=new u.default,t.scopeElement=p,t.filterLabel=l,t.nothingFoundLabel=c,t.render(),t.enableFlipper(),t}return(0,r.default)(a,[{key:"getElement",value:function(){return this.nodes.wrapper}},{key:"show",value:function(){var e=this;this.shouldOpenPopoverBottom||(this.nodes.wrapper.style.setProperty("--popover-height",this.calculateHeight()+"px"),this.nodes.wrapper.classList.add(this.className+"--opened-top")),this.search&&this.search.clear(),this.nodes.items.scrollTop=0,this.nodes.popover.classList.add(a.CSS.popoverOpened),this.nodes.overlay.classList.remove(a.CSS.popoverOverlayHidden),this.flipper.activate(this.flippableElements),this.searchable&&setTimeout((function(){e.search.focus()}),100),(0,h.isMobileScreen)()&&this.scrollLocker.lock(),this.isShown=!0}},{key:"hide",value:function(){var e=this;this.isShown&&(this.nodes.popover.classList.remove(a.CSS.popoverOpened),this.nodes.overlay.classList.add(a.CSS.popoverOverlayHidden),this.flipper.deactivate(),(0,h.isMobileScreen)()&&this.scrollLocker.unlock(),this.isShown=!1,this.nodes.wrapper.classList.remove(this.className+"--opened-top"),Array.from(this.nodes.items.querySelectorAll(".".concat(a.CSS.itemConfirmation))).forEach((function(t){return e.cleanUpConfirmationStateForItem(t)})),this.disableSpecialHoverAndFocusBehavior(),this.emit(b.Close))}},{key:"destroy",value:function(){this.flipper.deactivate(),this.listeners.removeAll(),this.disableSpecialHoverAndFocusBehavior(),(0,h.isMobileScreen)()&&this.scrollLocker.unlock()}},{key:"hasFocus",value:function(){return this.flipper.hasFocus()}},{key:"calculateHeight",value:function(){var e,t=this.nodes.popover.cloneNode(!0);return t.style.visibility="hidden",t.style.position="absolute",t.style.top="-1000px",t.classList.add(a.CSS.popoverOpened),document.body.appendChild(t),e=t.offsetHeight,t.remove(),e}},{key:"render",value:function(){var e=this;this.nodes.wrapper=c.default.make("div",this.className),this.nodes.popover=c.default.make("div",a.CSS.popover),this.nodes.wrapper.appendChild(this.nodes.popover),this.nodes.overlay=c.default.make("div",[a.CSS.popoverOverlay,a.CSS.popoverOverlayHidden]),this.nodes.wrapper.appendChild(this.nodes.overlay),this.searchable&&this.addSearch(this.nodes.popover),this.customContent&&(this.customContent.classList.add(a.CSS.customContent),this.nodes.popover.appendChild(this.customContent)),this.nodes.items=c.default.make("div",a.CSS.itemsWrapper),this.items.forEach((function(t){e.nodes.items.appendChild(e.createItem(t))})),this.nodes.popover.appendChild(this.nodes.items),this.nodes.nothingFound=c.default.make("div",[a.CSS.noFoundMessage],{textContent:this.nothingFoundLabel}),this.nodes.popover.appendChild(this.nodes.nothingFound),this.listeners.on(this.nodes.popover,"click",(function(t){var n=t.target.closest(".".concat(a.CSS.item));n&&e.itemClicked(n,t)})),this.listeners.on(this.nodes.overlay,"click",(function(){e.emit(b.OverlayClicked)}))}},{key:"addSearch",value:function(e){var t=this;this.search=new d.default({items:this.items,placeholder:this.filterLabel,onSearch:function(e){var n=[];t.items.forEach((function(o,r){var i=t.nodes.items.children[r];e.includes(o)?(n.push(i),i.classList.remove(a.CSS.itemHidden)):i.classList.add(a.CSS.itemHidden)})),t.nodes.nothingFound.classList.toggle(a.CSS.noFoundMessageShown,0===n.length);var o=e.length===t.items.length,r=o?t.flippableElements:n;t.customContent&&t.customContent.classList.toggle(a.CSS.customContentHidden,!o),t.flipper.isActivated&&(t.reactivateFlipper(r),t.flipper.focusFirst())}});var n=this.search.getElement();e.appendChild(n)}},{key:"createItem",value:function(e){var t=c.default.make("div",a.CSS.item);e.name&&(t.dataset.itemName=e.name);var n=c.default.make("div",a.CSS.itemLabel,{innerHTML:e.title||""});return t.appendChild(c.default.make("div",a.CSS.itemIcon,{innerHTML:e.icon||g.IconDotCircle})),t.appendChild(n),e.secondaryLabel&&t.appendChild(c.default.make("div",a.CSS.itemSecondaryLabel,{textContent:e.secondaryLabel})),e.isActive&&t.classList.add(a.CSS.itemActive),e.isDisabled&&t.classList.add(a.CSS.itemDisabled),t}},{key:"itemClicked",value:function(e,t){var n=this,o=Array.from(this.nodes.items.children),r=o.indexOf(e),i=this.items[r];i.isDisabled||(o.filter((function(t){return t!==e})).forEach((function(e){n.cleanUpConfirmationStateForItem(e)})),i.confirmation?this.enableConfirmationStateForItem(i,e,r):(i.onActivate(i,t),this.toggleIfNeeded(r,o),i.closeOnActivate&&this.hide()))}},{key:"toggleIfNeeded",value:function(e,t){var n=this,o=this.items[e];if(!0===o.toggle)return o.isActive=!o.isActive,void t[e].classList.toggle(a.CSS.itemActive);if("string"==typeof o.toggle){var r=this.items.filter((function(e){return e.toggle===o.toggle}));if(1===r.length)return o.isActive=!o.isActive,void t[e].classList.toggle(a.CSS.itemActive);r.forEach((function(e){var r=n.items.indexOf(e),i=e===o;e.isActive=i,t[r].classList.toggle(a.CSS.itemActive,i)}))}}},{key:"enableConfirmationStateForItem",value:function(e,n,o){var r;void 0===this.itemsRequiringConfirmation[o]&&(this.itemsRequiringConfirmation[o]=e);var i=Object.assign(Object.assign(Object.assign({},e),e.confirmation),{confirmation:e.confirmation.confirmation});this.items[o]=i;var s=this.createItem(i);(r=s.classList).add.apply(r,[a.CSS.itemConfirmation].concat((0,t.default)(Array.from(n.classList)))),n.parentElement.replaceChild(s,n),this.enableSpecialHoverAndFocusBehavior(s),this.reactivateFlipper(this.flippableElements,this.flippableElements.indexOf(s))}},{key:"cleanUpConfirmationStateForItem",value:function(e){var t=Array.from(this.nodes.items.children).indexOf(e),n=this.itemsRequiringConfirmation[t];if(void 0!==n){var o=this.createItem(n);e.parentElement.replaceChild(o,e),this.items[t]=n,delete this.itemsRequiringConfirmation[t],e.removeEventListener("mouseleave",this.removeSpecialHoverBehavior),this.disableSpecialHoverAndFocusBehavior(),this.reactivateFlipper(this.flippableElements,this.flippableElements.indexOf(o))}}},{key:"enableSpecialHoverAndFocusBehavior",value:function(e){e.classList.add(a.CSS.itemNoHover),e.classList.add(a.CSS.itemNoFocus),e.addEventListener("mouseleave",this.removeSpecialHoverBehavior,{once:!0}),this.flipper.onFlip(this.onFlip)}},{key:"disableSpecialHoverAndFocusBehavior",value:function(){this.removeSpecialFocusBehavior(),this.removeSpecialHoverBehavior(),this.flipper.removeOnFlip(this.onFlip)}},{key:"removeSpecialFocusBehavior",value:function(){var e=this.nodes.items.querySelector(".".concat(a.CSS.itemNoFocus));e&&e.classList.remove(a.CSS.itemNoFocus)}},{key:"reactivateFlipper",value:function(e,t){this.flipper.deactivate(),this.flipper.activate(e,t)}},{key:"enableFlipper",value:function(){this.flipper=new f.default({items:this.flippableElements,focusedItemClass:a.CSS.itemFocused,allowedKeys:[h.keyCodes.TAB,h.keyCodes.UP,h.keyCodes.DOWN,h.keyCodes.ENTER]})}},{key:"flippableElements",get:function(){var e=Array.from(this.nodes.wrapper.querySelectorAll(".".concat(a.CSS.item)));return(this.customContentFlippableItems||[]).concat(e)}},{key:"shouldOpenPopoverBottom",get:function(){var e=this.nodes.wrapper.getBoundingClientRect(),t=this.scopeElement.getBoundingClientRect(),n=this.calculateHeight(),o=e.top+n,r=e.top-n,i=Math.min(window.innerHeight,t.bottom);return r0;(i>>>=1)&&(t+=t))1&i&&(n+=t);return n}},function(e,t){e.exports=Math.sign||function(e){return 0==(e=+e)||e!=e?e:e<0?-1:1}},function(e,t){var n=Math.expm1;e.exports=!n||n(10)>22025.465794806718||n(10)<22025.465794806718||-2e-17!=n(-2e-17)?function(e){return 0==(e=+e)?e:e>-1e-6&&e<1e-6?e+e*e/2:Math.exp(e)-1}:n},function(e,t,n){var o=n(33),r=n(39);e.exports=function(e){return function(t,n){var i,a,s=String(r(t)),l=o(n),c=s.length;return l<0||l>=c?e?"":void 0:(i=s.charCodeAt(l))<55296||i>56319||l+1===c||(a=s.charCodeAt(l+1))<56320||a>57343?e?s.charAt(l):i:e?s.slice(l,l+2):a-56320+(i-55296<<10)+65536}}},function(e,t,n){"use strict";var o=n(46),r=n(0),i=n(23),a=n(27),s=n(57),l=n(131),c=n(55),u=n(51),f=n(14)("iterator"),d=!([].keys&&"next"in[].keys()),p=function(){return this};e.exports=function(e,t,n,h,v,g,y){l(n,t,h);var k,b,m,w=function(e){if(!d&&e in T)return T[e];switch(e){case"keys":case"values":return function(){return new n(this,e)}}return function(){return new n(this,e)}},x=t+" Iterator",C="values"==v,S=!1,T=e.prototype,E=T[f]||T["@@iterator"]||v&&T[v],B=E||w(v),M=v?C?w("entries"):B:void 0,_="Array"==t&&T.entries||E;if(_&&(m=u(_.call(new e)))!==Object.prototype&&m.next&&(c(m,x,!0),o||"function"==typeof m[f]||a(m,f,p)),C&&E&&"values"!==E.name&&(S=!0,B=function(){return E.call(this)}),o&&!y||!d&&!S&&T[f]||a(T,f,B),s[t]=B,s[x]=p,v)if(k={values:C?B:w("values"),keys:g?B:w("keys"),entries:M},y)for(b in k)b in T||i(T,b,k[b]);else r(r.P+r.F*(d||S),t,k);return k}},function(e,t,n){var o=n(100),r=n(39);e.exports=function(e,t,n){if(o(t))throw TypeError("String#"+n+" doesn't accept regex!");return String(r(e))}},function(e,t,n){var o=n(13),r=n(38),i=n(14)("match");e.exports=function(e){var t;return o(e)&&(void 0!==(t=e[i])?!!t:"RegExp"==r(e))}},function(e,t,n){var o=n(14)("match");e.exports=function(e){var t=/./;try{"/./"[e](t)}catch(n){try{return t[o]=!1,!"/./"[e](t)}catch(e){}}return!0}},function(e,t,n){var o=n(57),r=n(14)("iterator"),i=Array.prototype;e.exports=function(e){return void 0!==e&&(o.Array===e||i[r]===e)}},function(e,t,n){"use strict";var o=n(18),r=n(44);e.exports=function(e,t,n){t in e?o.f(e,t,r(0,n)):e[t]=n}},function(e,t,n){var o=n(64),r=n(14)("iterator"),i=n(57);e.exports=n(16).getIteratorMethod=function(e){if(null!=e)return e[r]||e["@@iterator"]||i[o(e)]}},function(e,t,n){"use strict";var o=n(22),r=n(48),i=n(15);e.exports=function(e){for(var t=o(this),n=i(t.length),a=arguments.length,s=r(a>1?arguments[1]:void 0,n),l=a>2?arguments[2]:void 0,c=void 0===l?n:r(l,n);c>s;)t[s++]=e;return t}},function(e,t,n){"use strict";var o=n(52),r=n(136),i=n(57),a=n(28);e.exports=n(98)(Array,"Array",(function(e,t){this._t=a(e),this._i=0,this._k=t}),(function(){var e=this._t,t=this._k,n=this._i++;return!e||n>=e.length?(this._t=void 0,r(1)):r(0,"keys"==t?n:"values"==t?e[n]:[n,e[n]])}),"values"),i.Arguments=i.Array,o("keys"),o("values"),o("entries")},function(e,t,n){"use strict";var o,r,i=n(75),a=RegExp.prototype.exec,s=String.prototype.replace,l=a,c=(o=/a/,r=/b*/g,a.call(o,"a"),a.call(r,"a"),0!==o.lastIndex||0!==r.lastIndex),u=void 0!==/()??/.exec("")[1];(c||u)&&(l=function(e){var t,n,o,r,l=this;return u&&(n=new RegExp("^"+l.source+"$(?!\\s)",i.call(l))),c&&(t=l.lastIndex),o=a.call(l,e),c&&o&&(l.lastIndex=l.global?o.index+o[0].length:t),u&&o&&o.length>1&&s.call(o[0],n,(function(){for(r=1;rn;)t.push(arguments[n++]);return y[++g]=function(){s("function"==typeof e?e:Function(e),t)},o(g),g},p=function(e){delete y[e]},"process"==n(38)(f)?o=function(e){f.nextTick(a(k,e,1))}:v&&v.now?o=function(e){v.now(a(k,e,1))}:h?(i=(r=new h).port2,r.port1.onmessage=b,o=a(i.postMessage,i,1)):u.addEventListener&&"function"==typeof postMessage&&!u.importScripts?(o=function(e){u.postMessage(e+"","*")},u.addEventListener("message",b,!1)):o="onreadystatechange"in c("script")?function(e){l.appendChild(c("script")).onreadystatechange=function(){l.removeChild(this),k.call(e)}}:function(e){setTimeout(a(k,e,1),0)}),e.exports={set:d,clear:p}},function(e,t,n){"use strict";var o=n(10),r=n(17),i=n(46),a=n(81),s=n(27),l=n(60),c=n(11),u=n(59),f=n(33),d=n(15),p=n(144),h=n(50).f,v=n(18).f,g=n(105),y=n(55),k=o.ArrayBuffer,b=o.DataView,m=o.Math,w=o.RangeError,x=o.Infinity,C=k,S=m.abs,T=m.pow,E=m.floor,B=m.log,M=m.LN2,_=r?"_b":"buffer",O=r?"_l":"byteLength",I=r?"_o":"byteOffset";function L(e,t,n){var o,r,i,a=new Array(n),s=8*n-t-1,l=(1<>1,u=23===t?T(2,-24)-T(2,-77):0,f=0,d=e<0||0===e&&1/e<0?1:0;for((e=S(e))!=e||e===x?(r=e!=e?1:0,o=l):(o=E(B(e)/M),e*(i=T(2,-o))<1&&(o--,i*=2),(e+=o+c>=1?u/i:u*T(2,1-c))*i>=2&&(o++,i/=2),o+c>=l?(r=0,o=l):o+c>=1?(r=(e*i-1)*T(2,t),o+=c):(r=e*T(2,c-1)*T(2,t),o=0));t>=8;a[f++]=255&r,r/=256,t-=8);for(o=o<0;a[f++]=255&o,o/=256,s-=8);return a[--f]|=128*d,a}function P(e,t,n){var o,r=8*n-t-1,i=(1<>1,s=r-7,l=n-1,c=e[l--],u=127&c;for(c>>=7;s>0;u=256*u+e[l],l--,s-=8);for(o=u&(1<<-s)-1,u>>=-s,s+=t;s>0;o=256*o+e[l],l--,s-=8);if(0===u)u=1-a;else{if(u===i)return o?NaN:c?-x:x;o+=T(2,t),u-=a}return(c?-1:1)*o*T(2,u-t)}function j(e){return e[3]<<24|e[2]<<16|e[1]<<8|e[0]}function R(e){return[255&e]}function A(e){return[255&e,e>>8&255]}function N(e){return[255&e,e>>8&255,e>>16&255,e>>24&255]}function D(e){return L(e,52,8)}function F(e){return L(e,23,4)}function H(e,t,n){v(e.prototype,t,{get:function(){return this[n]}})}function W(e,t,n,o){var r=p(+n);if(r+t>e[O])throw w("Wrong index!");var i=e[_]._b,a=r+e[I],s=i.slice(a,a+t);return o?s:s.reverse()}function U(e,t,n,o,r,i){var a=p(+n);if(a+t>e[O])throw w("Wrong index!");for(var s=e[_]._b,l=a+e[I],c=o(+r),u=0;uX;)(z=Y[X++])in k||s(k,z,C[z]);i||(V.constructor=k)}var G=new b(new k(2)),K=b.prototype.setInt8;G.setInt8(0,2147483648),G.setInt8(1,2147483649),!G.getInt8(0)&&G.getInt8(1)||l(b.prototype,{setInt8:function(e,t){K.call(this,e,t<<24>>24)},setUint8:function(e,t){K.call(this,e,t<<24>>24)}},!0)}else k=function(e){u(this,k,"ArrayBuffer");var t=p(e);this._b=g.call(new Array(t),0),this[O]=t},b=function(e,t,n){u(this,b,"DataView"),u(e,k,"DataView");var o=e[O],r=f(t);if(r<0||r>o)throw w("Wrong offset!");if(r+(n=void 0===n?o-r:d(n))>o)throw w("Wrong length!");this[_]=e,this[I]=r,this[O]=n},r&&(H(k,"byteLength","_l"),H(b,"buffer","_b"),H(b,"byteLength","_l"),H(b,"byteOffset","_o")),l(b.prototype,{getInt8:function(e){return W(this,1,e)[0]<<24>>24},getUint8:function(e){return W(this,1,e)[0]},getInt16:function(e){var t=W(this,2,e,arguments[1]);return(t[1]<<8|t[0])<<16>>16},getUint16:function(e){var t=W(this,2,e,arguments[1]);return t[1]<<8|t[0]},getInt32:function(e){return j(W(this,4,e,arguments[1]))},getUint32:function(e){return j(W(this,4,e,arguments[1]))>>>0},getFloat32:function(e){return P(W(this,4,e,arguments[1]),23,4)},getFloat64:function(e){return P(W(this,8,e,arguments[1]),52,8)},setInt8:function(e,t){U(this,1,e,R,t)},setUint8:function(e,t){U(this,1,e,R,t)},setInt16:function(e,t){U(this,2,e,A,t,arguments[2])},setUint16:function(e,t){U(this,2,e,A,t,arguments[2])},setInt32:function(e,t){U(this,4,e,N,t,arguments[2])},setUint32:function(e,t){U(this,4,e,N,t,arguments[2])},setFloat32:function(e,t){U(this,4,e,F,t,arguments[2])},setFloat64:function(e,t){U(this,8,e,D,t,arguments[2])}});y(k,"ArrayBuffer"),y(b,"DataView"),s(b.prototype,a.VIEW,!0),t.ArrayBuffer=k,t.DataView=b},function(e,t){function n(t,o){return e.exports=n=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(e,t){return e.__proto__=t,e},e.exports.__esModule=!0,e.exports.default=e.exports,n(t,o)}e.exports=n,e.exports.__esModule=!0,e.exports.default=e.exports},function(e,t,n){var o,r,i,a=n(7);"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self&&self,r=[t,n(2),n(3),n(8)],void 0===(i="function"==typeof(o=function(o,r,i,s){"use strict";var l=n(1);function c(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(c=function(e){return e?n:t})(e)}Object.defineProperty(o,"__esModule",{value:!0}),o.default=void 0,r=l(r),i=l(i),s=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!==a(e)&&"function"!=typeof e)return{default:e};var n=c(t);if(n&&n.has(e))return n.get(e);var o={},r=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var i in e)if("default"!==i&&Object.prototype.hasOwnProperty.call(e,i)){var s=r?Object.getOwnPropertyDescriptor(e,i):null;s&&(s.get||s.set)?Object.defineProperty(o,i,s):o[i]=e[i]}return o.default=e,n&&n.set(e,o),o}(s);var u=function(){function e(){(0,r.default)(this,e),this.allListeners=[]}return(0,i.default)(e,[{key:"on",value:function(e,t,n){var o=arguments.length>3&&void 0!==arguments[3]&&arguments[3],r=s.generateId("l"),i={id:r,element:e,eventType:t,handler:n,options:o},a=this.findOne(e,t,n);if(!a)return this.allListeners.push(i),e.addEventListener(t,n,o),r}},{key:"off",value:function(e,t,n,o){var r=this,i=this.findAll(e,t,n);i.forEach((function(e,t){var n=r.allListeners.indexOf(i[t]);n>-1&&(r.allListeners.splice(n,1),e.element.removeEventListener(e.eventType,e.handler,e.options))}))}},{key:"offById",value:function(e){var t=this.findById(e);t&&t.element.removeEventListener(t.eventType,t.handler,t.options)}},{key:"findOne",value:function(e,t,n){var o=this.findAll(e,t,n);return o.length>0?o[0]:null}},{key:"findAll",value:function(e,t,n){var o=e?this.findByEventTarget(e):[];return e&&t&&n?o.filter((function(e){return e.eventType===t&&e.handler===n})):e&&t?o.filter((function(e){return e.eventType===t})):o}},{key:"removeAll",value:function(){this.allListeners.map((function(e){e.element.removeEventListener(e.eventType,e.handler,e.options)})),this.allListeners=[]}},{key:"destroy",value:function(){this.removeAll()}},{key:"findByEventTarget",value:function(e){return this.allListeners.filter((function(t){if(t.element===e)return t}))}},{key:"findByType",value:function(e){return this.allListeners.filter((function(t){if(t.eventType===e)return t}))}},{key:"findByHandler",value:function(e){return this.allListeners.filter((function(t){if(t.handler===e)return t}))}},{key:"findById",value:function(e){return this.allListeners.find((function(t){return t.id===e}))}}]),e}();o.default=u,u.displayName="Listeners",e.exports=t.default})?o.apply(t,r):o)||(e.exports=i)},function(e,t,n){var o,r,i;"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self&&self,r=[t],void 0===(i="function"==typeof(o=function(n){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.default=void 0;var o=function(e){var t={get id(){return e.id},get name(){return e.name},get config(){return e.config},get holder(){return e.holder},get isEmpty(){return e.isEmpty},get selected(){return e.selected},set stretched(t){e.stretched=t},get stretched(){return e.stretched},call:function(t,n){return e.call(t,n)},save:function(){return e.save()},validate:function(t){return e.validate(t)},dispatchChange:function(){e.dispatchChange()}};Object.setPrototypeOf(this,t)};n.default=o,e.exports=t.default})?o.apply(t,r):o)||(e.exports=i)},function(e,t,n){var o=n(377);function r(){return"undefined"!=typeof Reflect&&Reflect.get?(e.exports=r=Reflect.get.bind(),e.exports.__esModule=!0,e.exports.default=e.exports):(e.exports=r=function(e,t,n){var r=o(e,t);if(r){var i=Object.getOwnPropertyDescriptor(r,t);return i.get?i.get.call(arguments.length<3?e:n):i.value}},e.exports.__esModule=!0,e.exports.default=e.exports),r.apply(this,arguments)}e.exports=r,e.exports.__esModule=!0,e.exports.default=e.exports},function(e,t,n){var o,r,i;"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self&&self,r=[t,n(2),n(3),n(381)],void 0===(i="function"==typeof(o=function(o,r,i,a){"use strict";var s=n(1);Object.defineProperty(o,"__esModule",{value:!0}),o.default=void 0,r=s(r),i=s(i),a=s(a);var l=function(){function e(){(0,r.default)(this,e),this.lib=new a.default}return(0,i.default)(e,[{key:"destroy",value:function(){this.lib.destroy()}},{key:"show",value:function(e,t,n){this.lib.show(e,t,n)}},{key:"hide",value:function(){var e=arguments.length>0&&void 0!==arguments[0]&&arguments[0];this.lib.hide(e)}},{key:"onHover",value:function(e,t,n){this.lib.onHover(e,t,n)}}]),e}();o.default=l,l.displayName="Tooltip",e.exports=t.default})?o.apply(t,r):o)||(e.exports=i)},function(e,t,n){var o,r,i;"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self&&self,r=[t,n(41),n(2),n(3),n(385)],void 0===(i="function"==typeof(o=function(o,r,i,a,s){"use strict";var l=n(1);Object.defineProperty(o,"__esModule",{value:!0}),o.default=void 0,r=l(r),i=l(i),a=l(a),s=l(s);var c=function(){function e(){(0,i.default)(this,e),this.registeredShortcuts=new Map}return(0,a.default)(e,[{key:"add",value:function(e){if(this.findShortcut(e.on,e.name))throw Error("Shortcut ".concat(e.name," is already registered for ").concat(e.on,". Please remove it before add a new handler."));var t=new s.default({name:e.name,on:e.on,callback:e.handler}),n=this.registeredShortcuts.get(e.on)||[];this.registeredShortcuts.set(e.on,[].concat((0,r.default)(n),[t]))}},{key:"remove",value:function(e,t){var n=this.findShortcut(e,t);if(n){n.remove();var o=this.registeredShortcuts.get(e);this.registeredShortcuts.set(e,o.filter((function(e){return e!==n})))}}},{key:"findShortcut",value:function(e,t){return(this.registeredShortcuts.get(e)||[]).find((function(e){return e.name===t}))}}]),e}();c.displayName="Shortcuts";var u=new c;o.default=u,e.exports=t.default})?o.apply(t,r):o)||(e.exports=i)},function(e,t,n){e.exports=!n(17)&&!n(11)((function(){return 7!=Object.defineProperty(n(86)("div"),"a",{get:function(){return 7}}).a}))},function(e,t,n){var o=n(10),r=n(16),i=n(46),a=n(87),s=n(18).f;e.exports=function(e){var t=r.Symbol||(r.Symbol=i?{}:o.Symbol||{});"_"==e.charAt(0)||e in t||s(t,e,{value:a.f(e)})}},function(e,t,n){var o=n(26),r=n(28),i=n(71)(!1),a=n(88)("IE_PROTO");e.exports=function(e,t){var n,s=r(e),l=0,c=[];for(n in s)n!=a&&o(s,n)&&c.push(n);for(;t.length>l;)o(s,n=t[l++])&&(~i(c,n)||c.push(n));return c}},function(e,t,n){var o=n(18),r=n(12),i=n(47);e.exports=n(17)?Object.defineProperties:function(e,t){r(e);for(var n,a=i(t),s=a.length,l=0;s>l;)o.f(e,n=a[l++],t[n]);return e}},function(e,t,n){var o=n(28),r=n(50).f,i={}.toString,a="object"==typeof window&&window&&Object.getOwnPropertyNames?Object.getOwnPropertyNames(window):[];e.exports.f=function(e){return a&&"[object Window]"==i.call(e)?function(e){try{return r(e)}catch(e){return a.slice()}}(e):r(o(e))}},function(e,t,n){"use strict";var o=n(17),r=n(47),i=n(72),a=n(63),s=n(22),l=n(62),c=Object.assign;e.exports=!c||n(11)((function(){var e={},t={},n=Symbol(),o="abcdefghijklmnopqrst";return e[n]=7,o.split("").forEach((function(e){t[e]=e})),7!=c({},e)[n]||Object.keys(c({},t)).join("")!=o}))?function(e,t){for(var n=s(e),c=arguments.length,u=1,f=i.f,d=a.f;c>u;)for(var p,h=l(arguments[u++]),v=f?r(h).concat(f(h)):r(h),g=v.length,y=0;g>y;)p=v[y++],o&&!d.call(h,p)||(n[p]=h[p]);return n}:c},function(e,t){e.exports=Object.is||function(e,t){return e===t?0!==e||1/e==1/t:e!=e&&t!=t}},function(e,t,n){"use strict";var o=n(32),r=n(13),i=n(125),a=[].slice,s={},l=function(e,t,n){if(!(t in s)){for(var o=[],r=0;r>>0||(a.test(n)?16:10))}:o},function(e,t,n){var o=n(10).parseFloat,r=n(56).trim;e.exports=1/o(n(92)+"-0")!=-1/0?function(e){var t=r(String(e),3),n=o(t);return 0===n&&"-"==t.charAt(0)?-0:n}:o},function(e,t,n){var o=n(38);e.exports=function(e,t){if("number"!=typeof e&&"Number"!=o(e))throw TypeError(t);return+e}},function(e,t,n){var o=n(13),r=Math.floor;e.exports=function(e){return!o(e)&&isFinite(e)&&r(e)===e}},function(e,t){e.exports=Math.log1p||function(e){return(e=+e)>-1e-8&&e<1e-8?e-e*e/2:Math.log(1+e)}},function(e,t,n){"use strict";var o=n(49),r=n(44),i=n(55),a={};n(27)(a,n(14)("iterator"),(function(){return this})),e.exports=function(e,t,n){e.prototype=o(a,{next:r(1,n)}),i(e,t+" Iterator")}},function(e,t,n){var o=n(12);e.exports=function(e,t,n,r){try{return r?t(o(n)[0],n[1]):t(n)}catch(t){var i=e.return;throw void 0!==i&&o(i.call(e)),t}}},function(e,t,n){var o=n(282);e.exports=function(e,t){return new(o(e))(t)}},function(e,t,n){var o=n(32),r=n(22),i=n(62),a=n(15);e.exports=function(e,t,n,s,l){o(t);var c=r(e),u=i(c),f=a(c.length),d=l?f-1:0,p=l?-1:1;if(n<2)for(;;){if(d in u){s=u[d],d+=p;break}if(d+=p,l?d<0:f<=d)throw TypeError("Reduce of empty array with no initial value")}for(;l?d>=0:f>d;d+=p)d in u&&(s=t(s,u[d],d,c));return s}},function(e,t,n){"use strict";var o=n(22),r=n(48),i=n(15);e.exports=[].copyWithin||function(e,t){var n=o(this),a=i(n.length),s=r(e,a),l=r(t,a),c=arguments.length>2?arguments[2]:void 0,u=Math.min((void 0===c?a:r(c,a))-l,a-s),f=1;for(l0;)l in n?n[s]=n[l]:delete n[s],s+=f,l+=f;return n}},function(e,t){e.exports=function(e,t){return{value:t,done:!!e}}},function(e,t,n){"use strict";var o=n(107);n(0)({target:"RegExp",proto:!0,forced:o!==/./.exec},{exec:o})},function(e,t,n){n(17)&&"g"!=/./g.flags&&n(18).f(RegExp.prototype,"flags",{configurable:!0,get:n(75)})},function(e,t,n){"use strict";var o,r,i,a,s=n(46),l=n(10),c=n(31),u=n(64),f=n(0),d=n(13),p=n(32),h=n(59),v=n(78),g=n(65),y=n(109).set,k=n(302)(),b=n(140),m=n(303),w=n(79),x=n(141),C=l.TypeError,S=l.process,T=S&&S.versions,E=T&&T.v8||"",B=l.Promise,M="process"==u(S),_=function(){},O=r=b.f,I=!!function(){try{var e=B.resolve(1),t=(e.constructor={})[n(14)("species")]=function(e){e(_,_)};return(M||"function"==typeof PromiseRejectionEvent)&&e.then(_)instanceof t&&0!==E.indexOf("6.6")&&-1===w.indexOf("Chrome/66")}catch(e){}}(),L=function(e){var t;return!(!d(e)||"function"!=typeof(t=e.then))&&t},P=function(e,t){if(!e._n){e._n=!0;var n=e._c;k((function(){for(var o=e._v,r=1==e._s,i=0,a=function(t){var n,i,a,s=r?t.ok:t.fail,l=t.resolve,c=t.reject,u=t.domain;try{s?(r||(2==e._h&&A(e),e._h=1),!0===s?n=o:(u&&u.enter(),n=s(o),u&&(u.exit(),a=!0)),n===t.promise?c(C("Promise-chain cycle")):(i=L(n))?i.call(n,l,c):l(n)):c(o)}catch(e){u&&!a&&u.exit(),c(e)}};n.length>i;)a(n[i++]);e._c=[],e._n=!1,t&&!e._h&&j(e)}))}},j=function(e){y.call(l,(function(){var t,n,o,r=e._v,i=R(e);if(i&&(t=m((function(){M?S.emit("unhandledRejection",r,e):(n=l.onunhandledrejection)?n({promise:e,reason:r}):(o=l.console)&&o.error&&o.error("Unhandled promise rejection",r)})),e._h=M||R(e)?2:1),e._a=void 0,i&&t.e)throw t.v}))},R=function(e){return 1!==e._h&&0===(e._a||e._c).length},A=function(e){y.call(l,(function(){var t;M?S.emit("rejectionHandled",e):(t=l.onrejectionhandled)&&t({promise:e,reason:e._v})}))},N=function(e){var t=this;t._d||(t._d=!0,(t=t._w||t)._v=e,t._s=2,t._a||(t._a=t._c.slice()),P(t,!0))},D=function(e){var t,n=this;if(!n._d){n._d=!0,n=n._w||n;try{if(n===e)throw C("Promise can't be resolved itself");(t=L(e))?k((function(){var o={_w:n,_d:!1};try{t.call(e,c(D,o,1),c(N,o,1))}catch(e){N.call(o,e)}})):(n._v=e,n._s=1,P(n,!1))}catch(e){N.call({_w:n,_d:!1},e)}}};I||(B=function(e){h(this,B,"Promise","_h"),p(e),o.call(this);try{e(c(D,this,1),c(N,this,1))}catch(e){N.call(this,e)}},(o=function(e){this._c=[],this._a=void 0,this._s=0,this._d=!1,this._v=void 0,this._h=0,this._n=!1}).prototype=n(60)(B.prototype,{then:function(e,t){var n=O(g(this,B));return n.ok="function"!=typeof e||e,n.fail="function"==typeof t&&t,n.domain=M?S.domain:void 0,this._c.push(n),this._a&&this._a.push(n),this._s&&P(this,!1),n.promise},catch:function(e){return this.then(void 0,e)}}),i=function(){var e=new o;this.promise=e,this.resolve=c(D,e,1),this.reject=c(N,e,1)},b.f=O=function(e){return e===B||e===a?new i(e):r(e)}),f(f.G+f.W+f.F*!I,{Promise:B}),n(55)(B,"Promise"),n(58)("Promise"),a=n(16).Promise,f(f.S+f.F*!I,"Promise",{reject:function(e){var t=O(this);return(0,t.reject)(e),t.promise}}),f(f.S+f.F*(s||!I),"Promise",{resolve:function(e){return x(s&&this===a?B:this,e)}}),f(f.S+f.F*!(I&&n(74)((function(e){B.all(e).catch(_)}))),"Promise",{all:function(e){var t=this,n=O(t),o=n.resolve,r=n.reject,i=m((function(){var n=[],i=0,a=1;v(e,!1,(function(e){var s=i++,l=!1;n.push(void 0),a++,t.resolve(e).then((function(e){l||(l=!0,n[s]=e,--a||o(n))}),r)})),--a||o(n)}));return i.e&&r(i.v),n.promise},race:function(e){var t=this,n=O(t),o=n.reject,r=m((function(){v(e,!1,(function(e){t.resolve(e).then(n.resolve,o)}))}));return r.e&&o(r.v),n.promise}})},function(e,t,n){"use strict";var o=n(32);function r(e){var t,n;this.promise=new e((function(e,o){if(void 0!==t||void 0!==n)throw TypeError("Bad Promise constructor");t=e,n=o})),this.resolve=o(t),this.reject=o(n)}e.exports.f=function(e){return new r(e)}},function(e,t,n){var o=n(12),r=n(13),i=n(140);e.exports=function(e,t){if(o(e),r(t)&&t.constructor===e)return t;var n=i.f(e);return(0,n.resolve)(t),n.promise}},function(e,t,n){"use strict";var o=n(18).f,r=n(49),i=n(60),a=n(31),s=n(59),l=n(78),c=n(98),u=n(136),f=n(58),d=n(17),p=n(43).fastKey,h=n(53),v=d?"_s":"size",g=function(e,t){var n,o=p(t);if("F"!==o)return e._i[o];for(n=e._f;n;n=n.n)if(n.k==t)return n};e.exports={getConstructor:function(e,t,n,c){var u=e((function(e,o){s(e,u,t,"_i"),e._t=t,e._i=r(null),e._f=void 0,e._l=void 0,e[v]=0,null!=o&&l(o,n,e[c],e)}));return i(u.prototype,{clear:function(){for(var e=h(this,t),n=e._i,o=e._f;o;o=o.n)o.r=!0,o.p&&(o.p=o.p.n=void 0),delete n[o.i];e._f=e._l=void 0,e[v]=0},delete:function(e){var n=h(this,t),o=g(n,e);if(o){var r=o.n,i=o.p;delete n._i[o.i],o.r=!0,i&&(i.n=r),r&&(r.p=i),n._f==o&&(n._f=r),n._l==o&&(n._l=i),n[v]--}return!!o},forEach:function(e){h(this,t);for(var n,o=a(e,arguments.length>1?arguments[1]:void 0,3);n=n?n.n:this._f;)for(o(n.v,n.k,this);n&&n.r;)n=n.p},has:function(e){return!!g(h(this,t),e)}}),d&&o(u.prototype,"size",{get:function(){return h(this,t)[v]}}),u},def:function(e,t,n){var o,r,i=g(e,t);return i?i.v=n:(e._l=i={i:r=p(t,!0),k:t,v:n,p:o=e._l,n:void 0,r:!1},e._f||(e._f=i),o&&(o.n=i),e[v]++,"F"!==r&&(e._i[r]=i)),e},getEntry:g,setStrong:function(e,t,n){c(e,t,(function(e,n){this._t=h(e,t),this._k=n,this._l=void 0}),(function(){for(var e=this._k,t=this._l;t&&t.r;)t=t.p;return this._t&&(this._l=t=t?t.n:this._t._f)?u(0,"keys"==e?t.k:"values"==e?t.v:[t.k,t.v]):(this._t=void 0,u(1))}),n?"entries":"values",!n,!0),f(t)}}},function(e,t,n){"use strict";var o=n(60),r=n(43).getWeak,i=n(12),a=n(13),s=n(59),l=n(78),c=n(36),u=n(26),f=n(53),d=c(5),p=c(6),h=0,v=function(e){return e._l||(e._l=new g)},g=function(){this.a=[]},y=function(e,t){return d(e.a,(function(e){return e[0]===t}))};g.prototype={get:function(e){var t=y(this,e);if(t)return t[1]},has:function(e){return!!y(this,e)},set:function(e,t){var n=y(this,e);n?n[1]=t:this.a.push([e,t])},delete:function(e){var t=p(this.a,(function(t){return t[0]===e}));return~t&&this.a.splice(t,1),!!~t}},e.exports={getConstructor:function(e,t,n,i){var c=e((function(e,o){s(e,c,t,"_i"),e._t=t,e._i=h++,e._l=void 0,null!=o&&l(o,n,e[i],e)}));return o(c.prototype,{delete:function(e){if(!a(e))return!1;var n=r(e);return!0===n?v(f(this,t)).delete(e):n&&u(n,this._i)&&delete n[this._i]},has:function(e){if(!a(e))return!1;var n=r(e);return!0===n?v(f(this,t)).has(e):n&&u(n,this._i)}}),c},def:function(e,t,n){var o=r(i(t),!0);return!0===o?v(e).set(t,n):o[e._i]=n,e},ufstore:v}},function(e,t,n){var o=n(33),r=n(15);e.exports=function(e){if(void 0===e)return 0;var t=o(e),n=r(t);if(t!==n)throw RangeError("Wrong length!");return n}},function(e,t,n){var o=n(50),r=n(72),i=n(12),a=n(10).Reflect;e.exports=a&&a.ownKeys||function(e){var t=o.f(i(e)),n=r.f;return n?t.concat(n(e)):t}},function(e,t,n){var o=n(15),r=n(94),i=n(39);e.exports=function(e,t,n,a){var s=String(i(e)),l=s.length,c=void 0===n?" ":String(n),u=o(t);if(u<=l||""==c)return s;var f=u-l,d=r.call(c,Math.ceil(f/c.length));return d.length>f&&(d=d.slice(0,f)),a?d+s:s+d}},function(e,t,n){var o=n(17),r=n(47),i=n(28),a=n(63).f;e.exports=function(e){return function(t){for(var n,s=i(t),l=r(s),c=l.length,u=0,f=[];c>u;)n=l[u++],o&&!a.call(s,n)||f.push(e?[n,s[n]]:s[n]);return f}}},function(e,t,n){var o=n(149);e.exports=function(e,t){if(e){if("string"==typeof e)return o(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?o(e,t):void 0}},e.exports.__esModule=!0,e.exports.default=e.exports},function(e,t){e.exports=function(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,o=new Array(t);n0&&void 0!==arguments[0]?arguments[0]:e.config.defaultBlock,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},o=arguments.length>3?arguments[3]:void 0,r=arguments.length>4?arguments[4]:void 0,i=arguments.length>5?arguments[5]:void 0,a=arguments.length>6?arguments[6]:void 0,s=e.Editor.BlockManager.insert({id:a,tool:t,data:n,index:o,needToFocus:r,replace:i});return new p.default(s)},e.composeBlockData=function(){var t=(0,i.default)(r.default.mark((function t(n){var o,i;return r.default.wrap((function(t){for(;;)switch(t.prev=t.next){case 0:return o=e.Editor.Tools.blockTools.get(n),i=new v.default({tool:o,api:e.Editor.API,readOnly:!0,data:{},tunesData:{}}),t.abrupt("return",i.data);case 3:case"end":return t.stop()}}),t)})));return function(e){return t.apply(this,arguments)}}(),e.update=function(t,n){var o=e.Editor.BlockManager,r=o.getBlockById(t);if(r){var i=o.getBlockIndex(r);o.insert({id:r.id,tool:r.name,data:n,index:i,replace:!0,tunes:r.tunes})}else d.log("blocks.update(): Block with passed id was not found","warn")},e}return(0,l.default)(n,[{key:"methods",get:function(){var e=this;return{clear:function(){return e.clear()},render:function(t){return e.render(t)},renderFromHTML:function(t){return e.renderFromHTML(t)},delete:function(t){return e.delete(t)},swap:function(t,n){return e.swap(t,n)},move:function(t,n){return e.move(t,n)},getBlockByIndex:function(t){return e.getBlockByIndex(t)},getById:function(t){return e.getById(t)},getCurrentBlockIndex:function(){return e.getCurrentBlockIndex()},getBlockIndex:function(t){return e.getBlockIndex(t)},getBlocksCount:function(){return e.getBlocksCount()},stretchBlock:function(t){var n=!(arguments.length>1&&void 0!==arguments[1])||arguments[1];return e.stretchBlock(t,n)},insertNewBlock:function(){return e.insertNewBlock()},insert:this.insert,update:this.update,composeBlockData:this.composeBlockData}}},{key:"getBlocksCount",value:function(){return this.Editor.BlockManager.blocks.length}},{key:"getCurrentBlockIndex",value:function(){return this.Editor.BlockManager.currentBlockIndex}},{key:"getBlockIndex",value:function(e){var t=this.Editor.BlockManager.getBlockById(e);if(t)return this.Editor.BlockManager.getBlockIndex(t);d.logLabeled("There is no block with id `"+e+"`","warn")}},{key:"getBlockByIndex",value:function(e){var t=this.Editor.BlockManager.getBlockByIndex(e);if(void 0!==t)return new p.default(t);d.logLabeled("There is no block at index `"+e+"`","warn")}},{key:"getById",value:function(e){var t=this.Editor.BlockManager.getBlockById(e);return void 0===t?(d.logLabeled("There is no block with id `"+e+"`","warn"),null):new p.default(t)}},{key:"swap",value:function(e,t){d.log("`blocks.swap()` method is deprecated and will be removed in the next major release. Use `block.move()` method instead","info"),this.Editor.BlockManager.swap(e,t)}},{key:"move",value:function(e,t){this.Editor.BlockManager.move(e,t)}},{key:"delete",value:function(e){try{this.Editor.BlockManager.removeBlock(e)}catch(e){return void d.logLabeled(e,"warn")}0===this.Editor.BlockManager.blocks.length&&this.Editor.BlockManager.insert(),this.Editor.BlockManager.currentBlock&&this.Editor.Caret.setToBlock(this.Editor.BlockManager.currentBlock,this.Editor.Caret.positions.END),this.Editor.Toolbar.close()}},{key:"clear",value:function(){this.Editor.BlockManager.clear(!0),this.Editor.InlineToolbar.close()}},{key:"render",value:function(e){return this.Editor.BlockManager.clear(),this.Editor.Renderer.render(e.blocks)}},{key:"renderFromHTML",value:function(e){return this.Editor.BlockManager.clear(),this.Editor.Paste.processText(e,!0)}},{key:"stretchBlock",value:function(e){var t=!(arguments.length>1&&void 0!==arguments[1])||arguments[1];d.deprecationAssert(!0,"blocks.stretchBlock()","BlockAPI");var n=this.Editor.BlockManager.getBlockByIndex(e);n&&(n.stretched=t)}},{key:"insertNewBlock",value:function(){d.log("Method blocks.insertNewBlock() is deprecated and it will be removed in the next major release. Use blocks.insert() instead.","warn"),this.insert()}}]),n}(h.default);o.default=b,b.displayName="BlocksAPI",e.exports=t.default})?o.apply(t,r):o)||(e.exports=i)},function(e,t,n){var o,r,i;"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self&&self,r=[t,n(2),n(3),n(5),n(6),n(4),n(9)],void 0===(i="function"==typeof(o=function(o,r,i,a,s,l,c){"use strict";var u=n(1);function f(e){var t=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}();return function(){var n,o=(0,l.default)(e);if(t){var r=(0,l.default)(this).constructor;n=Reflect.construct(o,arguments,r)}else n=o.apply(this,arguments);return(0,s.default)(this,n)}}Object.defineProperty(o,"__esModule",{value:!0}),o.default=void 0,r=u(r),i=u(i),a=u(a),s=u(s),l=u(l);var d=function(e){(0,a.default)(n,e);var t=f(n);function n(){var e;return(0,r.default)(this,n),(e=t.apply(this,arguments)).setToFirstBlock=function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:e.Editor.Caret.positions.DEFAULT,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0;return!!e.Editor.BlockManager.firstBlock&&(e.Editor.Caret.setToBlock(e.Editor.BlockManager.firstBlock,t,n),!0)},e.setToLastBlock=function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:e.Editor.Caret.positions.DEFAULT,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0;return!!e.Editor.BlockManager.lastBlock&&(e.Editor.Caret.setToBlock(e.Editor.BlockManager.lastBlock,t,n),!0)},e.setToPreviousBlock=function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:e.Editor.Caret.positions.DEFAULT,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0;return!!e.Editor.BlockManager.previousBlock&&(e.Editor.Caret.setToBlock(e.Editor.BlockManager.previousBlock,t,n),!0)},e.setToNextBlock=function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:e.Editor.Caret.positions.DEFAULT,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0;return!!e.Editor.BlockManager.nextBlock&&(e.Editor.Caret.setToBlock(e.Editor.BlockManager.nextBlock,t,n),!0)},e.setToBlock=function(t){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:e.Editor.Caret.positions.DEFAULT,o=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0;return!!e.Editor.BlockManager.blocks[t]&&(e.Editor.Caret.setToBlock(e.Editor.BlockManager.blocks[t],n,o),!0)},e.focus=function(){var t=arguments.length>0&&void 0!==arguments[0]&&arguments[0];return t?e.setToLastBlock(e.Editor.Caret.positions.END):e.setToFirstBlock(e.Editor.Caret.positions.START)},e}return(0,i.default)(n,[{key:"methods",get:function(){return{setToFirstBlock:this.setToFirstBlock,setToLastBlock:this.setToLastBlock,setToPreviousBlock:this.setToPreviousBlock,setToNextBlock:this.setToNextBlock,setToBlock:this.setToBlock,focus:this.focus}}}]),n}((c=u(c)).default);o.default=d,d.displayName="CaretAPI",e.exports=t.default})?o.apply(t,r):o)||(e.exports=i)},function(e,t,n){var o,r,i;"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self&&self,r=[t,n(2),n(3),n(5),n(6),n(4),n(9)],void 0===(i="function"==typeof(o=function(o,r,i,a,s,l,c){"use strict";var u=n(1);function f(e){var t=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}();return function(){var n,o=(0,l.default)(e);if(t){var r=(0,l.default)(this).constructor;n=Reflect.construct(o,arguments,r)}else n=o.apply(this,arguments);return(0,s.default)(this,n)}}Object.defineProperty(o,"__esModule",{value:!0}),o.default=void 0,r=u(r),i=u(i),a=u(a),s=u(s),l=u(l);var d=function(e){(0,a.default)(n,e);var t=f(n);function n(){return(0,r.default)(this,n),t.apply(this,arguments)}return(0,i.default)(n,[{key:"methods",get:function(){var e=this;return{emit:function(t,n){return e.emit(t,n)},off:function(t,n){return e.off(t,n)},on:function(t,n){return e.on(t,n)}}}},{key:"on",value:function(e,t){this.eventsDispatcher.on(e,t)}},{key:"emit",value:function(e,t){this.eventsDispatcher.emit(e,t)}},{key:"off",value:function(e,t){this.eventsDispatcher.off(e,t)}}]),n}((c=u(c)).default);o.default=d,d.displayName="EventsAPI",e.exports=t.default})?o.apply(t,r):o)||(e.exports=i)},function(e,t,n){var o,r,i;"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self&&self,r=[t,n(2),n(3),n(5),n(6),n(4),n(54),n(8),n(9)],void 0===(i="function"==typeof(o=function(o,r,i,a,s,l,c,u,f){"use strict";var d=n(1);function p(e){var t=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}();return function(){var n,o=(0,l.default)(e);if(t){var r=(0,l.default)(this).constructor;n=Reflect.construct(o,arguments,r)}else n=o.apply(this,arguments);return(0,s.default)(this,n)}}Object.defineProperty(o,"__esModule",{value:!0}),o.default=void 0,r=d(r),i=d(i),a=d(a),s=d(s),l=d(l),c=d(c);var h=function(e){(0,a.default)(n,e);var t=p(n);function n(){return(0,r.default)(this,n),t.apply(this,arguments)}return(0,i.default)(n,[{key:"methods",get:function(){return{t:function(){(0,u.logLabeled)("I18n.t() method can be accessed only from Tools","warn")}}}},{key:"getMethodsForTool",value:function(e){return Object.assign(this.methods,{t:function(t){return c.default.t(n.getNamespace(e),t)}})}}],[{key:"getNamespace",value:function(e){return e.isTune()?"blockTunes.".concat(e.name):"tools.".concat(e.name)}}]),n}((f=d(f)).default);o.default=h,h.displayName="I18nAPI",e.exports=t.default})?o.apply(t,r):o)||(e.exports=i)},function(e,t,n){var o,r,i;"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self&&self,r=[t,n(2),n(3),n(5),n(6),n(4),n(9)],void 0===(i="function"==typeof(o=function(o,r,i,a,s,l,c){"use strict";var u=n(1);function f(e){var t=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}();return function(){var n,o=(0,l.default)(e);if(t){var r=(0,l.default)(this).constructor;n=Reflect.construct(o,arguments,r)}else n=o.apply(this,arguments);return(0,s.default)(this,n)}}Object.defineProperty(o,"__esModule",{value:!0}),o.default=void 0,r=u(r),i=u(i),a=u(a),s=u(s),l=u(l);var d=function(e){(0,a.default)(n,e);var t=f(n);function n(){return(0,r.default)(this,n),t.apply(this,arguments)}return(0,i.default)(n,[{key:"methods",get:function(){var e=this;return{close:function(){return e.close()},open:function(){return e.open()}}}},{key:"open",value:function(){this.Editor.InlineToolbar.tryToShow()}},{key:"close",value:function(){this.Editor.InlineToolbar.close()}}]),n}((c=u(c)).default);o.default=d,d.displayName="InlineToolbarAPI",e.exports=t.default})?o.apply(t,r):o)||(e.exports=i)},function(e,t,n){var o,r,i;"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self&&self,r=[t,n(2),n(3),n(5),n(6),n(4),n(9)],void 0===(i="function"==typeof(o=function(o,r,i,a,s,l,c){"use strict";var u=n(1);function f(e){var t=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}();return function(){var n,o=(0,l.default)(e);if(t){var r=(0,l.default)(this).constructor;n=Reflect.construct(o,arguments,r)}else n=o.apply(this,arguments);return(0,s.default)(this,n)}}Object.defineProperty(o,"__esModule",{value:!0}),o.default=void 0,r=u(r),i=u(i),a=u(a),s=u(s),l=u(l);var d=function(e){(0,a.default)(n,e);var t=f(n);function n(){return(0,r.default)(this,n),t.apply(this,arguments)}return(0,i.default)(n,[{key:"methods",get:function(){var e=this;return{on:function(t,n,o,r){return e.on(t,n,o,r)},off:function(t,n,o,r){return e.off(t,n,o,r)},offById:function(t){return e.offById(t)}}}},{key:"on",value:function(e,t,n,o){return this.listeners.on(e,t,n,o)}},{key:"off",value:function(e,t,n,o){this.listeners.off(e,t,n,o)}},{key:"offById",value:function(e){this.listeners.offById(e)}}]),n}((c=u(c)).default);o.default=d,d.displayName="ListenersAPI",e.exports=t.default})?o.apply(t,r):o)||(e.exports=i)},function(e,t,n){var o,r,i;"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self&&self,r=[t,n(2),n(3),n(5),n(6),n(4),n(378),n(9)],void 0===(i="function"==typeof(o=function(o,r,i,a,s,l,c,u){"use strict";var f=n(1);function d(e){var t=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}();return function(){var n,o=(0,l.default)(e);if(t){var r=(0,l.default)(this).constructor;n=Reflect.construct(o,arguments,r)}else n=o.apply(this,arguments);return(0,s.default)(this,n)}}Object.defineProperty(o,"__esModule",{value:!0}),o.default=void 0,r=f(r),i=f(i),a=f(a),s=f(s),l=f(l),c=f(c);var p=function(e){(0,a.default)(n,e);var t=d(n);function n(e){var o,i=e.config,a=e.eventsDispatcher;return(0,r.default)(this,n),(o=t.call(this,{config:i,eventsDispatcher:a})).notifier=new c.default,o}return(0,i.default)(n,[{key:"methods",get:function(){var e=this;return{show:function(t){return e.show(t)}}}},{key:"show",value:function(e){return this.notifier.show(e)}}]),n}((u=f(u)).default);o.default=p,p.displayName="NotifierAPI",e.exports=t.default})?o.apply(t,r):o)||(e.exports=i)},function(e,t,n){var o,r,i;"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self&&self,r=[t,n(2),n(3),n(5),n(6),n(4),n(9)],void 0===(i="function"==typeof(o=function(o,r,i,a,s,l,c){"use strict";var u=n(1);function f(e){var t=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}();return function(){var n,o=(0,l.default)(e);if(t){var r=(0,l.default)(this).constructor;n=Reflect.construct(o,arguments,r)}else n=o.apply(this,arguments);return(0,s.default)(this,n)}}Object.defineProperty(o,"__esModule",{value:!0}),o.default=void 0,r=u(r),i=u(i),a=u(a),s=u(s),l=u(l);var d=function(e){(0,a.default)(n,e);var t=f(n);function n(){return(0,r.default)(this,n),t.apply(this,arguments)}return(0,i.default)(n,[{key:"methods",get:function(){var e=this;return{toggle:function(t){return e.toggle(t)},get isEnabled(){return e.isEnabled}}}},{key:"toggle",value:function(e){return this.Editor.ReadOnly.toggle(e)}},{key:"isEnabled",get:function(){return this.Editor.ReadOnly.isEnabled}}]),n}((c=u(c)).default);o.default=d,d.displayName="ReadOnlyAPI",e.exports=t.default})?o.apply(t,r):o)||(e.exports=i)},function(e,t,n){var o,r,i;"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self&&self,r=[t,n(2),n(3),n(5),n(6),n(4),n(9),n(66)],void 0===(i="function"==typeof(o=function(o,r,i,a,s,l,c,u){"use strict";var f=n(1);function d(e){var t=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}();return function(){var n,o=(0,l.default)(e);if(t){var r=(0,l.default)(this).constructor;n=Reflect.construct(o,arguments,r)}else n=o.apply(this,arguments);return(0,s.default)(this,n)}}Object.defineProperty(o,"__esModule",{value:!0}),o.default=void 0,r=f(r),i=f(i),a=f(a),s=f(s),l=f(l);var p=function(e){(0,a.default)(n,e);var t=d(n);function n(){return(0,r.default)(this,n),t.apply(this,arguments)}return(0,i.default)(n,[{key:"methods",get:function(){var e=this;return{clean:function(t,n){return e.clean(t,n)}}}},{key:"clean",value:function(e,t){return(0,u.clean)(e,t)}}]),n}((c=f(c)).default);o.default=p,p.displayName="SanitizerAPI",e.exports=t.default})?o.apply(t,r):o)||(e.exports=i)},function(e,t,n){var o,r,i,a=n(7);"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self&&self,r=[t,n(2),n(3),n(5),n(6),n(4),n(8),n(9)],void 0===(i="function"==typeof(o=function(o,r,i,s,l,c,u,f){"use strict";var d=n(1);function p(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(p=function(e){return e?n:t})(e)}function h(e){var t=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}();return function(){var n,o=(0,c.default)(e);if(t){var r=(0,c.default)(this).constructor;n=Reflect.construct(o,arguments,r)}else n=o.apply(this,arguments);return(0,l.default)(this,n)}}Object.defineProperty(o,"__esModule",{value:!0}),o.default=void 0,r=d(r),i=d(i),s=d(s),l=d(l),c=d(c),u=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!==a(e)&&"function"!=typeof e)return{default:e};var n=p(t);if(n&&n.has(e))return n.get(e);var o={},r=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var i in e)if("default"!==i&&Object.prototype.hasOwnProperty.call(e,i)){var s=r?Object.getOwnPropertyDescriptor(e,i):null;s&&(s.get||s.set)?Object.defineProperty(o,i,s):o[i]=e[i]}return o.default=e,n&&n.set(e,o),o}(u);var v=function(e){(0,s.default)(n,e);var t=h(n);function n(){return(0,r.default)(this,n),t.apply(this,arguments)}return(0,i.default)(n,[{key:"methods",get:function(){var e=this;return{save:function(){return e.save()}}}},{key:"save",value:function(){var e="Editor's content can not be saved in read-only mode";return this.Editor.ReadOnly.isEnabled?(u.logLabeled(e,"warn"),Promise.reject(new Error(e))):this.Editor.Saver.save()}}]),n}((f=d(f)).default);o.default=v,v.displayName="SaverAPI",e.exports=t.default})?o.apply(t,r):o)||(e.exports=i)},function(e,t,n){var o,r,i;"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self&&self,r=[t,n(2),n(3),n(5),n(6),n(4),n(25),n(9)],void 0===(i="function"==typeof(o=function(o,r,i,a,s,l,c,u){"use strict";var f=n(1);function d(e){var t=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}();return function(){var n,o=(0,l.default)(e);if(t){var r=(0,l.default)(this).constructor;n=Reflect.construct(o,arguments,r)}else n=o.apply(this,arguments);return(0,s.default)(this,n)}}Object.defineProperty(o,"__esModule",{value:!0}),o.default=void 0,r=f(r),i=f(i),a=f(a),s=f(s),l=f(l),c=f(c);var p=function(e){(0,a.default)(n,e);var t=d(n);function n(){return(0,r.default)(this,n),t.apply(this,arguments)}return(0,i.default)(n,[{key:"methods",get:function(){var e=this;return{findParentTag:function(t,n){return e.findParentTag(t,n)},expandToTag:function(t){return e.expandToTag(t)}}}},{key:"findParentTag",value:function(e,t){return(new c.default).findParentTag(e,t)}},{key:"expandToTag",value:function(e){(new c.default).expandToTag(e)}}]),n}((u=f(u)).default);o.default=p,p.displayName="SelectionAPI",e.exports=t.default})?o.apply(t,r):o)||(e.exports=i)},function(e,t,n){var o,r,i;"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self&&self,r=[t,n(2),n(3),n(5),n(6),n(4),n(9)],void 0===(i="function"==typeof(o=function(o,r,i,a,s,l,c){"use strict";var u=n(1);function f(e){var t=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}();return function(){var n,o=(0,l.default)(e);if(t){var r=(0,l.default)(this).constructor;n=Reflect.construct(o,arguments,r)}else n=o.apply(this,arguments);return(0,s.default)(this,n)}}Object.defineProperty(o,"__esModule",{value:!0}),o.default=void 0,r=u(r),i=u(i),a=u(a),s=u(s),l=u(l);var d=function(e){(0,a.default)(n,e);var t=f(n);function n(){return(0,r.default)(this,n),t.apply(this,arguments)}return(0,i.default)(n,[{key:"classes",get:function(){return{block:"cdx-block",inlineToolButton:"ce-inline-tool",inlineToolButtonActive:"ce-inline-tool--active",input:"cdx-input",loader:"cdx-loader",button:"cdx-button",settingsButton:"cdx-settings-button",settingsButtonActive:"cdx-settings-button--active"}}}]),n}((c=u(c)).default);o.default=d,d.displayName="StylesAPI",e.exports=t.default})?o.apply(t,r):o)||(e.exports=i)},function(e,t,n){var o,r,i,a=n(7);"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self&&self,r=[t,n(2),n(3),n(5),n(6),n(4),n(9),n(8)],void 0===(i="function"==typeof(o=function(o,r,i,s,l,c,u,f){"use strict";var d=n(1);function p(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(p=function(e){return e?n:t})(e)}function h(e){var t=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}();return function(){var n,o=(0,c.default)(e);if(t){var r=(0,c.default)(this).constructor;n=Reflect.construct(o,arguments,r)}else n=o.apply(this,arguments);return(0,l.default)(this,n)}}Object.defineProperty(o,"__esModule",{value:!0}),o.default=void 0,r=d(r),i=d(i),s=d(s),l=d(l),c=d(c),u=d(u),f=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!==a(e)&&"function"!=typeof e)return{default:e};var n=p(t);if(n&&n.has(e))return n.get(e);var o={},r=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var i in e)if("default"!==i&&Object.prototype.hasOwnProperty.call(e,i)){var s=r?Object.getOwnPropertyDescriptor(e,i):null;s&&(s.get||s.set)?Object.defineProperty(o,i,s):o[i]=e[i]}return o.default=e,n&&n.set(e,o),o}(f);var v=function(e){(0,s.default)(n,e);var t=h(n);function n(){return(0,r.default)(this,n),t.apply(this,arguments)}return(0,i.default)(n,[{key:"methods",get:function(){var e=this;return{close:function(){return e.close()},open:function(){return e.open()},toggleBlockSettings:function(t){return e.toggleBlockSettings(t)}}}},{key:"open",value:function(){this.Editor.Toolbar.moveAndOpen()}},{key:"close",value:function(){this.Editor.Toolbar.close()}},{key:"toggleBlockSettings",value:function(e){-1!==this.Editor.BlockManager.currentBlockIndex?(null!=e?e:!this.Editor.BlockSettings.opened)?(this.Editor.Toolbar.moveAndOpen(),this.Editor.BlockSettings.open()):this.Editor.BlockSettings.close():f.logLabeled("Could't toggle the Toolbar because there is no block selected ","warn")}}]),n}(u.default);o.default=v,v.displayName="ToolbarAPI",e.exports=t.default})?o.apply(t,r):o)||(e.exports=i)},function(e,t,n){var o,r,i;"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self&&self,r=[t,n(2),n(3),n(5),n(6),n(4),n(9),n(115)],void 0===(i="function"==typeof(o=function(o,r,i,a,s,l,c,u){"use strict";var f=n(1);function d(e){var t=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}();return function(){var n,o=(0,l.default)(e);if(t){var r=(0,l.default)(this).constructor;n=Reflect.construct(o,arguments,r)}else n=o.apply(this,arguments);return(0,s.default)(this,n)}}Object.defineProperty(o,"__esModule",{value:!0}),o.default=void 0,r=f(r),i=f(i),a=f(a),s=f(s),l=f(l),c=f(c),u=f(u);var p=function(e){(0,a.default)(n,e);var t=d(n);function n(e){var o,i=e.config,a=e.eventsDispatcher;return(0,r.default)(this,n),(o=t.call(this,{config:i,eventsDispatcher:a})).tooltip=new u.default,o}return(0,i.default)(n,[{key:"destroy",value:function(){this.tooltip.destroy()}},{key:"methods",get:function(){var e=this;return{show:function(t,n,o){return e.show(t,n,o)},hide:function(){return e.hide()},onHover:function(t,n,o){return e.onHover(t,n,o)}}}},{key:"show",value:function(e,t,n){this.tooltip.show(e,t,n)}},{key:"hide",value:function(){this.tooltip.hide()}},{key:"onHover",value:function(e,t,n){this.tooltip.onHover(e,t,n)}}]),n}(c.default);o.default=p,p.displayName="TooltipAPI",e.exports=t.default})?o.apply(t,r):o)||(e.exports=i)},function(e,t,n){var o,r,i;"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self&&self,r=[t,n(2),n(3),n(5),n(6),n(4),n(9)],void 0===(i="function"==typeof(o=function(o,r,i,a,s,l,c){"use strict";var u=n(1);function f(e){var t=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}();return function(){var n,o=(0,l.default)(e);if(t){var r=(0,l.default)(this).constructor;n=Reflect.construct(o,arguments,r)}else n=o.apply(this,arguments);return(0,s.default)(this,n)}}Object.defineProperty(o,"__esModule",{value:!0}),o.default=void 0,r=u(r),i=u(i),a=u(a),s=u(s),l=u(l);var d=function(e){(0,a.default)(n,e);var t=f(n);function n(){return(0,r.default)(this,n),t.apply(this,arguments)}return(0,i.default)(n,[{key:"methods",get:function(){return{nodes:this.editorNodes}}},{key:"editorNodes",get:function(){return{wrapper:this.Editor.UI.nodes.wrapper,redactor:this.Editor.UI.nodes.redactor}}}]),n}((c=u(c)).default);o.default=d,d.displayName="UiAPI",e.exports=t.default})?o.apply(t,r):o)||(e.exports=i)},function(e,t,n){var o,r,i,a=n(7);"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self&&self,r=[t,n(2),n(3),n(5),n(6),n(4),n(9),n(8),n(25),n(67)],void 0===(i="function"==typeof(o=function(o,r,i,s,l,c,u,f,d,p){"use strict";var h=n(1);function v(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(v=function(e){return e?n:t})(e)}function g(e){var t=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}();return function(){var n,o=(0,c.default)(e);if(t){var r=(0,c.default)(this).constructor;n=Reflect.construct(o,arguments,r)}else n=o.apply(this,arguments);return(0,l.default)(this,n)}}Object.defineProperty(o,"__esModule",{value:!0}),o.default=void 0,r=h(r),i=h(i),s=h(s),l=h(l),c=h(c),u=h(u),f=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!==a(e)&&"function"!=typeof e)return{default:e};var n=v(t);if(n&&n.has(e))return n.get(e);var o={},r=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var i in e)if("default"!==i&&Object.prototype.hasOwnProperty.call(e,i)){var s=r?Object.getOwnPropertyDescriptor(e,i):null;s&&(s.get||s.set)?Object.defineProperty(o,i,s):o[i]=e[i]}return o.default=e,n&&n.set(e,o),o}(f),d=h(d),p=h(p);var y=function(e){(0,s.default)(n,e);var t=g(n);function n(){return(0,r.default)(this,n),t.apply(this,arguments)}return(0,i.default)(n,[{key:"keydown",value:function(e){switch(this.beforeKeydownProcessing(e),e.keyCode){case f.keyCodes.BACKSPACE:this.backspace(e);break;case f.keyCodes.ENTER:this.enter(e);break;case f.keyCodes.DOWN:case f.keyCodes.RIGHT:this.arrowRightAndDown(e);break;case f.keyCodes.UP:case f.keyCodes.LEFT:this.arrowLeftAndUp(e);break;case f.keyCodes.TAB:this.tabPressed(e)}}},{key:"beforeKeydownProcessing",value:function(e){this.needToolbarClosing(e)&&f.isPrintableKey(e.keyCode)&&(this.Editor.Toolbar.close(),this.Editor.ConversionToolbar.close(),e.ctrlKey||e.metaKey||e.altKey||e.shiftKey||(this.Editor.BlockManager.clearFocused(),this.Editor.BlockSelection.clearSelection(e)))}},{key:"keyup",value:function(e){e.shiftKey||this.Editor.UI.checkEmptiness()}},{key:"tabPressed",value:function(e){this.Editor.BlockSelection.clearSelection(e);var t=this.Editor,n=t.BlockManager,o=t.InlineToolbar,r=t.ConversionToolbar,i=n.currentBlock;if(i){var a=i.isEmpty,s=i.tool.isDefault&&a,l=!a&&r.opened,c=!a&&!d.default.isCollapsed&&o.opened,u=!l&&!c;s?this.activateToolbox():u&&this.activateBlockSettings()}}},{key:"dragOver",value:function(e){this.Editor.BlockManager.getBlockByChildNode(e.target).dropTarget=!0}},{key:"dragLeave",value:function(e){this.Editor.BlockManager.getBlockByChildNode(e.target).dropTarget=!1}},{key:"handleCommandC",value:function(e){var t=this.Editor.BlockSelection;t.anyBlockSelected&&t.copySelectedBlocks(e)}},{key:"handleCommandX",value:function(e){var t=this.Editor,n=t.BlockSelection,o=t.BlockManager,r=t.Caret;n.anyBlockSelected&&n.copySelectedBlocks(e).then((function(){var t=o.removeSelectedBlocks(),i=o.insertDefaultBlockAtIndex(t,!0);r.setToBlock(i,r.positions.START),n.clearSelection(e)}))}},{key:"enter",value:function(e){var t=this.Editor,n=t.BlockManager,o=t.UI;if(!n.currentBlock.tool.isLineBreaksEnabled&&!(o.someToolbarOpened&&o.someFlipperButtonFocused||e.shiftKey)){var r=this.Editor.BlockManager.currentBlock;this.Editor.Caret.isAtStart&&!this.Editor.BlockManager.currentBlock.hasMedia?this.Editor.BlockManager.insertDefaultBlockAtIndex(this.Editor.BlockManager.currentBlockIndex):r=this.Editor.BlockManager.split(),this.Editor.Caret.setToBlock(r),this.Editor.Toolbar.moveAndOpen(r),e.preventDefault()}}},{key:"backspace",value:function(e){var t=this.Editor,n=t.BlockManager,o=t.BlockSelection,r=t.Caret,i=n.currentBlock,a=i.tool;if(i.selected||i.isEmpty&&i.currentInput===i.firstInput){e.preventDefault();var s=n.currentBlockIndex;return n.previousBlock&&0===n.previousBlock.inputs.length?n.removeBlock(s-1):n.removeBlock(),r.setToBlock(n.currentBlock,s?r.positions.END:r.positions.START),this.Editor.Toolbar.close(),void o.clearSelection(e)}if(!a.isLineBreaksEnabled||r.isAtStart){var l=0===n.currentBlockIndex;r.isAtStart&&d.default.isCollapsed&&i.currentInput===i.firstInput&&!l&&(e.preventDefault(),this.mergeBlocks())}}},{key:"mergeBlocks",value:function(){var e=this.Editor,t=e.BlockManager,n=e.Caret,o=e.Toolbar,r=t.previousBlock,i=t.currentBlock;if(i.name!==r.name||!r.mergeable)return 0===r.inputs.length||r.isEmpty?(t.removeBlock(t.currentBlockIndex-1),n.setToBlock(t.currentBlock),void o.close()):void(n.navigatePrevious()&&o.close());n.createShadow(r.pluginsContent),t.mergeBlocks(r,i).then((function(){n.restoreCaret(r.pluginsContent),r.pluginsContent.normalize(),o.close()}))}},{key:"arrowRightAndDown",value:function(e){var t=this,n=p.default.usedKeys.includes(e.keyCode)&&(!e.shiftKey||e.keyCode===f.keyCodes.TAB);if(!this.Editor.UI.someToolbarOpened||!n){this.Editor.BlockManager.clearFocused(),this.Editor.Toolbar.close();var o=this.Editor.Caret.isAtEnd||this.Editor.BlockSelection.anyBlockSelected;e.shiftKey&&e.keyCode===f.keyCodes.DOWN&&o?this.Editor.CrossBlockSelection.toggleBlockSelectedState():((e.keyCode===f.keyCodes.DOWN||e.keyCode===f.keyCodes.RIGHT&&!this.isRtl?this.Editor.Caret.navigateNext():this.Editor.Caret.navigatePrevious())?e.preventDefault():f.delay((function(){t.Editor.BlockManager.currentBlock&&t.Editor.BlockManager.currentBlock.updateCurrentInput()}),20)(),this.Editor.BlockSelection.clearSelection(e))}}},{key:"arrowLeftAndUp",value:function(e){var t=this;if(this.Editor.UI.someToolbarOpened){if(p.default.usedKeys.includes(e.keyCode)&&(!e.shiftKey||e.keyCode===f.keyCodes.TAB))return;this.Editor.UI.closeAllToolbars()}this.Editor.BlockManager.clearFocused(),this.Editor.Toolbar.close();var n=this.Editor.Caret.isAtStart||this.Editor.BlockSelection.anyBlockSelected;e.shiftKey&&e.keyCode===f.keyCodes.UP&&n?this.Editor.CrossBlockSelection.toggleBlockSelectedState(!1):((e.keyCode===f.keyCodes.UP||e.keyCode===f.keyCodes.LEFT&&!this.isRtl?this.Editor.Caret.navigatePrevious():this.Editor.Caret.navigateNext())?e.preventDefault():f.delay((function(){t.Editor.BlockManager.currentBlock&&t.Editor.BlockManager.currentBlock.updateCurrentInput()}),20)(),this.Editor.BlockSelection.clearSelection(e))}},{key:"needToolbarClosing",value:function(e){var t=e.keyCode===f.keyCodes.ENTER&&this.Editor.Toolbar.toolbox.opened,n=e.keyCode===f.keyCodes.ENTER&&this.Editor.BlockSettings.opened,o=e.keyCode===f.keyCodes.ENTER&&this.Editor.InlineToolbar.opened,r=e.keyCode===f.keyCodes.ENTER&&this.Editor.ConversionToolbar.opened,i=e.keyCode===f.keyCodes.TAB;return!(e.shiftKey||i||t||n||o||r)}},{key:"activateToolbox",value:function(){this.Editor.Toolbar.opened||this.Editor.Toolbar.moveAndOpen(),this.Editor.Toolbar.toolbox.open()}},{key:"activateBlockSettings",value:function(){this.Editor.Toolbar.opened||(this.Editor.BlockManager.currentBlock.focused=!0,this.Editor.Toolbar.moveAndOpen()),this.Editor.BlockSettings.opened||this.Editor.BlockSettings.open()}}]),n}(u.default);o.default=y,y.displayName="BlockEvents",e.exports=t.default})?o.apply(t,r):o)||(e.exports=i)},function(e,t,n){var o,r,i,a=n(7);"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self&&self,r=[t,n(20),n(21),n(2),n(3),n(5),n(6),n(4),n(61),n(9),n(19),n(8),n(383),n(113),n(384)],void 0===(i="function"==typeof(o=function(o,r,i,s,l,c,u,f,d,p,h,v,g,y,k){"use strict";var b=n(1);function m(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(m=function(e){return e?n:t})(e)}function w(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!==a(e)&&"function"!=typeof e)return{default:e};var n=m(t);if(n&&n.has(e))return n.get(e);var o={},r=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var i in e)if("default"!==i&&Object.prototype.hasOwnProperty.call(e,i)){var s=r?Object.getOwnPropertyDescriptor(e,i):null;s&&(s.get||s.set)?Object.defineProperty(o,i,s):o[i]=e[i]}return o.default=e,n&&n.set(e,o),o}function x(e){var t=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}();return function(){var n,o=(0,f.default)(e);if(t){var r=(0,f.default)(this).constructor;n=Reflect.construct(o,arguments,r)}else n=o.apply(this,arguments);return(0,u.default)(this,n)}}Object.defineProperty(o,"__esModule",{value:!0}),o.default=void 0,r=b(r),i=b(i),s=b(s),l=b(l),c=b(c),u=b(u),f=b(f),d=w(d),p=b(p),h=b(h),v=w(v),g=b(g),y=b(y);var C=function(e){(0,c.default)(a,e);var t,n,o=x(a);function a(){var e;return(0,s.default)(this,a),(e=o.apply(this,arguments))._currentBlockIndex=-1,e._blocks=null,e}return(0,l.default)(a,[{key:"currentBlockIndex",get:function(){return this._currentBlockIndex},set:function(e){this._blocks[this._currentBlockIndex]&&this._blocks[this._currentBlockIndex].willUnselect(),this._blocks[e]&&this._blocks[e].willSelect(),this._currentBlockIndex=e}},{key:"firstBlock",get:function(){return this._blocks[0]}},{key:"lastBlock",get:function(){return this._blocks[this._blocks.length-1]}},{key:"currentBlock",get:function(){return this._blocks[this.currentBlockIndex]},set:function(e){this.currentBlockIndex=this.getBlockIndex(e)}},{key:"nextBlock",get:function(){return this.currentBlockIndex===this._blocks.length-1?null:this._blocks[this.currentBlockIndex+1]}},{key:"nextContentfulBlock",get:function(){return this.blocks.slice(this.currentBlockIndex+1).find((function(e){return!!e.inputs.length}))}},{key:"previousContentfulBlock",get:function(){return this.blocks.slice(0,this.currentBlockIndex).reverse().find((function(e){return!!e.inputs.length}))}},{key:"previousBlock",get:function(){return 0===this.currentBlockIndex?null:this._blocks[this.currentBlockIndex-1]}},{key:"blocks",get:function(){return this._blocks.array}},{key:"isEditorEmpty",get:function(){return this.blocks.every((function(e){return e.isEmpty}))}},{key:"prepare",value:function(){var e=this,t=new g.default(this.Editor.UI.nodes.redactor);this._blocks=new Proxy(t,{set:g.default.set,get:g.default.get}),this.listeners.on(document,"copy",(function(t){return e.Editor.BlockEvents.handleCommandC(t)}))}},{key:"toggleReadOnly",value:function(e){e?this.disableModuleBindings():this.enableModuleBindings()}},{key:"composeBlock",value:function(e){var t=e.tool,n=e.data,o=void 0===n?{}:n,r=e.id,i=void 0===r?void 0:r,a=e.tunes,s=void 0===a?{}:a,l=this.Editor.ReadOnly.isEnabled,c=this.Editor.Tools.blockTools.get(t),u=new d.default({id:i,data:o,tool:c,api:this.Editor.API,readOnly:l,tunesData:s});return l||this.bindBlockEvents(u),u}},{key:"insert",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=e.id,n=void 0===t?void 0:t,o=e.tool,r=void 0===o?this.config.defaultBlock:o,i=e.data,a=void 0===i?{}:i,s=e.index,l=e.needToFocus,c=void 0===l||l,u=e.replace,f=void 0!==u&&u,d=e.tunes,p=void 0===d?{}:d,h=s;void 0===h&&(h=this.currentBlockIndex+(f?0:1));var v=this.composeBlock({id:n,tool:r,data:a,tunes:p});return f&&this.blockDidMutated(k.BlockMutationType.Removed,this.getBlockByIndex(h),{index:h}),this._blocks.insert(h,v,f),this.blockDidMutated(k.BlockMutationType.Added,v,{index:h}),c?this.currentBlockIndex=h:h<=this.currentBlockIndex&&this.currentBlockIndex++,v}},{key:"replace",value:function(e){var t=e.tool,n=void 0===t?this.config.defaultBlock:t,o=e.data,r=void 0===o?{}:o;return this.insert({tool:n,data:r,index:this.currentBlockIndex,replace:!0})}},{key:"paste",value:function(e,t){var n=arguments.length>2&&void 0!==arguments[2]&&arguments[2],o=this.insert({tool:e,replace:n});try{o.call(d.BlockToolAPI.ON_PASTE,t)}catch(t){v.log("".concat(e,": onPaste callback call is failed"),"error",t)}return o}},{key:"insertDefaultBlockAtIndex",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1],n=this.composeBlock({tool:this.config.defaultBlock});return this._blocks[e]=n,this.blockDidMutated(k.BlockMutationType.Added,n,{index:e}),t?this.currentBlockIndex=e:e<=this.currentBlockIndex&&this.currentBlockIndex++,n}},{key:"insertAtEnd",value:function(){return this.currentBlockIndex=this.blocks.length-1,this.insert()}},{key:"mergeBlocks",value:(n=(0,i.default)(r.default.mark((function e(t,n){var o,i;return r.default.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(o=this._blocks.indexOf(n),!n.isEmpty){e.next=3;break}return e.abrupt("return");case 3:return e.next=5,n.data;case 5:if(i=e.sent,v.isEmpty(i)){e.next=9;break}return e.next=9,t.mergeWith(i);case 9:this.removeBlock(o),this.currentBlockIndex=this._blocks.indexOf(t);case 11:case"end":return e.stop()}}),e,this)}))),function(e,t){return n.apply(this,arguments)})},{key:"removeBlock",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:this.currentBlockIndex;if(!this.validateIndex(e))throw new Error("Can't find a Block to remove");var t=this._blocks[e];t.destroy(),this._blocks.remove(e),this.blockDidMutated(k.BlockMutationType.Removed,t,{index:e}),this.currentBlockIndex>=e&&this.currentBlockIndex--,this.blocks.length?0===e&&(this.currentBlockIndex=0):(this.currentBlockIndex=-1,this.insert())}},{key:"removeSelectedBlocks",value:function(){for(var e,t=this.blocks.length-1;t>=0;t--)this.blocks[t].selected&&(this.removeBlock(t),e=t);return e}},{key:"removeAllBlocks",value:function(){for(var e=this.blocks.length-1;e>=0;e--)this._blocks.remove(e);this.currentBlockIndex=-1,this.insert(),this.currentBlock.firstInput.focus()}},{key:"split",value:function(){var e=this.Editor.Caret.extractFragmentFromCaretPosition(),t=h.default.make("div");t.appendChild(e);var n={text:h.default.isEmpty(t)?"":t.innerHTML};return this.insert({data:n})}},{key:"getBlockByIndex",value:function(e){return-1===e&&(e=this._blocks.length-1),this._blocks[e]}},{key:"getBlockIndex",value:function(e){return this._blocks.indexOf(e)}},{key:"getBlockById",value:function(e){return this._blocks.array.find((function(t){return t.id===e}))}},{key:"getBlock",value:function(e){h.default.isElement(e)||(e=e.parentNode);var t=this._blocks.nodes,n=e.closest(".".concat(d.default.CSS.wrapper)),o=t.indexOf(n);if(o>=0)return this._blocks[o]}},{key:"highlightCurrentNode",value:function(){this.clearFocused(),this.currentBlock.focused=!0}},{key:"clearFocused",value:function(){this.blocks.forEach((function(e){e.focused=!1}))}},{key:"setCurrentBlockByChildNode",value:function(e){h.default.isElement(e)||(e=e.parentNode);var t=e.closest(".".concat(d.default.CSS.wrapper));if(t){var n=t.closest(".".concat(this.Editor.UI.CSS.editorWrapper));if(null==n?void 0:n.isEqualNode(this.Editor.UI.nodes.wrapper))return this.currentBlockIndex=this._blocks.nodes.indexOf(t),this.currentBlock.updateCurrentInput(),this.currentBlock}}},{key:"getBlockByChildNode",value:function(e){h.default.isElement(e)||(e=e.parentNode);var t=e.closest(".".concat(d.default.CSS.wrapper));return this.blocks.find((function(e){return e.holder===t}))}},{key:"swap",value:function(e,t){this._blocks.swap(e,t),this.currentBlockIndex=t}},{key:"move",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:this.currentBlockIndex;isNaN(e)||isNaN(t)?v.log("Warning during 'move' call: incorrect indices provided.","warn"):this.validateIndex(e)&&this.validateIndex(t)?(this._blocks.move(e,t),this.currentBlockIndex=e,this.blockDidMutated(k.BlockMutationType.Moved,this.currentBlock,{fromIndex:t,toIndex:e})):v.log("Warning during 'move' call: indices cannot be lower than 0 or greater than the amount of blocks.","warn")}},{key:"dropPointer",value:function(){this.currentBlockIndex=-1,this.clearFocused()}},{key:"clear",value:function(){var e=arguments.length>0&&void 0!==arguments[0]&&arguments[0];this._blocks.removeAll(),this.dropPointer(),e&&this.insert(),this.Editor.UI.checkEmptiness()}},{key:"destroy",value:(t=(0,i.default)(r.default.mark((function e(){return r.default.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,Promise.all(this.blocks.map((function(e){return e.destroy()})));case 2:case"end":return e.stop()}}),e,this)}))),function(){return t.apply(this,arguments)})},{key:"bindBlockEvents",value:function(e){var t=this,n=this.Editor.BlockEvents;this.readOnlyMutableListeners.on(e.holder,"keydown",(function(e){n.keydown(e)})),this.readOnlyMutableListeners.on(e.holder,"keyup",(function(e){n.keyup(e)})),this.readOnlyMutableListeners.on(e.holder,"dragover",(function(e){n.dragOver(e)})),this.readOnlyMutableListeners.on(e.holder,"dragleave",(function(e){n.dragLeave(e)})),e.on("didMutated",(function(e){return t.blockDidMutated(k.BlockMutationType.Changed,e,{index:t.getBlockIndex(e)})}))}},{key:"disableModuleBindings",value:function(){this.readOnlyMutableListeners.clearAll()}},{key:"enableModuleBindings",value:function(){var e=this;this.readOnlyMutableListeners.on(document,"cut",(function(t){return e.Editor.BlockEvents.handleCommandX(t)})),this.blocks.forEach((function(t){e.bindBlockEvents(t)}))}},{key:"validateIndex",value:function(e){return!(e<0||e>=this._blocks.length)}},{key:"blockDidMutated",value:function(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},o=new CustomEvent(e,{detail:Object.assign({target:new y.default(t)},n)});return this.Editor.ModificationsObserver.onChange(o),t}}]),a}(p.default);o.default=C,C.displayName="BlockManager",e.exports=t.default})?o.apply(t,r):o)||(e.exports=i)},function(e,t,n){var o,r,i,a=n(7);"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self&&self,r=[t,n(2),n(3),n(5),n(6),n(4),n(9),n(8),n(19),n(116),n(25),n(66)],void 0===(i="function"==typeof(o=function(o,r,i,s,l,c,u,f,d,p,h,v){"use strict";var g=n(1);function y(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(y=function(e){return e?n:t})(e)}function k(e){var t=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}();return function(){var n,o=(0,c.default)(e);if(t){var r=(0,c.default)(this).constructor;n=Reflect.construct(o,arguments,r)}else n=o.apply(this,arguments);return(0,l.default)(this,n)}}Object.defineProperty(o,"__esModule",{value:!0}),o.default=void 0,r=g(r),i=g(i),s=g(s),l=g(l),c=g(c),u=g(u),f=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!==a(e)&&"function"!=typeof e)return{default:e};var n=y(t);if(n&&n.has(e))return n.get(e);var o={},r=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var i in e)if("default"!==i&&Object.prototype.hasOwnProperty.call(e,i)){var s=r?Object.getOwnPropertyDescriptor(e,i):null;s&&(s.get||s.set)?Object.defineProperty(o,i,s):o[i]=e[i]}return o.default=e,n&&n.set(e,o),o}(f),d=g(d),p=g(p),h=g(h);var b=function(e){(0,s.default)(n,e);var t=k(n);function n(){var e;return(0,r.default)(this,n),(e=t.apply(this,arguments)).anyBlockSelectedCache=null,e.needToSelectAll=!1,e.nativeInputSelected=!1,e.readyToBlockSelection=!1,e}return(0,i.default)(n,[{key:"sanitizerConfig",get:function(){return{p:{},h1:{},h2:{},h3:{},h4:{},h5:{},h6:{},ol:{},ul:{},li:{},br:!0,img:{src:!0,width:!0,height:!0},a:{href:!0},b:{},i:{},u:{}}}},{key:"allBlocksSelected",get:function(){return this.Editor.BlockManager.blocks.every((function(e){return!0===e.selected}))},set:function(e){this.Editor.BlockManager.blocks.forEach((function(t){t.selected=e})),this.clearCache()}},{key:"anyBlockSelected",get:function(){var e=this.Editor.BlockManager;return null===this.anyBlockSelectedCache&&(this.anyBlockSelectedCache=e.blocks.some((function(e){return!0===e.selected}))),this.anyBlockSelectedCache}},{key:"selectedBlocks",get:function(){return this.Editor.BlockManager.blocks.filter((function(e){return e.selected}))}},{key:"prepare",value:function(){var e=this;this.selection=new h.default,p.default.add({name:"CMD+A",handler:function(t){var n=e.Editor,o=n.BlockManager;if(n.ReadOnly.isEnabled)return t.preventDefault(),void e.selectAllBlocks();o.currentBlock&&e.handleCommandA(t)},on:this.Editor.UI.nodes.redactor})}},{key:"toggleReadOnly",value:function(){h.default.get().removeAllRanges(),this.allBlocksSelected=!1}},{key:"unSelectBlockByIndex",value:function(e){var t=this.Editor.BlockManager;(isNaN(e)?t.currentBlock:t.getBlockByIndex(e)).selected=!1,this.clearCache()}},{key:"clearSelection",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1],n=this.Editor,o=n.BlockManager,r=n.Caret,i=n.RectangleSelection;this.needToSelectAll=!1,this.nativeInputSelected=!1,this.readyToBlockSelection=!1;var a=e&&e instanceof KeyboardEvent,s=a&&f.isPrintableKey(e.keyCode);if(this.anyBlockSelected&&a&&s&&!h.default.isSelectionExists){var l=o.removeSelectedBlocks();o.insertDefaultBlockAtIndex(l,!0),r.setToBlock(o.currentBlock),f.delay((function(){var t=e.key;r.insertContentAtCaretPosition(t.length>1?"":t)}),20)()}this.Editor.CrossBlockSelection.clear(e),this.anyBlockSelected&&!i.isRectActivated()?(t&&this.selection.restore(),this.allBlocksSelected=!1):this.Editor.RectangleSelection.clearSelection()}},{key:"copySelectedBlocks",value:function(e){var t=this;e.preventDefault();var n=d.default.make("div");this.selectedBlocks.forEach((function(e){var o=(0,v.clean)(e.holder.innerHTML,t.sanitizerConfig),r=d.default.make("p");r.innerHTML=o,n.appendChild(r)}));var o=Array.from(n.childNodes).map((function(e){return e.textContent})).join("\n\n"),r=n.innerHTML;return e.clipboardData.setData("text/plain",o),e.clipboardData.setData("text/html",r),Promise.all(this.selectedBlocks.map((function(e){return e.save()}))).then((function(n){try{e.clipboardData.setData(t.Editor.Paste.MIME_TYPE,JSON.stringify(n))}catch(e){}}))}},{key:"selectBlockByIndex",value:function(e){var t,n=this.Editor.BlockManager;n.clearFocused(),t=isNaN(e)?n.currentBlock:n.getBlockByIndex(e),this.selection.save(),h.default.get().removeAllRanges(),t.selected=!0,this.clearCache(),this.Editor.InlineToolbar.close()}},{key:"clearCache",value:function(){this.anyBlockSelectedCache=null}},{key:"destroy",value:function(){p.default.remove(this.Editor.UI.nodes.redactor,"CMD+A")}},{key:"handleCommandA",value:function(e){if(this.Editor.RectangleSelection.clearSelection(),!d.default.isNativeInput(e.target)||this.readyToBlockSelection){var t=this.Editor.BlockManager.getBlock(e.target).inputs;t.length>1&&!this.readyToBlockSelection?this.readyToBlockSelection=!0:1!==t.length||this.needToSelectAll?this.needToSelectAll?(e.preventDefault(),this.selectAllBlocks(),this.needToSelectAll=!1,this.readyToBlockSelection=!1,this.Editor.ConversionToolbar.close()):this.readyToBlockSelection&&(e.preventDefault(),this.selectBlockByIndex(),this.needToSelectAll=!0):this.needToSelectAll=!0}else this.readyToBlockSelection=!0}},{key:"selectAllBlocks",value:function(){this.selection.save(),h.default.get().removeAllRanges(),this.allBlocksSelected=!0,this.Editor.InlineToolbar.close()}}]),n}(u.default);o.default=b,b.displayName="BlockSelection",e.exports=t.default})?o.apply(t,r):o)||(e.exports=i)},function(e,t,n){var o,r,i,a=n(7);"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self&&self,r=[t,n(2),n(3),n(5),n(6),n(4),n(25),n(9),n(19),n(8)],void 0===(i="function"==typeof(o=function(o,r,i,s,l,c,u,f,d,p){"use strict";var h=n(1);function v(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(v=function(e){return e?n:t})(e)}function g(e){var t=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}();return function(){var n,o=(0,c.default)(e);if(t){var r=(0,c.default)(this).constructor;n=Reflect.construct(o,arguments,r)}else n=o.apply(this,arguments);return(0,l.default)(this,n)}}Object.defineProperty(o,"__esModule",{value:!0}),o.default=void 0,r=h(r),i=h(i),s=h(s),l=h(l),c=h(c),u=h(u),f=h(f),d=h(d),p=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!==a(e)&&"function"!=typeof e)return{default:e};var n=v(t);if(n&&n.has(e))return n.get(e);var o={},r=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var i in e)if("default"!==i&&Object.prototype.hasOwnProperty.call(e,i)){var s=r?Object.getOwnPropertyDescriptor(e,i):null;s&&(s.get||s.set)?Object.defineProperty(o,i,s):o[i]=e[i]}return o.default=e,n&&n.set(e,o),o}(p);var y=function(e){(0,s.default)(n,e);var t=g(n);function n(){return(0,r.default)(this,n),t.apply(this,arguments)}return(0,i.default)(n,[{key:"positions",get:function(){return{START:"start",END:"end",DEFAULT:"default"}}},{key:"isAtStart",get:function(){var e=u.default.get(),t=d.default.getDeepestNode(this.Editor.BlockManager.currentBlock.currentInput),n=e.focusNode;if(d.default.isNativeInput(t))return 0===t.selectionEnd;if(!e.anchorNode)return!1;var o=n.textContent.search(/\S/);-1===o&&(o=0);var r=e.focusOffset;return n.nodeType!==Node.TEXT_NODE&&n.childNodes.length&&(n.childNodes[r]?(n=n.childNodes[r],r=0):r=(n=n.childNodes[r-1]).textContent.length),!(!d.default.isLineBreakTag(t)&&!d.default.isEmpty(t)||!this.getHigherLevelSiblings(n,"left").every((function(e){var t=d.default.isLineBreakTag(e),n=1===e.children.length&&d.default.isLineBreakTag(e.children[0]),o=t||n;return d.default.isEmpty(e)&&!o}))||r!==o)||(null===t||n===t&&r<=o)}},{key:"isAtEnd",get:function(){var e=u.default.get(),t=e.focusNode,n=d.default.getDeepestNode(this.Editor.BlockManager.currentBlock.currentInput,!0);if(d.default.isNativeInput(n))return n.selectionEnd===n.value.length;if(!e.focusNode)return!1;var o=e.focusOffset;if(t.nodeType!==Node.TEXT_NODE&&t.childNodes.length&&(t.childNodes[o-1]?o=(t=t.childNodes[o-1]).textContent.length:(t=t.childNodes[0],o=0)),d.default.isLineBreakTag(n)||d.default.isEmpty(n)){var r=this.getHigherLevelSiblings(t,"right");if(r.every((function(e,t){return t===r.length-1&&d.default.isLineBreakTag(e)||d.default.isEmpty(e)&&!d.default.isLineBreakTag(e)}))&&o===t.textContent.length)return!0}var i=n.textContent.replace(/\s+$/,"");return t===n&&o>=i.length}},{key:"setToBlock",value:function(e){var t,n=this,o=arguments.length>1&&void 0!==arguments[1]?arguments[1]:this.positions.DEFAULT,r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0,i=this.Editor.BlockManager;switch(o){case this.positions.START:t=e.firstInput;break;case this.positions.END:t=e.lastInput;break;default:t=e.currentInput}if(t){var a=d.default.getDeepestNode(t,o===this.positions.END),s=d.default.getContentLength(a);switch(!0){case o===this.positions.START:r=0;break;case o===this.positions.END:case r>s:r=s}p.delay((function(){n.set(a,r)}),20)(),i.setCurrentBlockByChildNode(e.holder),i.currentBlock.currentInput=t}}},{key:"setToInput",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:this.positions.DEFAULT,n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0,o=this.Editor.BlockManager.currentBlock,r=d.default.getDeepestNode(e);switch(t){case this.positions.START:this.set(r,0);break;case this.positions.END:this.set(r,d.default.getContentLength(r));break;default:n&&this.set(r,n)}o.currentInput=e}},{key:"set",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,n=u.default.setCursor(e,t),o=n.top,r=n.bottom,i=window,a=i.innerHeight;o<0&&window.scrollBy(0,o),r>a&&window.scrollBy(0,r-a)}},{key:"setToTheLastBlock",value:function(){var e=this.Editor.BlockManager.lastBlock;if(e)if(e.tool.isDefault&&e.isEmpty)this.setToBlock(e);else{var t=this.Editor.BlockManager.insertAtEnd();this.setToBlock(t)}}},{key:"extractFragmentFromCaretPosition",value:function(){var e=u.default.get();if(e.rangeCount){var t=e.getRangeAt(0),n=this.Editor.BlockManager.currentBlock.currentInput;if(t.deleteContents(),n){if(d.default.isNativeInput(n)){var o=n,r=document.createDocumentFragment(),i=o.value.substring(0,o.selectionStart),a=o.value.substring(o.selectionStart);return r.textContent=a,o.value=i,r}var s=t.cloneRange();return s.selectNodeContents(n),s.setStart(t.endContainer,t.endOffset),s.extractContents()}}}},{key:"navigateNext",value:function(){var e=this.Editor.BlockManager,t=e.currentBlock,n=e.nextContentfulBlock,o=t.nextInput,r=this.isAtEnd,i=n;if(!i&&!o){if(t.tool.isDefault||!r)return!1;i=e.insertAtEnd()}return!!r&&(o?this.setToInput(o,this.positions.START):this.setToBlock(i,this.positions.START),!0)}},{key:"navigatePrevious",value:function(){var e=this.Editor.BlockManager,t=e.currentBlock,n=e.previousContentfulBlock;if(!t)return!1;var o=t.previousInput;return!(!n&&!o||!this.isAtStart||(o?this.setToInput(o,this.positions.END):this.setToBlock(n,this.positions.END),0))}},{key:"createShadow",value:function(e){var t=document.createElement("span");t.classList.add(n.CSS.shadowCaret),e.insertAdjacentElement("beforeend",t)}},{key:"restoreCaret",value:function(e){var t=e.querySelector(".".concat(n.CSS.shadowCaret));t&&((new u.default).expandToTag(t),setTimeout((function(){var e=document.createRange();e.selectNode(t),e.extractContents()}),50))}},{key:"insertContentAtCaretPosition",value:function(e){var t=document.createDocumentFragment(),n=document.createElement("div"),o=u.default.get(),r=u.default.range;n.innerHTML=e,Array.from(n.childNodes).forEach((function(e){return t.appendChild(e)})),0===t.childNodes.length&&t.appendChild(new Text);var i=t.lastChild;r.deleteContents(),r.insertNode(t);var a=document.createRange();a.setStart(i,i.textContent.length),o.removeAllRanges(),o.addRange(a)}},{key:"getHigherLevelSiblings",value:function(e,t){for(var n=e,o=[];n.parentNode&&"true"!==n.parentNode.contentEditable;)n=n.parentNode;for(var r="left"===t?"previousSibling":"nextSibling";n[r];)n=n[r],o.push(n);return o}}],[{key:"CSS",get:function(){return{shadowCaret:"cdx-shadow-caret"}}}]),n}(f.default);o.default=y,y.displayName="Caret",e.exports=t.default})?o.apply(t,r):o)||(e.exports=i)},function(e,t,n){var o,r,i,a=n(7);"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self&&self,r=[t,n(20),n(21),n(2),n(3),n(5),n(6),n(4),n(9),n(25),n(8)],void 0===(i="function"==typeof(o=function(o,r,i,s,l,c,u,f,d,p,h){"use strict";var v=n(1);function g(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(g=function(e){return e?n:t})(e)}function y(e){var t=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}();return function(){var n,o=(0,f.default)(e);if(t){var r=(0,f.default)(this).constructor;n=Reflect.construct(o,arguments,r)}else n=o.apply(this,arguments);return(0,u.default)(this,n)}}Object.defineProperty(o,"__esModule",{value:!0}),o.default=void 0,r=v(r),i=v(i),s=v(s),l=v(l),c=v(c),u=v(u),f=v(f),d=v(d),p=v(p),h=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!==a(e)&&"function"!=typeof e)return{default:e};var n=g(t);if(n&&n.has(e))return n.get(e);var o={},r=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var i in e)if("default"!==i&&Object.prototype.hasOwnProperty.call(e,i)){var s=r?Object.getOwnPropertyDescriptor(e,i):null;s&&(s.get||s.set)?Object.defineProperty(o,i,s):o[i]=e[i]}return o.default=e,n&&n.set(e,o),o}(h);var k=function(e){(0,c.default)(o,e);var t,n=y(o);function o(){var e;return(0,s.default)(this,o),(e=n.apply(this,arguments)).onMouseUp=function(){e.listeners.off(document,"mouseover",e.onMouseOver),e.listeners.off(document,"mouseup",e.onMouseUp)},e.onMouseOver=function(t){var n=e.Editor,o=n.BlockManager,r=n.BlockSelection,i=o.getBlockByChildNode(t.relatedTarget)||e.lastSelectedBlock,a=o.getBlockByChildNode(t.target);if(i&&a&&a!==i){if(i===e.firstSelectedBlock)return p.default.get().removeAllRanges(),i.selected=!0,a.selected=!0,void r.clearCache();if(a===e.firstSelectedBlock)return i.selected=!1,a.selected=!1,void r.clearCache();e.Editor.InlineToolbar.close(),e.toggleBlocksSelectedState(i,a),e.lastSelectedBlock=a}},e}return(0,l.default)(o,[{key:"prepare",value:(t=(0,i.default)(r.default.mark((function e(){var t=this;return r.default.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:this.listeners.on(document,"mousedown",(function(e){t.enableCrossBlockSelection(e)}));case 1:case"end":return e.stop()}}),e,this)}))),function(){return t.apply(this,arguments)})},{key:"watchSelection",value:function(e){if(e.button===h.mouseButtons.LEFT){var t=this.Editor.BlockManager;this.firstSelectedBlock=t.getBlock(e.target),this.lastSelectedBlock=this.firstSelectedBlock,this.listeners.on(document,"mouseover",this.onMouseOver),this.listeners.on(document,"mouseup",this.onMouseUp)}}},{key:"isCrossBlockSelectionStarted",get:function(){return!!this.firstSelectedBlock&&!!this.lastSelectedBlock}},{key:"toggleBlockSelectedState",value:function(){var e=!(arguments.length>0&&void 0!==arguments[0])||arguments[0],t=this.Editor,n=t.BlockManager,o=t.BlockSelection;this.lastSelectedBlock||(this.lastSelectedBlock=this.firstSelectedBlock=n.currentBlock),this.firstSelectedBlock===this.lastSelectedBlock&&(this.firstSelectedBlock.selected=!0,o.clearCache(),p.default.get().removeAllRanges());var r=n.blocks.indexOf(this.lastSelectedBlock)+(e?1:-1),i=n.blocks[r];i&&(this.lastSelectedBlock.selected!==i.selected?(i.selected=!0,o.clearCache()):(this.lastSelectedBlock.selected=!1,o.clearCache()),this.lastSelectedBlock=i,this.Editor.InlineToolbar.close(),i.holder.scrollIntoView({block:"nearest"}))}},{key:"clear",value:function(e){var t=this.Editor,n=t.BlockManager,o=t.BlockSelection,r=t.Caret,i=n.blocks.indexOf(this.firstSelectedBlock),a=n.blocks.indexOf(this.lastSelectedBlock);if(o.anyBlockSelected&&i>-1&&a>-1)if(e&&e instanceof KeyboardEvent)switch(e.keyCode){case h.keyCodes.DOWN:case h.keyCodes.RIGHT:r.setToBlock(n.blocks[Math.max(i,a)],r.positions.END);break;case h.keyCodes.UP:case h.keyCodes.LEFT:r.setToBlock(n.blocks[Math.min(i,a)],r.positions.START);break;default:r.setToBlock(n.blocks[Math.max(i,a)],r.positions.END)}else r.setToBlock(n.blocks[Math.max(i,a)],r.positions.END);this.firstSelectedBlock=this.lastSelectedBlock=null}},{key:"enableCrossBlockSelection",value:function(e){var t=this.Editor.UI;p.default.isCollapsed||this.Editor.BlockSelection.clearSelection(e),t.nodes.redactor.contains(e.target)?this.watchSelection(e):this.Editor.BlockSelection.clearSelection(e)}},{key:"toggleBlocksSelectedState",value:function(e,t){for(var n=this.Editor,o=n.BlockManager,r=n.BlockSelection,i=o.blocks.indexOf(e),a=o.blocks.indexOf(t),s=e.selected!==t.selected,l=Math.min(i,a);l<=Math.max(i,a);l++){var c=o.blocks[l];c!==this.firstSelectedBlock&&c!==(s?e:t)&&(o.blocks[l].selected=!o.blocks[l].selected,r.clearCache())}}}]),o}(d.default);o.default=k,k.displayName="CrossBlockSelection",e.exports=t.default})?o.apply(t,r):o)||(e.exports=i)},function(e,t,n){var o,r,i;"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self&&self,r=[t,n(20),n(21),n(2),n(3),n(5),n(6),n(4),n(25),n(9)],void 0===(i="function"==typeof(o=function(o,r,i,a,s,l,c,u,f,d){"use strict";var p=n(1);function h(e){var t=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}();return function(){var n,o=(0,u.default)(e);if(t){var r=(0,u.default)(this).constructor;n=Reflect.construct(o,arguments,r)}else n=o.apply(this,arguments);return(0,c.default)(this,n)}}Object.defineProperty(o,"__esModule",{value:!0}),o.default=void 0,r=p(r),i=p(i),a=p(a),s=p(s),l=p(l),c=p(c),u=p(u),f=p(f);var v=function(e){(0,l.default)(o,e);var t,n=h(o);function o(){var e;return(0,a.default)(this,o),(e=n.apply(this,arguments)).isStartedAtEditor=!1,e}return(0,s.default)(o,[{key:"toggleReadOnly",value:function(e){e?this.disableModuleBindings():this.enableModuleBindings()}},{key:"enableModuleBindings",value:function(){var e=this,t=this.Editor.UI;this.readOnlyMutableListeners.on(t.nodes.holder,"drop",function(){var t=(0,i.default)(r.default.mark((function t(n){return r.default.wrap((function(t){for(;;)switch(t.prev=t.next){case 0:return t.next=2,e.processDrop(n);case 2:case"end":return t.stop()}}),t)})));return function(e){return t.apply(this,arguments)}}(),!0),this.readOnlyMutableListeners.on(t.nodes.holder,"dragstart",(function(){e.processDragStart()})),this.readOnlyMutableListeners.on(t.nodes.holder,"dragover",(function(t){e.processDragOver(t)}),!0)}},{key:"disableModuleBindings",value:function(){this.readOnlyMutableListeners.clearAll()}},{key:"processDrop",value:(t=(0,i.default)(r.default.mark((function e(t){var n,o,i,a,s,l;return r.default.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return n=this.Editor,o=n.BlockManager,i=n.Caret,a=n.Paste,t.preventDefault(),o.blocks.forEach((function(e){e.dropTarget=!1})),f.default.isAtEditor&&!f.default.isCollapsed&&this.isStartedAtEditor&&document.execCommand("delete"),this.isStartedAtEditor=!1,(s=o.setCurrentBlockByChildNode(t.target))?this.Editor.Caret.setToBlock(s,i.positions.END):(l=o.setCurrentBlockByChildNode(o.lastBlock.holder),this.Editor.Caret.setToBlock(l,i.positions.END)),e.next=9,a.processDataTransfer(t.dataTransfer,!0);case 9:case"end":return e.stop()}}),e,this)}))),function(e){return t.apply(this,arguments)})},{key:"processDragStart",value:function(){f.default.isAtEditor&&!f.default.isCollapsed&&(this.isStartedAtEditor=!0),this.Editor.InlineToolbar.close()}},{key:"processDragOver",value:function(e){e.preventDefault()}}]),o}((d=p(d)).default);o.default=v,v.displayName="DragNDrop",e.exports=t.default})?o.apply(t,r):o)||(e.exports=i)},function(e,t,n){var o,r,i,a=n(7);"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self&&self,r=[t,n(2),n(3),n(5),n(6),n(4),n(9),n(8)],void 0===(i="function"==typeof(o=function(o,r,i,s,l,c,u,f){"use strict";var d=n(1);function p(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(p=function(e){return e?n:t})(e)}function h(e){var t=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}();return function(){var n,o=(0,c.default)(e);if(t){var r=(0,c.default)(this).constructor;n=Reflect.construct(o,arguments,r)}else n=o.apply(this,arguments);return(0,l.default)(this,n)}}Object.defineProperty(o,"__esModule",{value:!0}),o.default=void 0,r=d(r),i=d(i),s=d(s),l=d(l),c=d(c),u=d(u),f=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!==a(e)&&"function"!=typeof e)return{default:e};var n=p(t);if(n&&n.has(e))return n.get(e);var o={},r=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var i in e)if("default"!==i&&Object.prototype.hasOwnProperty.call(e,i)){var s=r?Object.getOwnPropertyDescriptor(e,i):null;s&&(s.get||s.set)?Object.defineProperty(o,i,s):o[i]=e[i]}return o.default=e,n&&n.set(e,o),o}(f);var v=function(e){(0,s.default)(n,e);var t=h(n);function n(){var e;return(0,r.default)(this,n),(e=t.apply(this,arguments)).disabled=!1,e}return(0,i.default)(n,[{key:"enable",value:function(){this.disabled=!1}},{key:"disable",value:function(){this.disabled=!0}},{key:"onChange",value:function(e){!this.disabled&&f.isFunction(this.config.onChange)&&this.config.onChange(this.Editor.API.methods,e)}}]),n}(u.default);o.default=v,v.displayName="ModificationsObserver",e.exports=t.default})?o.apply(t,r):o)||(e.exports=i)},function(e,t,n){var o,r,i,a=n(7);"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self&&self,r=[t,n(20),n(30),n(41),n(21),n(2),n(3),n(5),n(6),n(4),n(9),n(19),n(8),n(66)],void 0===(i="function"==typeof(o=function(o,r,i,s,l,c,u,f,d,p,h,v,g,y){"use strict";var k=n(1);function b(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(b=function(e){return e?n:t})(e)}function m(e){var t=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}();return function(){var n,o=(0,p.default)(e);if(t){var r=(0,p.default)(this).constructor;n=Reflect.construct(o,arguments,r)}else n=o.apply(this,arguments);return(0,d.default)(this,n)}}Object.defineProperty(o,"__esModule",{value:!0}),o.default=void 0,r=k(r),i=k(i),s=k(s),l=k(l),c=k(c),u=k(u),f=k(f),d=k(d),p=k(p),h=k(h),v=k(v),g=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!==a(e)&&"function"!=typeof e)return{default:e};var n=b(t);if(n&&n.has(e))return n.get(e);var o={},r=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var i in e)if("default"!==i&&Object.prototype.hasOwnProperty.call(e,i)){var s=r?Object.getOwnPropertyDescriptor(e,i):null;s&&(s.get||s.set)?Object.defineProperty(o,i,s):o[i]=e[i]}return o.default=e,n&&n.set(e,o),o}(g);var w=function(e){(0,f.default)(w,e);var t,n,o,a,d,p,h,k,b=m(w);function w(){var e;return(0,c.default)(this,w),(e=b.apply(this,arguments)).MIME_TYPE="application/x-editor-js",e.toolsTags={},e.tagsByTool={},e.toolsPatterns=[],e.toolsFiles={},e.exceptionList=[],e.processTool=function(t){try{var n=t.create({},{},!1);if(!1===t.pasteConfig)return void e.exceptionList.push(t.name);if(!g.isFunction(n.onPaste))return;e.getTagsConfig(t),e.getFilesConfig(t),e.getPatternsConfig(t)}catch(e){g.log("Paste handling for «".concat(t.name,"» Tool hasn't been set up because of the error"),"warn",e)}},e.handlePasteEvent=function(){var t=(0,l.default)(r.default.mark((function t(n){var o,i,a;return r.default.wrap((function(t){for(;;)switch(t.prev=t.next){case 0:if(o=e.Editor,i=o.BlockManager,a=o.Toolbar,i.currentBlock&&(!e.isNativeBehaviour(n.target)||n.clipboardData.types.includes("Files"))){t.next=3;break}return t.abrupt("return");case 3:if(!i.currentBlock||!e.exceptionList.includes(i.currentBlock.name)){t.next=5;break}return t.abrupt("return");case 5:n.preventDefault(),e.processDataTransfer(n.clipboardData),i.clearFocused(),a.close();case 9:case"end":return t.stop()}}),t)})));return function(e){return t.apply(this,arguments)}}(),e}return(0,u.default)(w,[{key:"prepare",value:(k=(0,l.default)(r.default.mark((function e(){return r.default.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:this.processTools();case 1:case"end":return e.stop()}}),e,this)}))),function(){return k.apply(this,arguments)})},{key:"toggleReadOnly",value:function(e){e?this.unsetCallback():this.setCallback()}},{key:"processDataTransfer",value:(h=(0,l.default)(r.default.mark((function e(t){var n,o,i,a,s,l,c,u,f,d=this,p=arguments;return r.default.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(n=p.length>1&&void 0!==p[1]&&p[1],o=this.Editor.Tools,!((i=t.types).includes?i.includes("Files"):i.contains("Files"))||g.isEmpty(this.toolsFiles)){e.next=8;break}return e.next=7,this.processFiles(t.files);case 7:return e.abrupt("return");case 8:if(a=t.getData(this.MIME_TYPE),s=t.getData("text/plain"),l=t.getData("text/html"),!a){e.next=19;break}return e.prev=12,this.insertEditorJSData(JSON.parse(a)),e.abrupt("return");case 17:e.prev=17,e.t0=e.catch(12);case 19:if(n&&s.trim()&&l.trim()&&(l="

    "+(l.trim()?l:s)+"

    "),c=Object.keys(this.toolsTags).reduce((function(e,t){var n;return e[t.toLowerCase()]=null!==(n=d.toolsTags[t].sanitizationConfig)&&void 0!==n?n:{},e}),{}),u=Object.assign({},c,o.getAllInlineToolsSanitizeConfig(),{br:{}}),(f=(0,y.clean)(l,u)).trim()&&f.trim()!==s&&v.default.isHTMLString(f)){e.next=28;break}return e.next=26,this.processText(s);case 26:e.next=30;break;case 28:return e.next=30,this.processText(f,!0);case 30:case"end":return e.stop()}}),e,this,[[12,17]])}))),function(e){return h.apply(this,arguments)})},{key:"processText",value:(p=(0,l.default)(r.default.mark((function e(t){var n,o,i,a,s,c,u,f=this,d=arguments;return r.default.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(n=d.length>1&&void 0!==d[1]&&d[1],o=this.Editor,i=o.Caret,a=o.BlockManager,(s=n?this.processHTML(t):this.processPlain(t)).length){e.next=5;break}return e.abrupt("return");case 5:if(1!==s.length){e.next=8;break}return s[0].isBlock?this.processSingleBlock(s.pop()):this.processInlinePaste(s.pop()),e.abrupt("return");case 8:c=a.currentBlock&&a.currentBlock.tool.isDefault,u=c&&a.currentBlock.isEmpty,s.map(function(){var e=(0,l.default)(r.default.mark((function e(t,n){return r.default.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",f.insertBlock(t,0===n&&u));case 1:case"end":return e.stop()}}),e)})));return function(t,n){return e.apply(this,arguments)}}()),a.currentBlock&&i.setToBlock(a.currentBlock,i.positions.END);case 12:case"end":return e.stop()}}),e,this)}))),function(e){return p.apply(this,arguments)})},{key:"setCallback",value:function(){this.listeners.on(this.Editor.UI.nodes.holder,"paste",this.handlePasteEvent)}},{key:"unsetCallback",value:function(){this.listeners.off(this.Editor.UI.nodes.holder,"paste",this.handlePasteEvent)}},{key:"processTools",value:function(){var e=this.Editor.Tools.blockTools;Array.from(e.values()).forEach(this.processTool)}},{key:"collectTagNames",value:function(e){return g.isString(e)?[e]:g.isObject(e)?Object.keys(e):[]}},{key:"getTagsConfig",value:function(e){var t=this,n=e.pasteConfig.tags||[],o=[];n.forEach((function(n){var r=t.collectTagNames(n);o.push.apply(o,(0,s.default)(r)),r.forEach((function(o){if(Object.prototype.hasOwnProperty.call(t.toolsTags,o))g.log("Paste handler for «".concat(e.name,"» Tool on «").concat(o,"» tag is skipped ")+"because it is already used by «".concat(t.toolsTags[o].tool.name,"» Tool."),"warn");else{var r=g.isObject(n)?n[o]:null;t.toolsTags[o.toUpperCase()]={tool:e,sanitizationConfig:r}}}))})),this.tagsByTool[e.name]=o.map((function(e){return e.toUpperCase()}))}},{key:"getFilesConfig",value:function(e){var t=e.pasteConfig.files,n=void 0===t?{}:t,o=n.extensions,r=n.mimeTypes;(o||r)&&(o&&!Array.isArray(o)&&(g.log("«extensions» property of the onDrop config for «".concat(e.name,"» Tool should be an array")),o=[]),r&&!Array.isArray(r)&&(g.log("«mimeTypes» property of the onDrop config for «".concat(e.name,"» Tool should be an array")),r=[]),r&&(r=r.filter((function(t){return!!g.isValidMimeType(t)||(g.log("MIME type value «".concat(t,"» for the «").concat(e.name,"» Tool is not a valid MIME type"),"warn"),!1)}))),this.toolsFiles[e.name]={extensions:o||[],mimeTypes:r||[]})}},{key:"getPatternsConfig",value:function(e){var t=this;e.pasteConfig.patterns&&!g.isEmpty(e.pasteConfig.patterns)&&Object.entries(e.pasteConfig.patterns).forEach((function(n){var o=(0,i.default)(n,2),r=o[0],a=o[1];a instanceof RegExp||g.log("Pattern ".concat(a," for «").concat(e.name,"» Tool is skipped because it should be a Regexp instance."),"warn"),t.toolsPatterns.push({key:r,pattern:a,tool:e})}))}},{key:"isNativeBehaviour",value:function(e){return v.default.isNativeInput(e)}},{key:"processFiles",value:(d=(0,l.default)(r.default.mark((function e(t){var n,o,i,a,s=this;return r.default.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return n=this.Editor.BlockManager,e.next=3,Promise.all(Array.from(t).map((function(e){return s.processFile(e)})));case 3:o=(o=e.sent).filter((function(e){return!!e})),i=n.currentBlock.tool.isDefault,a=i&&n.currentBlock.isEmpty,o.forEach((function(e,t){n.paste(e.type,e.event,0===t&&a)}));case 8:case"end":return e.stop()}}),e,this)}))),function(e){return d.apply(this,arguments)})},{key:"processFile",value:(a=(0,l.default)(r.default.mark((function e(t){var n,o,a,s,l;return r.default.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(n=g.getFileExtension(t),o=Object.entries(this.toolsFiles).find((function(e){var o=(0,i.default)(e,2),r=(o[0],o[1]),a=r.mimeTypes,s=r.extensions,l=t.type.split("/"),c=(0,i.default)(l,2),u=c[0],f=c[1],d=s.find((function(e){return e.toLowerCase()===n.toLowerCase()})),p=a.find((function(e){var t=e.split("/"),n=(0,i.default)(t,2),o=n[0],r=n[1];return o===u&&(r===f||"*"===r)}));return!!d||!!p}))){e.next=4;break}return e.abrupt("return");case 4:return a=(0,i.default)(o,1),s=a[0],l=this.composePasteEvent("file",{file:t}),e.abrupt("return",{event:l,type:s});case 7:case"end":return e.stop()}}),e,this)}))),function(e){return a.apply(this,arguments)})},{key:"processHTML",value:function(e){var t=this,n=this.Editor.Tools,o=v.default.make("DIV");return o.innerHTML=e,this.getNodes(o).map((function(e){var o,r=n.defaultTool,i=!1;switch(e.nodeType){case Node.DOCUMENT_FRAGMENT_NODE:(o=v.default.make("div")).appendChild(e);break;case Node.ELEMENT_NODE:o=e,i=!0,t.toolsTags[o.tagName]&&(r=t.toolsTags[o.tagName].tool)}var a=r.pasteConfig.tags.reduce((function(e,n){return t.collectTagNames(n).forEach((function(t){var o=g.isObject(n)?n[t]:null;e[t.toLowerCase()]=o||{}})),e}),{}),s=Object.assign({},a,r.baseSanitizeConfig);if("table"===o.tagName.toLowerCase()){var l=(0,y.clean)(o.outerHTML,s);o=v.default.make("div",void 0,{innerHTML:l}).firstChild}else o.innerHTML=(0,y.clean)(o.innerHTML,s);var c=t.composePasteEvent("tag",{data:o});return{content:o,isBlock:i,tool:r.name,event:c}})).filter((function(e){var t=v.default.isEmpty(e.content),n=v.default.isSingleTag(e.content);return!t||n}))}},{key:"processPlain",value:function(e){var t=this,n=this.config.defaultBlock;if(!e)return[];var o=n;return e.split(/\r?\n/).filter((function(e){return e.trim()})).map((function(e){var n=v.default.make("div");n.textContent=e;var r=t.composePasteEvent("tag",{data:n});return{content:n,tool:o,isBlock:!1,event:r}}))}},{key:"processSingleBlock",value:(o=(0,l.default)(r.default.mark((function e(t){var n,o,i,a;return r.default.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(n=this.Editor,o=n.Caret,i=n.BlockManager,(a=i.currentBlock)&&t.tool===a.name&&v.default.containsOnlyInlineElements(t.content.innerHTML)){e.next=5;break}return this.insertBlock(t,(null==a?void 0:a.tool.isDefault)&&a.isEmpty),e.abrupt("return");case 5:o.insertContentAtCaretPosition(t.content.innerHTML);case 6:case"end":return e.stop()}}),e,this)}))),function(e){return o.apply(this,arguments)})},{key:"processInlinePaste",value:(n=(0,l.default)(r.default.mark((function e(t){var n,o,i,a,s,l,c,u;return r.default.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(n=this.Editor,o=n.BlockManager,i=n.Caret,a=t.content,!(o.currentBlock&&o.currentBlock.tool.isDefault&&a.textContent.length1&&void 0!==arguments[1]&&arguments[1],o=this.Editor,r=o.BlockManager,i=o.Caret,a=r.currentBlock;if(n&&a&&a.isEmpty)return t=r.paste(e.tool,e.event,!0),void i.setToBlock(t,i.positions.END);t=r.paste(e.tool,e.event),i.setToBlock(t,i.positions.END)}},{key:"insertEditorJSData",value:function(e){var t=this.Editor,n=t.BlockManager,o=t.Caret,r=t.Tools;(0,y.sanitizeBlocks)(e,(function(e){return r.blockTools.get(e).sanitizeConfig})).forEach((function(e,t){var r=e.tool,i=e.data,a=!1;0===t&&(a=n.currentBlock&&n.currentBlock.tool.isDefault&&n.currentBlock.isEmpty);var s=n.insert({tool:r,data:i,replace:a});o.setToBlock(s,o.positions.END)}))}},{key:"processElementNode",value:function(e,t,n){var o=Object.keys(this.toolsTags),r=e,i=(this.toolsTags[r.tagName]||{}).tool,a=this.tagsByTool[null==i?void 0:i.name]||[],l=o.includes(r.tagName),c=v.default.blockElements.includes(r.tagName.toLowerCase()),u=Array.from(r.children).some((function(e){var t=e.tagName;return o.includes(t)&&!a.includes(t)})),f=Array.from(r.children).some((function(e){var t=e.tagName;return v.default.blockElements.includes(t.toLowerCase())}));return c||l||u?l&&!u||c&&!f&&!u?[].concat((0,s.default)(t),[n,r]):void 0:(n.appendChild(r),[].concat((0,s.default)(t),[n]))}},{key:"getNodes",value:function(e){var t,n=this;return Array.from(e.childNodes).reduce((function e(o,r){if(v.default.isEmpty(r)&&!v.default.isSingleTag(r))return o;var i=o[o.length-1],a=new DocumentFragment;switch(i&&v.default.isFragment(i)&&(a=o.pop()),r.nodeType){case Node.ELEMENT_NODE:if(t=n.processElementNode(r,o,a))return t;break;case Node.TEXT_NODE:return a.appendChild(r),[].concat((0,s.default)(o),[a]);default:return[].concat((0,s.default)(o),[a])}return[].concat((0,s.default)(o),(0,s.default)(Array.from(r.childNodes).reduce(e,[])))}),[])}},{key:"composePasteEvent",value:function(e,t){return new CustomEvent(e,{detail:t})}}]),w}(h.default);o.default=w,w.displayName="Paste",w.PATTERN_PROCESSING_MAX_LENGTH=450,e.exports=t.default})?o.apply(t,r):o)||(e.exports=i)},function(e,t,n){var o,r,i;"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self&&self,r=[t,n(20),n(30),n(21),n(2),n(3),n(5),n(6),n(4),n(9),n(151)],void 0===(i="function"==typeof(o=function(o,r,i,a,s,l,c,u,f,d,p){"use strict";var h=n(1);function v(e){var t=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}();return function(){var n,o=(0,f.default)(e);if(t){var r=(0,f.default)(this).constructor;n=Reflect.construct(o,arguments,r)}else n=o.apply(this,arguments);return(0,u.default)(this,n)}}Object.defineProperty(o,"__esModule",{value:!0}),o.default=void 0,r=h(r),i=h(i),a=h(a),s=h(s),l=h(l),c=h(c),u=h(u),f=h(f);var g=function(e){(0,c.default)(u,e);var t,n,o=v(u);function u(){var e;return(0,s.default)(this,u),(e=o.apply(this,arguments)).toolsDontSupportReadOnly=[],e.readOnlyEnabled=!1,e}return(0,l.default)(u,[{key:"isEnabled",get:function(){return this.readOnlyEnabled}},{key:"prepare",value:(n=(0,a.default)(r.default.mark((function e(){var t,n,o;return r.default.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:t=this.Editor.Tools,n=t.blockTools,o=[],Array.from(n.entries()).forEach((function(e){var t=(0,i.default)(e,2),n=t[0];t[1].isReadOnlySupported||o.push(n)})),this.toolsDontSupportReadOnly=o,this.config.readOnly&&o.length>0&&this.throwCriticalError(),this.toggle(this.config.readOnly);case 7:case"end":return e.stop()}}),e,this)}))),function(){return n.apply(this,arguments)})},{key:"toggle",value:(t=(0,a.default)(r.default.mark((function e(){var t,n,o,i,a=arguments;return r.default.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:(t=a.length>0&&void 0!==a[0]?a[0]:!this.readOnlyEnabled)&&this.toolsDontSupportReadOnly.length>0&&this.throwCriticalError(),n=this.readOnlyEnabled,this.readOnlyEnabled=t,e.t0=r.default.keys(this.Editor);case 5:if((e.t1=e.t0()).done){e.next=12;break}if(o=e.t1.value,this.Editor[o].toggleReadOnly){e.next=9;break}return e.abrupt("continue",5);case 9:this.Editor[o].toggleReadOnly(t),e.next=5;break;case 12:if(n!==t){e.next=14;break}return e.abrupt("return",this.readOnlyEnabled);case 14:return e.next=16,this.Editor.Saver.save();case 16:return i=e.sent,e.next=19,this.Editor.BlockManager.clear();case 19:return e.next=21,this.Editor.Renderer.render(i.blocks);case 21:return e.abrupt("return",this.readOnlyEnabled);case 22:case"end":return e.stop()}}),e,this)}))),function(){return t.apply(this,arguments)})},{key:"throwCriticalError",value:function(){throw new p.CriticalError("To enable read-only mode all connected tools should support it. Tools ".concat(this.toolsDontSupportReadOnly.join(", ")," don't support read-only mode."))}}]),u}((d=h(d)).default);o.default=g,g.displayName="ReadOnly",e.exports=t.default})?o.apply(t,r):o)||(e.exports=i)},function(e,t,n){var o,r,i,a=n(7);"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self&&self,r=[t,n(2),n(3),n(5),n(6),n(4),n(9),n(19),n(25),n(61),n(8)],void 0===(i="function"==typeof(o=function(o,r,i,s,l,c,u,f,d,p,h){"use strict";var v=n(1);function g(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(g=function(e){return e?n:t})(e)}function y(e,t){var n="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!n){if(Array.isArray(e)||(n=function(e,t){if(e){if("string"==typeof e)return k(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?k(e,t):void 0}}(e))||t&&e&&"number"==typeof e.length){n&&(e=n);var o=0,r=function(){};return{s:r,n:function(){return o>=e.length?{done:!0}:{done:!1,value:e[o++]}},e:function(e){throw e},f:r}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var i,a=!0,s=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return a=e.done,e},e:function(e){s=!0,i=e},f:function(){try{a||null==n.return||n.return()}finally{if(s)throw i}}}}function k(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,o=new Array(t);nn&&this.mouseX>n,a=this.startX=this.startY?(this.overlayRectangle.style.top="".concat(this.startY-window.pageYOffset,"px"),this.overlayRectangle.style.bottom="calc(100% - ".concat(this.mouseY-window.pageYOffset,"px")):(this.overlayRectangle.style.bottom="calc(100% - ".concat(this.startY-window.pageYOffset,"px"),this.overlayRectangle.style.top="".concat(this.mouseY-window.pageYOffset,"px")),this.mouseX>=this.startX?(this.overlayRectangle.style.left="".concat(this.startX-window.pageXOffset,"px"),this.overlayRectangle.style.right="calc(100% - ".concat(this.mouseX-window.pageXOffset,"px")):(this.overlayRectangle.style.right="calc(100% - ".concat(this.startX-window.pageXOffset,"px"),this.overlayRectangle.style.left="".concat(this.mouseX-window.pageXOffset,"px"))}},{key:"genInfoForMouseSelection",value:function(){var e,t=document.body.offsetWidth/2,n=this.mouseY-window.pageYOffset,o=document.elementFromPoint(t,n),r=this.Editor.BlockManager.getBlockByChildNode(o);void 0!==r&&(e=this.Editor.BlockManager.blocks.findIndex((function(e){return e.holder===r.holder})));var i=this.Editor.BlockManager.lastBlock.holder.querySelector("."+p.default.CSS.content),a=Number.parseInt(window.getComputedStyle(i).width,10)/2;return{index:e,leftPos:t-a,rightPos:t+a}}},{key:"addBlockInSelection",value:function(e){this.rectCrossesBlocks&&this.Editor.BlockSelection.selectBlockByIndex(e),this.stackOfSelected.push(e)}},{key:"trySelectNextBlock",value:function(e){var t=this,n=this.stackOfSelected[this.stackOfSelected.length-1]===e,o=this.stackOfSelected.length;if(!n){var r=this.stackOfSelected[o-1]-this.stackOfSelected[o-2]>0,i=0;o>1&&(i=r?1:-1);var a=e>this.stackOfSelected[o-1]&&1===i,s=ethis.stackOfSelected[o-1]||void 0===this.stackOfSelected[o-1])){if(!l&&e=e;c--)this.addBlockInSelection(c);else if(l){var u,f=o-1;for(u=e>this.stackOfSelected[o-1]?function(){return e>t.stackOfSelected[f]}:function(){return e0&&void 0!==arguments[0]?arguments[0]:this.Editor.BlockManager.currentBlock;this.opened=!0,this.selection.save(),t.selected=!0,this.Editor.BlockSelection.clearCache();var n=t.getTunes(),o=(0,r.default)(n,2),i=o[0],a=o[1];this.eventsDispatcher.emit(this.events.opened),this.popover=new h.default({className:this.CSS.settings,searchable:!0,filterLabel:v.default.ui(g.I18nInternalNS.ui.popover,"Filter"),nothingFoundLabel:v.default.ui(g.I18nInternalNS.ui.popover,"Nothing found"),items:i.map((function(t){return e.resolveTuneAliases(t)})),customContent:a,customContentFlippableItems:this.getControls(a),scopeElement:this.Editor.API.methods.ui.nodes.redactor}),this.popover.on(h.PopoverEvent.OverlayClicked,this.onOverlayClicked),this.popover.on(h.PopoverEvent.Close,(function(){return e.close()})),this.nodes.wrapper.append(this.popover.getElement()),this.popover.show()}},{key:"getElement",value:function(){return this.nodes.wrapper}},{key:"close",value:function(){this.opened=!1,p.default.isAtEditor||this.selection.restore(),this.selection.clearSaved(),!this.Editor.CrossBlockSelection.isCrossBlockSelectionStarted&&this.Editor.BlockManager.currentBlock&&(this.Editor.BlockManager.currentBlock.selected=!1),this.eventsDispatcher.emit(this.events.closed),this.popover&&(this.popover.off(h.PopoverEvent.OverlayClicked,this.onOverlayClicked),this.popover.destroy(),this.popover.getElement().remove(),this.popover=null)}},{key:"getControls",value:function(e){var t=this.Editor.StylesAPI,n=e.querySelectorAll(".".concat(t.classes.settingsButton,", ").concat(d.default.allInputsSelector));return Array.from(n)}},{key:"resolveTuneAliases",value:function(e){var t=(0,y.resolveAliases)(e,{label:"title"});return e.confirmation&&(t.confirmation=this.resolveTuneAliases(e.confirmation)),t}}]),n}(f.default);o.default=w,w.displayName="BlockSettings",e.exports=t.default})?o.apply(t,r):o)||(e.exports=i)},function(e,t,n){var o,r,i,a=n(7);"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self&&self,r=[t,n(20),n(30),n(21),n(41),n(2),n(3),n(5),n(6),n(4),n(9),n(19),n(8),n(67),n(54),n(68),n(66)],void 0===(i="function"==typeof(o=function(o,r,i,s,l,c,u,f,d,p,h,v,g,y,k,b,m){"use strict";var w=n(1);function x(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(x=function(e){return e?n:t})(e)}function C(e){var t=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}();return function(){var n,o=(0,p.default)(e);if(t){var r=(0,p.default)(this).constructor;n=Reflect.construct(o,arguments,r)}else n=o.apply(this,arguments);return(0,d.default)(this,n)}}Object.defineProperty(o,"__esModule",{value:!0}),o.default=void 0,r=w(r),i=w(i),s=w(s),l=w(l),c=w(c),u=w(u),f=w(f),d=w(d),p=w(p),h=w(h),v=w(v),g=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!==a(e)&&"function"!=typeof e)return{default:e};var n=x(t);if(n&&n.has(e))return n.get(e);var o={},r=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var i in e)if("default"!==i&&Object.prototype.hasOwnProperty.call(e,i)){var s=r?Object.getOwnPropertyDescriptor(e,i):null;s&&(s.get||s.set)?Object.defineProperty(o,i,s):o[i]=e[i]}return o.default=e,n&&n.set(e,o),o}(g),y=w(y),k=w(k);var S=function(e){(0,f.default)(a,e);var t,n,o=C(a);function a(){var e;return(0,c.default)(this,a),(e=o.apply(this,arguments)).opened=!1,e.tools=[],e.flipper=null,e.togglingCallback=null,e}return(0,u.default)(a,[{key:"make",value:function(){this.nodes.wrapper=v.default.make("div",[a.CSS.conversionToolbarWrapper].concat((0,l.default)(this.isRtl?[this.Editor.UI.CSS.editorRtlFix]:[]))),this.nodes.tools=v.default.make("div",a.CSS.conversionToolbarTools);var e=v.default.make("div",a.CSS.conversionToolbarLabel,{textContent:k.default.ui(b.I18nInternalNS.ui.inlineToolbar.converter,"Convert to")});return this.addTools(),this.enableFlipper(),v.default.append(this.nodes.wrapper,e),v.default.append(this.nodes.wrapper,this.nodes.tools),this.nodes.wrapper}},{key:"destroy",value:function(){this.flipper&&(this.flipper.deactivate(),this.flipper=null),this.removeAllNodes()}},{key:"toggle",value:function(e){this.opened?this.close():this.open(),g.isFunction(e)&&(this.togglingCallback=e)}},{key:"open",value:function(){var e=this;this.filterTools(),this.opened=!0,this.nodes.wrapper.classList.add(a.CSS.conversionToolbarShowed),window.requestAnimationFrame((function(){e.flipper.activate(e.tools.map((function(e){return e.button})).filter((function(e){return!e.classList.contains(a.CSS.conversionToolHidden)}))),e.flipper.focusFirst(),g.isFunction(e.togglingCallback)&&e.togglingCallback(!0)}))}},{key:"close",value:function(){this.opened=!1,this.flipper.deactivate(),this.nodes.wrapper.classList.remove(a.CSS.conversionToolbarShowed),g.isFunction(this.togglingCallback)&&this.togglingCallback(!1)}},{key:"hasTools",value:function(){return 1!==this.tools.length||this.tools[0].name!==this.config.defaultBlock}},{key:"replaceWithBlock",value:(n=(0,s.default)(r.default.mark((function e(t,n){var o,i,a,s,l,c,u,f,d,p=this;return r.default.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return o=this.Editor.BlockManager.currentBlock.tool,e.next=3,this.Editor.BlockManager.currentBlock.save();case 3:if(i=e.sent,a=i.data,s=this.Editor.Tools.blockTools.get(t),l="",c=o.conversionConfig.export,!g.isFunction(c)){e.next=12;break}l=c(a),e.next=18;break;case 12:if(!g.isString(c)){e.next=16;break}l=a[c],e.next=18;break;case 16:return g.log("Conversion «export» property must be a string or function. String means key of saved data object to export. Function should export processed string to export."),e.abrupt("return");case 18:if(u=(0,m.clean)(l,s.sanitizeConfig),f={},d=s.conversionConfig.import,!g.isFunction(d)){e.next=25;break}f=d(u),e.next=31;break;case 25:if(!g.isString(d)){e.next=29;break}f[d]=u,e.next=31;break;case 29:return g.log("Conversion «import» property must be a string or function. String means key of tool data to import. Function accepts a imported string and return composed tool data."),e.abrupt("return");case 31:n&&(f=Object.assign(f,n)),this.Editor.BlockManager.replace({tool:t,data:f}),this.Editor.BlockSelection.clearSelection(),this.close(),this.Editor.InlineToolbar.close(),g.delay((function(){p.Editor.Caret.setToBlock(p.Editor.BlockManager.currentBlock)}),10)();case 37:case"end":return e.stop()}}),e,this)}))),function(e,t){return n.apply(this,arguments)})},{key:"addTools",value:function(){var e=this,t=this.Editor.Tools.blockTools;Array.from(t.entries()).forEach((function(t){var n=(0,i.default)(t,2),o=n[0],r=n[1],a=r.conversionConfig;a&&a.import&&r.toolbox.forEach((function(t){return e.addToolIfValid(o,t)}))}))}},{key:"addToolIfValid",value:function(e,t){!g.isEmpty(t)&&t.icon&&this.addTool(e,t)}},{key:"addTool",value:function(e,t){var n=this,o=v.default.make("div",[a.CSS.conversionTool]),i=v.default.make("div",[a.CSS.conversionToolIcon]);o.dataset.tool=e,i.innerHTML=t.icon,v.default.append(o,i),v.default.append(o,v.default.text(k.default.t(b.I18nInternalNS.toolNames,t.title||g.capitalize(e)))),v.default.append(this.nodes.tools,o),this.tools.push({name:e,button:o,toolboxItem:t}),this.listeners.on(o,"click",(0,s.default)(r.default.mark((function o(){return r.default.wrap((function(o){for(;;)switch(o.prev=o.next){case 0:return o.next=2,n.replaceWithBlock(e,t.data);case 2:case"end":return o.stop()}}),o)}))))}},{key:"filterTools",value:(t=(0,s.default)(r.default.mark((function e(){var t,n,o;return r.default.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return o=function(e,t){return e.icon===t.icon&&e.title===t.title},t=this.Editor.BlockManager.currentBlock,e.next=4,t.getActiveToolboxEntry();case 4:n=e.sent,this.tools.forEach((function(e){var r=!1;if(n){var i=o(n,e.toolboxItem);r=e.button.dataset.tool===t.name&&i}e.button.hidden=r,e.button.classList.toggle(a.CSS.conversionToolHidden,r)}));case 6:case"end":return e.stop()}}),e,this)}))),function(){return t.apply(this,arguments)})},{key:"enableFlipper",value:function(){this.flipper=new y.default({focusedItemClass:a.CSS.conversionToolFocused})}}],[{key:"CSS",get:function(){return{conversionToolbarWrapper:"ce-conversion-toolbar",conversionToolbarShowed:"ce-conversion-toolbar--showed",conversionToolbarTools:"ce-conversion-toolbar__tools",conversionToolbarLabel:"ce-conversion-toolbar__label",conversionTool:"ce-conversion-tool",conversionToolHidden:"ce-conversion-tool--hidden",conversionToolIcon:"ce-conversion-tool__icon",conversionToolFocused:"ce-conversion-tool--focused",conversionToolActive:"ce-conversion-tool--active"}}}]),a}(h.default);o.default=S,S.displayName="ConversionToolbar",e.exports=t.default})?o.apply(t,r):o)||(e.exports=i)},function(e,t,n){var o,r,i,a=n(7);"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self&&self,r=[t,n(20),n(21),n(41),n(30),n(2),n(3),n(5),n(6),n(4),n(9),n(19),n(25),n(8),n(67),n(54),n(68),n(116),n(115),n(69),n(37)],void 0===(i="function"==typeof(o=function(o,r,i,s,l,c,u,f,d,p,h,v,g,y,k,b,m,w,x,C,S){"use strict";var T=n(1);function E(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(E=function(e){return e?n:t})(e)}function B(e){var t=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}();return function(){var n,o=(0,p.default)(e);if(t){var r=(0,p.default)(this).constructor;n=Reflect.construct(o,arguments,r)}else n=o.apply(this,arguments);return(0,d.default)(this,n)}}Object.defineProperty(o,"__esModule",{value:!0}),o.default=void 0,r=T(r),i=T(i),s=T(s),l=T(l),c=T(c),u=T(u),f=T(f),d=T(d),p=T(p),h=T(h),v=T(v),g=T(g),y=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!==a(e)&&"function"!=typeof e)return{default:e};var n=E(t);if(n&&n.has(e))return n.get(e);var o={},r=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var i in e)if("default"!==i&&Object.prototype.hasOwnProperty.call(e,i)){var s=r?Object.getOwnPropertyDescriptor(e,i):null;s&&(s.get||s.set)?Object.defineProperty(o,i,s):o[i]=e[i]}return o.default=e,n&&n.set(e,o),o}(y),k=T(k),b=T(b),w=T(w),x=T(x);var M=function(e){(0,f.default)(o,e);var t,n=B(o);function o(e){var t,r=e.config,i=e.eventsDispatcher;return(0,c.default)(this,o),(t=n.call(this,{config:r,eventsDispatcher:i})).CSS={inlineToolbar:"ce-inline-toolbar",inlineToolbarShowed:"ce-inline-toolbar--showed",inlineToolbarLeftOriented:"ce-inline-toolbar--left-oriented",inlineToolbarRightOriented:"ce-inline-toolbar--right-oriented",inlineToolbarShortcut:"ce-inline-toolbar__shortcut",buttonsWrapper:"ce-inline-toolbar__buttons",actionsWrapper:"ce-inline-toolbar__actions",inlineToolButton:"ce-inline-tool",inputField:"cdx-input",focusedButton:"ce-inline-tool--focused",conversionToggler:"ce-inline-toolbar__dropdown",conversionTogglerArrow:"ce-inline-toolbar__dropdown-arrow",conversionTogglerHidden:"ce-inline-toolbar__dropdown--hidden",conversionTogglerContent:"ce-inline-toolbar__dropdown-content",togglerAndButtonsWrapper:"ce-inline-toolbar__toggler-and-button-wrapper"},t.opened=!1,t.toolbarVerticalMargin=y.isMobileScreen()?20:6,t.buttonsList=null,t.width=0,t.flipper=null,t.tooltip=new x.default,t}return(0,u.default)(o,[{key:"toggleReadOnly",value:function(e){e?(this.destroy(),this.Editor.ConversionToolbar.destroy()):this.make()}},{key:"tryToShow",value:function(){var e=arguments.length>0&&void 0!==arguments[0]&&arguments[0],t=!(arguments.length>1&&void 0!==arguments[1])||arguments[1];this.allowedToShow()?(this.move(),this.open(t),this.Editor.Toolbar.close()):e&&this.close()}},{key:"move",value:function(){var e=g.default.rect,t=this.Editor.UI.nodes.wrapper.getBoundingClientRect(),n={x:e.x-t.left,y:e.y+e.height-t.top+this.toolbarVerticalMargin};e.width&&(n.x+=Math.floor(e.width/2));var o=n.x-this.width/2,r=n.x+this.width/2;this.nodes.wrapper.classList.toggle(this.CSS.inlineToolbarLeftOriented,othis.Editor.UI.contentRect.right),this.nodes.wrapper.style.left=Math.floor(n.x)+"px",this.nodes.wrapper.style.top=Math.floor(n.y)+"px"}},{key:"close",value:function(){var e=this;this.opened&&(this.Editor.ReadOnly.isEnabled||(this.nodes.wrapper.classList.remove(this.CSS.inlineToolbarShowed),Array.from(this.toolsInstances.entries()).forEach((function(t){var n=(0,l.default)(t,2),o=n[0],r=n[1],i=e.getToolShortcut(o);i&&w.default.remove(e.Editor.UI.nodes.redactor,i),y.isFunction(r.clear)&&r.clear()})),this.opened=!1,this.flipper.deactivate(),this.Editor.ConversionToolbar.close()))}},{key:"open",value:function(){var e=!(arguments.length>0&&void 0!==arguments[0])||arguments[0];if(!this.opened){this.addToolsFiltered(),this.nodes.wrapper.classList.add(this.CSS.inlineToolbarShowed),this.buttonsList=this.nodes.buttons.querySelectorAll(".".concat(this.CSS.inlineToolButton)),this.opened=!0,e&&this.Editor.ConversionToolbar.hasTools()?this.setConversionTogglerContent():this.nodes.conversionToggler.hidden=!0;var t=Array.from(this.buttonsList);t.unshift(this.nodes.conversionToggler),t=t.filter((function(e){return!e.hidden})),this.flipper.activate(t)}}},{key:"containsNode",value:function(e){return this.nodes.wrapper.contains(e)}},{key:"destroy",value:function(){this.flipper&&(this.flipper.deactivate(),this.flipper=null),this.removeAllNodes(),this.tooltip.destroy()}},{key:"make",value:function(){var e=this;this.nodes.wrapper=v.default.make("div",[this.CSS.inlineToolbar].concat((0,s.default)(this.isRtl?[this.Editor.UI.CSS.editorRtlFix]:[]))),this.nodes.togglerAndButtonsWrapper=v.default.make("div",this.CSS.togglerAndButtonsWrapper),this.nodes.buttons=v.default.make("div",this.CSS.buttonsWrapper),this.nodes.actions=v.default.make("div",this.CSS.actionsWrapper),this.listeners.on(this.nodes.wrapper,"mousedown",(function(t){t.target.closest(".".concat(e.CSS.actionsWrapper))||t.preventDefault()})),v.default.append(this.nodes.wrapper,[this.nodes.togglerAndButtonsWrapper,this.nodes.actions]),v.default.append(this.Editor.UI.nodes.wrapper,this.nodes.wrapper),this.addConversionToggler(),v.default.append(this.nodes.togglerAndButtonsWrapper,this.nodes.buttons),this.prepareConversionToolbar(),this.recalculateWidth(),this.enableFlipper()}},{key:"allowedToShow",value:function(){var e=g.default.get(),t=g.default.text;if(!e||!e.anchorNode)return!1;if(e.isCollapsed||t.length<1)return!1;var n=v.default.isElement(e.anchorNode)?e.anchorNode:e.anchorNode.parentElement;if(e&&["IMG","INPUT"].includes(n.tagName))return!1;if(null===n.closest('[contenteditable="true"]'))return!1;var o=this.Editor.BlockManager.getBlock(e.anchorNode);return!!o&&0!==o.tool.inlineTools.size}},{key:"recalculateWidth",value:function(){this.width=this.nodes.wrapper.offsetWidth}},{key:"addConversionToggler",value:function(){var e=this;this.nodes.conversionToggler=v.default.make("div",this.CSS.conversionToggler),this.nodes.conversionTogglerContent=v.default.make("div",this.CSS.conversionTogglerContent);var t=v.default.make("div",this.CSS.conversionTogglerArrow,{innerHTML:S.IconChevronDown});this.nodes.conversionToggler.appendChild(this.nodes.conversionTogglerContent),this.nodes.conversionToggler.appendChild(t),this.nodes.togglerAndButtonsWrapper.appendChild(this.nodes.conversionToggler),this.listeners.on(this.nodes.conversionToggler,"click",(function(){e.Editor.ConversionToolbar.toggle((function(t){!t&&e.opened?e.flipper.activate():e.opened&&e.flipper.deactivate()}))})),!1===y.isMobileScreen()&&this.tooltip.onHover(this.nodes.conversionToggler,b.default.ui(m.I18nInternalNS.ui.inlineToolbar.converter,"Convert to"),{placement:"top",hidingDelay:100})}},{key:"setConversionTogglerContent",value:(t=(0,i.default)(r.default.mark((function e(){var t,n,o,i,a,s;return r.default.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=this.Editor.BlockManager,n=t.currentBlock,o=n.name,i=n.tool.conversionConfig,a=i&&i.export,this.nodes.conversionToggler.hidden=!a,this.nodes.conversionToggler.classList.toggle(this.CSS.conversionTogglerHidden,!a),e.next=9,n.getActiveToolboxEntry();case 9:if(e.t0=e.sent,e.t0){e.next=12;break}e.t0={};case 12:s=e.t0,this.nodes.conversionTogglerContent.innerHTML=s.icon||s.title||y.capitalize(o);case 14:case"end":return e.stop()}}),e,this)}))),function(){return t.apply(this,arguments)})},{key:"prepareConversionToolbar",value:function(){var e=this.Editor.ConversionToolbar.make();v.default.append(this.nodes.wrapper,e)}},{key:"addToolsFiltered",value:function(){var e=this,t=g.default.get(),n=this.Editor.BlockManager.getBlock(t.anchorNode);this.nodes.buttons.innerHTML="",this.nodes.actions.innerHTML="",this.toolsInstances=new Map,Array.from(n.tool.inlineTools.values()).forEach((function(t){e.addTool(t)})),this.recalculateWidth()}},{key:"addTool",value:function(e){var t=this,n=e.create(),o=n.render();if(o){if(o.dataset.tool=e.name,this.nodes.buttons.appendChild(o),this.toolsInstances.set(e.name,n),y.isFunction(n.renderActions)){var r=n.renderActions();this.nodes.actions.appendChild(r)}this.listeners.on(o,"click",(function(e){t.toolClicked(n),e.preventDefault()}));var i=this.getToolShortcut(e.name);if(i)try{this.enableShortcuts(n,i)}catch(e){}var a=v.default.make("div"),s=b.default.t(m.I18nInternalNS.toolNames,e.title||y.capitalize(e.name));a.appendChild(v.default.text(s)),i&&a.appendChild(v.default.make("div",this.CSS.inlineToolbarShortcut,{textContent:y.beautifyShortcut(i)})),!1===y.isMobileScreen()&&this.tooltip.onHover(o,a,{placement:"top",hidingDelay:100}),n.checkState(g.default.get())}else y.log("Render method must return an instance of Node","warn",e.name)}},{key:"getToolShortcut",value:function(e){var t=this.Editor.Tools,n=t.inlineTools.get(e),o=t.internal.inlineTools;return Array.from(o.keys()).includes(e)?this.inlineTools[e][C.CommonInternalSettings.Shortcut]:n.shortcut}},{key:"enableShortcuts",value:function(e,t){var n=this;w.default.add({name:t,handler:function(t){var o=n.Editor.BlockManager.currentBlock;o&&o.tool.enabledInlineTools&&(t.preventDefault(),n.toolClicked(e))},on:this.Editor.UI.nodes.redactor})}},{key:"toolClicked",value:function(e){var t=g.default.range;e.surround(t),this.checkToolsState(),void 0!==e.renderActions&&this.flipper.deactivate()}},{key:"checkToolsState",value:function(){this.toolsInstances.forEach((function(e){e.checkState(g.default.get())}))}},{key:"inlineTools",get:function(){var e={};return Array.from(this.Editor.Tools.inlineTools.entries()).forEach((function(t){var n=(0,l.default)(t,2),o=n[0],r=n[1];e[o]=r.create()})),e}},{key:"enableFlipper",value:function(){this.flipper=new k.default({focusedItemClass:this.CSS.focusedButton,allowedKeys:[y.keyCodes.ENTER,y.keyCodes.TAB]})}}]),o}(h.default);o.default=M,M.displayName="InlineToolbar",e.exports=t.default})?o.apply(t,r):o)||(e.exports=i)},function(e,t,n){var o,r,i,a=n(7);"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self&&self,r=[t,n(20),n(41),n(30),n(21),n(2),n(3),n(5),n(6),n(4),n(7),n(390),n(9),n(8),n(391),n(392),n(393),n(394),n(395),n(399),n(400),n(401),n(185)],void 0===(i="function"==typeof(o=function(o,r,i,s,l,c,u,f,d,p,h,v,g,y,k,b,m,w,x,C,S,T,E){"use strict";var B=n(1);function M(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(M=function(e){return e?n:t})(e)}function _(e){var t=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}();return function(){var n,o=(0,p.default)(e);if(t){var r=(0,p.default)(this).constructor;n=Reflect.construct(o,arguments,r)}else n=o.apply(this,arguments);return(0,d.default)(this,n)}}Object.defineProperty(o,"__esModule",{value:!0}),o.default=void 0,r=B(r),i=B(i),s=B(s),l=B(l),c=B(c),u=B(u),f=B(f),d=B(d),p=B(p),h=B(h),v=B(v),g=B(g),y=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!==a(e)&&"function"!=typeof e)return{default:e};var n=M(t);if(n&&n.has(e))return n.get(e);var o={},r=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var i in e)if("default"!==i&&Object.prototype.hasOwnProperty.call(e,i)){var s=r?Object.getOwnPropertyDescriptor(e,i):null;s&&(s.get||s.set)?Object.defineProperty(o,i,s):o[i]=e[i]}return o.default=e,n&&n.set(e,o),o}(y),k=B(k),b=B(b),m=B(m),w=B(w),x=B(x),C=B(C),S=B(S),T=B(T),E=B(E);var O=function(e,t,n,o){var r,i=arguments.length,a=i<3?t:null===o?o=Object.getOwnPropertyDescriptor(t,n):o;if("object"===("undefined"==typeof Reflect?"undefined":(0,h.default)(Reflect))&&"function"==typeof Reflect.decorate)a=Reflect.decorate(e,t,n,o);else for(var s=e.length-1;s>=0;s--)(r=e[s])&&(a=(i<3?r(a):i>3?r(t,n,a):r(t,n))||a);return i>3&&a&&Object.defineProperty(t,n,a),a},I=function(e){(0,f.default)(o,e);var t,n=_(o);function o(){var e;return(0,c.default)(this,o),(e=n.apply(this,arguments)).stubTool="stub",e.toolsAvailable=new E.default,e.toolsUnavailable=new E.default,e}return(0,u.default)(o,[{key:"available",get:function(){return this.toolsAvailable}},{key:"unavailable",get:function(){return this.toolsUnavailable}},{key:"inlineTools",get:function(){return this.available.inlineTools}},{key:"blockTools",get:function(){return this.available.blockTools}},{key:"blockTunes",get:function(){return this.available.blockTunes}},{key:"defaultTool",get:function(){return this.blockTools.get(this.config.defaultBlock)}},{key:"internal",get:function(){return this.available.internalTools}},{key:"prepare",value:(t=(0,l.default)(r.default.mark((function e(){var t,n,o=this;return r.default.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(this.validateTools(),this.config.tools=y.deepMerge({},this.internalTools,this.config.tools),Object.prototype.hasOwnProperty.call(this.config,"tools")&&0!==Object.keys(this.config.tools).length){e.next=4;break}throw Error("Can't start without tools");case 4:if(t=this.prepareConfig(),this.factory=new x.default(t,this.config,this.Editor.API),0!==(n=this.getListOfPrepareFunctions(t)).length){e.next=9;break}return e.abrupt("return",Promise.resolve());case 9:return e.next=11,y.sequence(n,(function(e){o.toolPrepareMethodSuccess(e)}),(function(e){o.toolPrepareMethodFallback(e)}));case 11:this.prepareBlockTools();case 12:case"end":return e.stop()}}),e,this)}))),function(){return t.apply(this,arguments)})},{key:"getAllInlineToolsSanitizeConfig",value:function(){var e={};return Array.from(this.inlineTools.values()).forEach((function(t){Object.assign(e,t.sanitizeConfig)})),e}},{key:"destroy",value:function(){Object.values(this.available).forEach(function(){var e=(0,l.default)(r.default.mark((function e(t){return r.default.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(!y.isFunction(t.reset)){e.next=3;break}return e.next=3,t.reset();case 3:case"end":return e.stop()}}),e)})));return function(t){return e.apply(this,arguments)}}())}},{key:"internalTools",get:function(){return{bold:{class:k.default,isInternal:!0},italic:{class:b.default,isInternal:!0},link:{class:m.default,isInternal:!0},paragraph:{class:v.default,inlineToolbar:!0,isInternal:!0},stub:{class:w.default,isInternal:!0},moveUp:{class:T.default,isInternal:!0},delete:{class:S.default,isInternal:!0},moveDown:{class:C.default,isInternal:!0}}}},{key:"toolPrepareMethodSuccess",value:function(e){var t=this.factory.get(e.toolName);if(t.isInline()){var n=["render","surround","checkState"].filter((function(e){return!t.create()[e]}));if(n.length)return y.log("Incorrect Inline Tool: ".concat(t.name,". Some of required methods is not implemented %o"),"warn",n),void this.toolsUnavailable.set(t.name,t)}this.toolsAvailable.set(t.name,t)}},{key:"toolPrepareMethodFallback",value:function(e){this.toolsUnavailable.set(e.toolName,this.factory.get(e.toolName))}},{key:"getListOfPrepareFunctions",value:function(e){var t=[];return Object.entries(e).forEach((function(e){var n=(0,s.default)(e,2),o=n[0],r=n[1];t.push({function:y.isFunction(r.class.prepare)?r.class.prepare:function(){},data:{toolName:o,config:r.config}})})),t}},{key:"prepareBlockTools",value:function(){var e=this;Array.from(this.blockTools.values()).forEach((function(t){e.assignInlineToolsToBlockTool(t),e.assignBlockTunesToBlockTool(t)}))}},{key:"assignInlineToolsToBlockTool",value:function(e){var t=this;!1!==this.config.inlineToolbar&&(!0!==e.enabledInlineTools?Array.isArray(e.enabledInlineTools)&&(e.inlineTools=new E.default(e.enabledInlineTools.map((function(e){return[e,t.inlineTools.get(e)]})))):e.inlineTools=new E.default(Array.isArray(this.config.inlineToolbar)?this.config.inlineToolbar.map((function(e){return[e,t.inlineTools.get(e)]})):Array.from(this.inlineTools.entries())))}},{key:"assignBlockTunesToBlockTool",value:function(e){var t=this;if(!1!==e.enabledBlockTunes)if(Array.isArray(e.enabledBlockTunes)){var n=new E.default(e.enabledBlockTunes.map((function(e){return[e,t.blockTunes.get(e)]})));e.tunes=new E.default([].concat((0,i.default)(n),(0,i.default)(this.blockTunes.internalTools)))}else if(Array.isArray(this.config.tunes)){var o=new E.default(this.config.tunes.map((function(e){return[e,t.blockTunes.get(e)]})));e.tunes=new E.default([].concat((0,i.default)(o),(0,i.default)(this.blockTunes.internalTools)))}else e.tunes=this.blockTunes.internalTools}},{key:"validateTools",value:function(){for(var e in this.config.tools)if(Object.prototype.hasOwnProperty.call(this.config.tools,e)){if(e in this.internalTools)return;var t=this.config.tools[e];if(!y.isFunction(t)&&!y.isFunction(t.class))throw Error("Tool «".concat(e,"» must be a constructor function or an object with function in the «class» property"))}}},{key:"prepareConfig",value:function(){var e={};for(var t in this.config.tools)y.isObject(this.config.tools[t])?e[t]=this.config.tools[t]:e[t]={class:this.config.tools[t]};return e}}]),o}(g.default);o.default=I,I.displayName="Tools",O([y.cacheable],I.prototype,"getAllInlineToolsSanitizeConfig",null),e.exports=t.default})?o.apply(t,r):o)||(e.exports=i)},function(e,t,n){var o,r,i;"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self&&self,r=[t,n(30),n(2),n(3),n(5),n(6),n(4),n(153)],void 0===(i="function"==typeof(o=function(o,r,i,a,s,l,c,u){"use strict";var f=n(1);function d(e){var t=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}();return function(){var n,o=(0,c.default)(e);if(t){var r=(0,c.default)(this).constructor;n=Reflect.construct(o,arguments,r)}else n=o.apply(this,arguments);return(0,l.default)(this,n)}}Object.defineProperty(o,"__esModule",{value:!0}),o.default=void 0,r=f(r),i=f(i),a=f(a),s=f(s),l=f(l),c=f(c);var p=function(e){(0,s.default)(n,e);var t=d(n);function n(){return(0,i.default)(this,n),t.apply(this,arguments)}return(0,a.default)(n,[{key:"blockTools",get:function(){return new n(Array.from(this.entries()).filter((function(e){return(0,r.default)(e,2)[1].isBlock()})))}},{key:"inlineTools",get:function(){return new n(Array.from(this.entries()).filter((function(e){return(0,r.default)(e,2)[1].isInline()})))}},{key:"blockTunes",get:function(){return new n(Array.from(this.entries()).filter((function(e){return(0,r.default)(e,2)[1].isTune()})))}},{key:"internalTools",get:function(){return new n(Array.from(this.entries()).filter((function(e){return(0,r.default)(e,2)[1].isInternal})))}},{key:"externalTools",get:function(){return new n(Array.from(this.entries()).filter((function(e){return!(0,r.default)(e,2)[1].isInternal})))}}]),n}((0,(u=f(u)).default)(Map));o.default=p,p.displayName="ToolsCollection",e.exports=t.default})?o.apply(t,r):o)||(e.exports=i)},function(e,t,n){var o,r,i,a=n(7);"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self&&self,r=[t,n(20),n(41),n(30),n(21),n(2),n(3),n(5),n(6),n(4),n(9),n(19),n(8),n(25),n(61),n(67)],void 0===(i="function"==typeof(o=function(o,r,i,s,l,c,u,f,d,p,h,v,g,y,k,b){"use strict";var m=n(1);function w(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(w=function(e){return e?n:t})(e)}function x(e){var t=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}();return function(){var n,o=(0,p.default)(e);if(t){var r=(0,p.default)(this).constructor;n=Reflect.construct(o,arguments,r)}else n=o.apply(this,arguments);return(0,d.default)(this,n)}}Object.defineProperty(o,"__esModule",{value:!0}),o.default=void 0,r=m(r),i=m(i),s=m(s),l=m(l),c=m(c),u=m(u),f=m(f),d=m(d),p=m(p),h=m(h),v=m(v),g=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!==a(e)&&"function"!=typeof e)return{default:e};var n=w(t);if(n&&n.has(e))return n.get(e);var o={},r=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var i in e)if("default"!==i&&Object.prototype.hasOwnProperty.call(e,i)){var s=r?Object.getOwnPropertyDescriptor(e,i):null;s&&(s.get||s.set)?Object.defineProperty(o,i,s):o[i]=e[i]}return o.default=e,n&&n.set(e,o),o}(g),y=m(y),k=m(k),b=m(b);var C=function(e){(0,f.default)(a,e);var t,o=x(a);function a(){var e;return(0,c.default)(this,a),(e=o.apply(this,arguments)).isMobile=!1,e.contentRectCache=void 0,e.resizeDebouncer=g.debounce((function(){e.windowResize()}),200),e}return(0,u.default)(a,[{key:"events",get:function(){return{blockHovered:"block-hovered"}}},{key:"CSS",get:function(){return{editorWrapper:"codex-editor",editorWrapperNarrow:"codex-editor--narrow",editorZone:"codex-editor__redactor",editorZoneHidden:"codex-editor__redactor--hidden",editorLoader:"codex-editor__loader",editorEmpty:"codex-editor--empty",editorRtlFix:"codex-editor--rtl"}}},{key:"contentRect",get:function(){if(this.contentRectCache)return this.contentRectCache;var e=this.nodes.wrapper.querySelector(".".concat(k.default.CSS.content));return e?(this.contentRectCache=e.getBoundingClientRect(),this.contentRectCache):{width:650,left:0,right:0}}},{key:"addLoader",value:function(){this.nodes.loader=v.default.make("div",this.CSS.editorLoader),this.nodes.wrapper.prepend(this.nodes.loader),this.nodes.redactor.classList.add(this.CSS.editorZoneHidden)}},{key:"removeLoader",value:function(){this.nodes.loader.remove(),this.nodes.redactor.classList.remove(this.CSS.editorZoneHidden)}},{key:"prepare",value:(t=(0,l.default)(r.default.mark((function e(){return r.default.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:this.checkIsMobile(),this.make(),this.addLoader(),this.loadStyles();case 4:case"end":return e.stop()}}),e,this)}))),function(){return t.apply(this,arguments)})},{key:"toggleReadOnly",value:function(e){e?this.disableModuleBindings():this.enableModuleBindings()}},{key:"checkEmptiness",value:function(){var e=this.Editor.BlockManager;this.nodes.wrapper.classList.toggle(this.CSS.editorEmpty,e.isEditorEmpty)}},{key:"someToolbarOpened",get:function(){var e=this.Editor,t=e.Toolbar,n=e.BlockSettings,o=e.InlineToolbar,r=e.ConversionToolbar;return n.opened||o.opened||r.opened||t.toolbox.opened}},{key:"someFlipperButtonFocused",get:function(){return!!this.Editor.Toolbar.toolbox.hasFocus()||Object.entries(this.Editor).filter((function(e){var t=(0,s.default)(e,2);return t[0],t[1].flipper instanceof b.default})).some((function(e){var t=(0,s.default)(e,2);return t[0],t[1].flipper.hasFocus()}))}},{key:"destroy",value:function(){this.nodes.holder.innerHTML=""}},{key:"closeAllToolbars",value:function(){var e=this.Editor,t=e.Toolbar,n=e.BlockSettings,o=e.InlineToolbar,r=e.ConversionToolbar;n.close(),o.close(),r.close(),t.toolbox.close()}},{key:"checkIsMobile",value:function(){this.isMobile=window.innerWidth=0;if(o.anyBlockSelected&&!y.default.isSelectionExists)return o.clearSelection(e),e.preventDefault(),e.stopImmediatePropagation(),void e.stopPropagation();if(!this.someToolbarOpened&&r&&"BODY"===e.target.tagName){var i=this.Editor.BlockManager.insert();this.Editor.Caret.setToBlock(i),this.Editor.BlockManager.highlightCurrentNode(),this.Editor.Toolbar.moveAndOpen(i)}this.Editor.BlockSelection.clearSelection(e)}},{key:"documentClicked",value:function(e){if(e.isTrusted){var t=e.target;this.nodes.holder.contains(t)||y.default.isAtEditor||(this.Editor.BlockManager.dropPointer(),this.Editor.Toolbar.close());var n=this.Editor.BlockSettings.nodes.wrapper.contains(t),o=this.Editor.Toolbar.nodes.settingsToggler.contains(t),r=n||o;if(this.Editor.BlockSettings.opened&&!r){this.Editor.BlockSettings.close();var i=this.Editor.BlockManager.getBlockByChildNode(t);this.Editor.Toolbar.moveAndOpen(i)}this.Editor.BlockSelection.clearSelection(e)}}},{key:"documentTouched",value:function(e){var t=e.target;if(t===this.nodes.redactor){var n=e instanceof MouseEvent?e.clientX:e.touches[0].clientX,o=e instanceof MouseEvent?e.clientY:e.touches[0].clientY;t=document.elementFromPoint(n,o)}try{this.Editor.BlockManager.setCurrentBlockByChildNode(t),this.Editor.BlockManager.highlightCurrentNode()}catch(e){this.Editor.RectangleSelection.isRectActivated()||this.Editor.Caret.setToTheLastBlock()}this.Editor.Toolbar.moveAndOpen()}},{key:"redactorClicked",value:function(e){var t=this.Editor.BlockSelection;if(y.default.isCollapsed){var n=function(){e.stopImmediatePropagation(),e.stopPropagation()},o=e.target,r=e.metaKey||e.ctrlKey;if(v.default.isAnchor(o)&&r){n();var i=o.getAttribute("href"),a=g.getValidUrl(i);g.openTab(a)}else{var s=this.Editor.BlockManager.getBlockByIndex(-1),l=v.default.offset(s.holder).bottom,c=e.pageY;if(e.target instanceof Element&&e.target.isEqualNode(this.nodes.redactor)&&!t.anyBlockSelected&&lr;)Z(e,n=o[r++],t[n]);return e},J=function(e){var t=D.call(this,e=x(e,!0));return!(this===U&&r(H,e)&&!r(W,e))&&(!(t||!r(this,e)||!r(H,e)||r(this,A)&&this[A][e])||t)},$=function(e,t){if(e=w(e),t=x(t,!0),e!==U||!r(H,t)||r(W,t)){var n=O(e,t);return!n||!r(H,t)||r(e,A)&&e[A][t]||(n.enumerable=!0),n}},Q=function(e){for(var t,n=L(w(e)),o=[],i=0;n.length>i;)r(H,t=n[i++])||t==A||t==l||o.push(t);return o},ee=function(e){for(var t,n=e===U,o=L(n?W:w(e)),i=[],a=0;o.length>a;)!r(H,t=o[a++])||n&&!r(U,t)||i.push(H[t]);return i};z||(s((P=function(){if(this instanceof P)throw TypeError("Symbol is not a constructor!");var e=d(arguments.length>0?arguments[0]:void 0),t=function(n){this===U&&t.call(W,n),r(this,A)&&r(this[A],e)&&(this[A][e]=!1),X(this,e,C(1,n))};return i&&Y&&X(U,e,{configurable:!0,set:t}),G(e)}).prototype,"toString",(function(){return this._k})),E.f=$,M.f=Z,n(50).f=T.f=Q,n(63).f=J,B.f=ee,i&&!n(46)&&s(U,"propertyIsEnumerable",J,!0),h.f=function(e){return G(p(e))}),a(a.G+a.W+a.F*!z,{Symbol:P});for(var te="hasInstance,isConcatSpreadable,iterator,match,replace,search,species,split,toPrimitive,toStringTag,unscopables".split(","),ne=0;te.length>ne;)p(te[ne++]);for(var oe=_(p.store),re=0;oe.length>re;)v(oe[re++]);a(a.S+a.F*!z,"Symbol",{for:function(e){return r(F,e+="")?F[e]:F[e]=P(e)},keyFor:function(e){if(!K(e))throw TypeError(e+" is not a symbol!");for(var t in F)if(F[t]===e)return t},useSetter:function(){Y=!0},useSimple:function(){Y=!1}}),a(a.S+a.F*!z,"Object",{create:function(e,t){return void 0===t?S(e):q(S(e),t)},defineProperty:Z,defineProperties:q,getOwnPropertyDescriptor:$,getOwnPropertyNames:Q,getOwnPropertySymbols:ee});var ie=c((function(){B.f(1)}));a(a.S+a.F*ie,"Object",{getOwnPropertySymbols:function(e){return B.f(m(e))}}),j&&a(a.S+a.F*(!z||c((function(){var e=P();return"[null]"!=R([e])||"{}"!=R({a:e})||"{}"!=R(Object(e))}))),"JSON",{stringify:function(e){for(var t,n,o=[e],r=1;arguments.length>r;)o.push(arguments[r++]);if(n=t=o[1],(b(t)||void 0!==e)&&!K(e))return y(t)||(t=function(e,t){if("function"==typeof n&&(t=n.call(this,e,t)),!K(t))return t}),o[1]=t,R.apply(j,o)}}),P.prototype[N]||n(27)(P.prototype,N,P.prototype.valueOf),f(P,"Symbol"),f(Math,"Math",!0),f(o.JSON,"JSON",!0)},function(e,t,n){e.exports=n(70)("native-function-to-string",Function.toString)},function(e,t,n){var o=n(47),r=n(72),i=n(63);e.exports=function(e){var t=o(e),n=r.f;if(n)for(var a,s=n(e),l=i.f,c=0;s.length>c;)l.call(e,a=s[c++])&&t.push(a);return t}},function(e,t,n){var o=n(0);o(o.S,"Object",{create:n(49)})},function(e,t,n){var o=n(0);o(o.S+o.F*!n(17),"Object",{defineProperty:n(18).f})},function(e,t,n){var o=n(0);o(o.S+o.F*!n(17),"Object",{defineProperties:n(120)})},function(e,t,n){var o=n(28),r=n(34).f;n(35)("getOwnPropertyDescriptor",(function(){return function(e,t){return r(o(e),t)}}))},function(e,t,n){var o=n(22),r=n(51);n(35)("getPrototypeOf",(function(){return function(e){return r(o(e))}}))},function(e,t,n){var o=n(22),r=n(47);n(35)("keys",(function(){return function(e){return r(o(e))}}))},function(e,t,n){n(35)("getOwnPropertyNames",(function(){return n(121).f}))},function(e,t,n){var o=n(13),r=n(43).onFreeze;n(35)("freeze",(function(e){return function(t){return e&&o(t)?e(r(t)):t}}))},function(e,t,n){var o=n(13),r=n(43).onFreeze;n(35)("seal",(function(e){return function(t){return e&&o(t)?e(r(t)):t}}))},function(e,t,n){var o=n(13),r=n(43).onFreeze;n(35)("preventExtensions",(function(e){return function(t){return e&&o(t)?e(r(t)):t}}))},function(e,t,n){var o=n(13);n(35)("isFrozen",(function(e){return function(t){return!o(t)||!!e&&e(t)}}))},function(e,t,n){var o=n(13);n(35)("isSealed",(function(e){return function(t){return!o(t)||!!e&&e(t)}}))},function(e,t,n){var o=n(13);n(35)("isExtensible",(function(e){return function(t){return!!o(t)&&(!e||e(t))}}))},function(e,t,n){var o=n(0);o(o.S+o.F,"Object",{assign:n(122)})},function(e,t,n){var o=n(0);o(o.S,"Object",{is:n(123)})},function(e,t,n){var o=n(0);o(o.S,"Object",{setPrototypeOf:n(91).set})},function(e,t,n){"use strict";var o=n(64),r={};r[n(14)("toStringTag")]="z",r+""!="[object z]"&&n(23)(Object.prototype,"toString",(function(){return"[object "+o(this)+"]"}),!0)},function(e,t,n){var o=n(0);o(o.P,"Function",{bind:n(124)})},function(e,t,n){var o=n(18).f,r=Function.prototype,i=/^\s*function ([^ (]*)/;"name"in r||n(17)&&o(r,"name",{configurable:!0,get:function(){try{return(""+this).match(i)[1]}catch(e){return""}}})},function(e,t,n){"use strict";var o=n(13),r=n(51),i=n(14)("hasInstance"),a=Function.prototype;i in a||n(18).f(a,i,{value:function(e){if("function"!=typeof this||!o(e))return!1;if(!o(this.prototype))return e instanceof this;for(;e=r(e);)if(this.prototype===e)return!0;return!1}})},function(e,t,n){var o=n(0),r=n(126);o(o.G+o.F*(parseInt!=r),{parseInt:r})},function(e,t,n){var o=n(0),r=n(127);o(o.G+o.F*(parseFloat!=r),{parseFloat:r})},function(e,t,n){"use strict";var o=n(10),r=n(26),i=n(38),a=n(93),s=n(42),l=n(11),c=n(50).f,u=n(34).f,f=n(18).f,d=n(56).trim,p=o.Number,h=p,v=p.prototype,g="Number"==i(n(49)(v)),y="trim"in String.prototype,k=function(e){var t=s(e,!1);if("string"==typeof t&&t.length>2){var n,o,r,i=(t=y?t.trim():d(t,3)).charCodeAt(0);if(43===i||45===i){if(88===(n=t.charCodeAt(2))||120===n)return NaN}else if(48===i){switch(t.charCodeAt(1)){case 66:case 98:o=2,r=49;break;case 79:case 111:o=8,r=55;break;default:return+t}for(var a,l=t.slice(2),c=0,u=l.length;cr)return NaN;return parseInt(l,o)}}return+t};if(!p(" 0o1")||!p("0b1")||p("+0x1")){p=function(e){var t=arguments.length<1?0:e,n=this;return n instanceof p&&(g?l((function(){v.valueOf.call(n)})):"Number"!=i(n))?a(new h(k(t)),n,p):k(t)};for(var b,m=n(17)?c(h):"MAX_VALUE,MIN_VALUE,NaN,NEGATIVE_INFINITY,POSITIVE_INFINITY,EPSILON,isFinite,isInteger,isNaN,isSafeInteger,MAX_SAFE_INTEGER,MIN_SAFE_INTEGER,parseFloat,parseInt,isInteger".split(","),w=0;m.length>w;w++)r(h,b=m[w])&&!r(p,b)&&f(p,b,u(h,b));p.prototype=v,v.constructor=p,n(23)(o,"Number",p)}},function(e,t,n){"use strict";var o=n(0),r=n(33),i=n(128),a=n(94),s=1..toFixed,l=Math.floor,c=[0,0,0,0,0,0],u="Number.toFixed: incorrect invocation!",f=function(e,t){for(var n=-1,o=t;++n<6;)o+=e*c[n],c[n]=o%1e7,o=l(o/1e7)},d=function(e){for(var t=6,n=0;--t>=0;)n+=c[t],c[t]=l(n/e),n=n%e*1e7},p=function(){for(var e=6,t="";--e>=0;)if(""!==t||0===e||0!==c[e]){var n=String(c[e]);t=""===t?n:t+a.call("0",7-n.length)+n}return t},h=function(e,t,n){return 0===t?n:t%2==1?h(e,t-1,n*e):h(e*e,t/2,n)};o(o.P+o.F*(!!s&&("0.000"!==8e-5.toFixed(3)||"1"!==.9.toFixed(0)||"1.25"!==1.255.toFixed(2)||"1000000000000000128"!==(0xde0b6b3a7640080).toFixed(0))||!n(11)((function(){s.call({})}))),"Number",{toFixed:function(e){var t,n,o,s,l=i(this,u),c=r(e),v="",g="0";if(c<0||c>20)throw RangeError(u);if(l!=l)return"NaN";if(l<=-1e21||l>=1e21)return String(l);if(l<0&&(v="-",l=-l),l>1e-21)if(n=(t=function(e){for(var t=0,n=e;n>=4096;)t+=12,n/=4096;for(;n>=2;)t+=1,n/=2;return t}(l*h(2,69,1))-69)<0?l*h(2,-t,1):l/h(2,t,1),n*=4503599627370496,(t=52-t)>0){for(f(0,n),o=c;o>=7;)f(1e7,0),o-=7;for(f(h(10,o,1),0),o=t-1;o>=23;)d(1<<23),o-=23;d(1<0?v+((s=g.length)<=c?"0."+a.call("0",c-s)+g:g.slice(0,s-c)+"."+g.slice(s-c)):v+g}})},function(e,t,n){"use strict";var o=n(0),r=n(11),i=n(128),a=1..toPrecision;o(o.P+o.F*(r((function(){return"1"!==a.call(1,void 0)}))||!r((function(){a.call({})}))),"Number",{toPrecision:function(e){var t=i(this,"Number#toPrecision: incorrect invocation!");return void 0===e?a.call(t):a.call(t,e)}})},function(e,t,n){var o=n(0);o(o.S,"Number",{EPSILON:Math.pow(2,-52)})},function(e,t,n){var o=n(0),r=n(10).isFinite;o(o.S,"Number",{isFinite:function(e){return"number"==typeof e&&r(e)}})},function(e,t,n){var o=n(0);o(o.S,"Number",{isInteger:n(129)})},function(e,t,n){var o=n(0);o(o.S,"Number",{isNaN:function(e){return e!=e}})},function(e,t,n){var o=n(0),r=n(129),i=Math.abs;o(o.S,"Number",{isSafeInteger:function(e){return r(e)&&i(e)<=9007199254740991}})},function(e,t,n){var o=n(0);o(o.S,"Number",{MAX_SAFE_INTEGER:9007199254740991})},function(e,t,n){var o=n(0);o(o.S,"Number",{MIN_SAFE_INTEGER:-9007199254740991})},function(e,t,n){var o=n(0),r=n(127);o(o.S+o.F*(Number.parseFloat!=r),"Number",{parseFloat:r})},function(e,t,n){var o=n(0),r=n(126);o(o.S+o.F*(Number.parseInt!=r),"Number",{parseInt:r})},function(e,t,n){var o=n(0),r=n(130),i=Math.sqrt,a=Math.acosh;o(o.S+o.F*!(a&&710==Math.floor(a(Number.MAX_VALUE))&&a(1/0)==1/0),"Math",{acosh:function(e){return(e=+e)<1?NaN:e>94906265.62425156?Math.log(e)+Math.LN2:r(e-1+i(e-1)*i(e+1))}})},function(e,t,n){var o=n(0),r=Math.asinh;o(o.S+o.F*!(r&&1/r(0)>0),"Math",{asinh:function e(t){return isFinite(t=+t)&&0!=t?t<0?-e(-t):Math.log(t+Math.sqrt(t*t+1)):t}})},function(e,t,n){var o=n(0),r=Math.atanh;o(o.S+o.F*!(r&&1/r(-0)<0),"Math",{atanh:function(e){return 0==(e=+e)?e:Math.log((1+e)/(1-e))/2}})},function(e,t,n){var o=n(0),r=n(95);o(o.S,"Math",{cbrt:function(e){return r(e=+e)*Math.pow(Math.abs(e),1/3)}})},function(e,t,n){var o=n(0);o(o.S,"Math",{clz32:function(e){return(e>>>=0)?31-Math.floor(Math.log(e+.5)*Math.LOG2E):32}})},function(e,t,n){var o=n(0),r=Math.exp;o(o.S,"Math",{cosh:function(e){return(r(e=+e)+r(-e))/2}})},function(e,t,n){var o=n(0),r=n(96);o(o.S+o.F*(r!=Math.expm1),"Math",{expm1:r})},function(e,t,n){var o=n(0);o(o.S,"Math",{fround:n(236)})},function(e,t,n){var o=n(95),r=Math.pow,i=r(2,-52),a=r(2,-23),s=r(2,127)*(2-a),l=r(2,-126);e.exports=Math.fround||function(e){var t,n,r=Math.abs(e),c=o(e);return rs||n!=n?c*(1/0):c*n}},function(e,t,n){var o=n(0),r=Math.abs;o(o.S,"Math",{hypot:function(e,t){for(var n,o,i=0,a=0,s=arguments.length,l=0;a0?(o=n/l)*o:n;return l===1/0?1/0:l*Math.sqrt(i)}})},function(e,t,n){var o=n(0),r=Math.imul;o(o.S+o.F*n(11)((function(){return-5!=r(4294967295,5)||2!=r.length})),"Math",{imul:function(e,t){var n=+e,o=+t,r=65535&n,i=65535&o;return 0|r*i+((65535&n>>>16)*i+r*(65535&o>>>16)<<16>>>0)}})},function(e,t,n){var o=n(0);o(o.S,"Math",{log10:function(e){return Math.log(e)*Math.LOG10E}})},function(e,t,n){var o=n(0);o(o.S,"Math",{log1p:n(130)})},function(e,t,n){var o=n(0);o(o.S,"Math",{log2:function(e){return Math.log(e)/Math.LN2}})},function(e,t,n){var o=n(0);o(o.S,"Math",{sign:n(95)})},function(e,t,n){var o=n(0),r=n(96),i=Math.exp;o(o.S+o.F*n(11)((function(){return-2e-17!=!Math.sinh(-2e-17)})),"Math",{sinh:function(e){return Math.abs(e=+e)<1?(r(e)-r(-e))/2:(i(e-1)-i(-e-1))*(Math.E/2)}})},function(e,t,n){var o=n(0),r=n(96),i=Math.exp;o(o.S,"Math",{tanh:function(e){var t=r(e=+e),n=r(-e);return t==1/0?1:n==1/0?-1:(t-n)/(i(e)+i(-e))}})},function(e,t,n){var o=n(0);o(o.S,"Math",{trunc:function(e){return(e>0?Math.floor:Math.ceil)(e)}})},function(e,t,n){var o=n(0),r=n(48),i=String.fromCharCode,a=String.fromCodePoint;o(o.S+o.F*(!!a&&1!=a.length),"String",{fromCodePoint:function(e){for(var t,n=[],o=arguments.length,a=0;o>a;){if(t=+arguments[a++],r(t,1114111)!==t)throw RangeError(t+" is not a valid code point");n.push(t<65536?i(t):i(55296+((t-=65536)>>10),t%1024+56320))}return n.join("")}})},function(e,t,n){var o=n(0),r=n(28),i=n(15);o(o.S,"String",{raw:function(e){for(var t=r(e.raw),n=i(t.length),o=arguments.length,a=[],s=0;n>s;)a.push(String(t[s++])),s=t.length?{value:void 0,done:!0}:(e=o(t,n),this._i+=e.length,{value:e,done:!1})}))},function(e,t,n){"use strict";var o=n(0),r=n(97)(!1);o(o.P,"String",{codePointAt:function(e){return r(this,e)}})},function(e,t,n){"use strict";var o=n(0),r=n(15),i=n(99),a="".endsWith;o(o.P+o.F*n(101)("endsWith"),"String",{endsWith:function(e){var t=i(this,e,"endsWith"),n=arguments.length>1?arguments[1]:void 0,o=r(t.length),s=void 0===n?o:Math.min(r(n),o),l=String(e);return a?a.call(t,l,s):t.slice(s-l.length,s)===l}})},function(e,t,n){"use strict";var o=n(0),r=n(99);o(o.P+o.F*n(101)("includes"),"String",{includes:function(e){return!!~r(this,e,"includes").indexOf(e,arguments.length>1?arguments[1]:void 0)}})},function(e,t,n){var o=n(0);o(o.P,"String",{repeat:n(94)})},function(e,t,n){"use strict";var o=n(0),r=n(15),i=n(99),a="".startsWith;o(o.P+o.F*n(101)("startsWith"),"String",{startsWith:function(e){var t=i(this,e,"startsWith"),n=r(Math.min(arguments.length>1?arguments[1]:void 0,t.length)),o=String(e);return a?a.call(t,o,n):t.slice(n,n+o.length)===o}})},function(e,t,n){"use strict";n(24)("anchor",(function(e){return function(t){return e(this,"a","name",t)}}))},function(e,t,n){"use strict";n(24)("big",(function(e){return function(){return e(this,"big","","")}}))},function(e,t,n){"use strict";n(24)("blink",(function(e){return function(){return e(this,"blink","","")}}))},function(e,t,n){"use strict";n(24)("bold",(function(e){return function(){return e(this,"b","","")}}))},function(e,t,n){"use strict";n(24)("fixed",(function(e){return function(){return e(this,"tt","","")}}))},function(e,t,n){"use strict";n(24)("fontcolor",(function(e){return function(t){return e(this,"font","color",t)}}))},function(e,t,n){"use strict";n(24)("fontsize",(function(e){return function(t){return e(this,"font","size",t)}}))},function(e,t,n){"use strict";n(24)("italics",(function(e){return function(){return e(this,"i","","")}}))},function(e,t,n){"use strict";n(24)("link",(function(e){return function(t){return e(this,"a","href",t)}}))},function(e,t,n){"use strict";n(24)("small",(function(e){return function(){return e(this,"small","","")}}))},function(e,t,n){"use strict";n(24)("strike",(function(e){return function(){return e(this,"strike","","")}}))},function(e,t,n){"use strict";n(24)("sub",(function(e){return function(){return e(this,"sub","","")}}))},function(e,t,n){"use strict";n(24)("sup",(function(e){return function(){return e(this,"sup","","")}}))},function(e,t,n){var o=n(0);o(o.S,"Date",{now:function(){return(new Date).getTime()}})},function(e,t,n){"use strict";var o=n(0),r=n(22),i=n(42);o(o.P+o.F*n(11)((function(){return null!==new Date(NaN).toJSON()||1!==Date.prototype.toJSON.call({toISOString:function(){return 1}})})),"Date",{toJSON:function(e){var t=r(this),n=i(t);return"number"!=typeof n||isFinite(n)?t.toISOString():null}})},function(e,t,n){var o=n(0),r=n(271);o(o.P+o.F*(Date.prototype.toISOString!==r),"Date",{toISOString:r})},function(e,t,n){"use strict";var o=n(11),r=Date.prototype.getTime,i=Date.prototype.toISOString,a=function(e){return e>9?e:"0"+e};e.exports=o((function(){return"0385-07-25T07:06:39.999Z"!=i.call(new Date(-50000000000001))}))||!o((function(){i.call(new Date(NaN))}))?function(){if(!isFinite(r.call(this)))throw RangeError("Invalid time value");var e=this,t=e.getUTCFullYear(),n=e.getUTCMilliseconds(),o=t<0?"-":t>9999?"+":"";return o+("00000"+Math.abs(t)).slice(o?-6:-4)+"-"+a(e.getUTCMonth()+1)+"-"+a(e.getUTCDate())+"T"+a(e.getUTCHours())+":"+a(e.getUTCMinutes())+":"+a(e.getUTCSeconds())+"."+(n>99?n:"0"+a(n))+"Z"}:i},function(e,t,n){var o=Date.prototype,r=o.toString,i=o.getTime;new Date(NaN)+""!="Invalid Date"&&n(23)(o,"toString",(function(){var e=i.call(this);return e==e?r.call(this):"Invalid Date"}))},function(e,t,n){var o=n(14)("toPrimitive"),r=Date.prototype;o in r||n(27)(r,o,n(274))},function(e,t,n){"use strict";var o=n(12),r=n(42);e.exports=function(e){if("string"!==e&&"number"!==e&&"default"!==e)throw TypeError("Incorrect hint");return r(o(this),"number"!=e)}},function(e,t,n){var o=n(0);o(o.S,"Array",{isArray:n(73)})},function(e,t,n){"use strict";var o=n(31),r=n(0),i=n(22),a=n(132),s=n(102),l=n(15),c=n(103),u=n(104);r(r.S+r.F*!n(74)((function(e){Array.from(e)})),"Array",{from:function(e){var t,n,r,f,d=i(e),p="function"==typeof this?this:Array,h=arguments.length,v=h>1?arguments[1]:void 0,g=void 0!==v,y=0,k=u(d);if(g&&(v=o(v,h>2?arguments[2]:void 0,2)),null==k||p==Array&&s(k))for(n=new p(t=l(d.length));t>y;y++)c(n,y,g?v(d[y],y):d[y]);else for(f=k.call(d),n=new p;!(r=f.next()).done;y++)c(n,y,g?a(f,v,[r.value,y],!0):r.value);return n.length=y,n}})},function(e,t,n){"use strict";var o=n(0),r=n(103);o(o.S+o.F*n(11)((function(){function e(){}return!(Array.of.call(e)instanceof e)})),"Array",{of:function(){for(var e=0,t=arguments.length,n=new("function"==typeof this?this:Array)(t);t>e;)r(n,e,arguments[e++]);return n.length=t,n}})},function(e,t,n){"use strict";var o=n(0),r=n(28),i=[].join;o(o.P+o.F*(n(62)!=Object||!n(29)(i)),"Array",{join:function(e){return i.call(r(this),void 0===e?",":e)}})},function(e,t,n){"use strict";var o=n(0),r=n(90),i=n(38),a=n(48),s=n(15),l=[].slice;o(o.P+o.F*n(11)((function(){r&&l.call(r)})),"Array",{slice:function(e,t){var n=s(this.length),o=i(this);if(t=void 0===t?n:t,"Array"==o)return l.call(this,e,t);for(var r=a(e,n),c=a(t,n),u=s(c-r),f=new Array(u),d=0;d1&&(o=Math.min(o,i(arguments[1]))),o<0&&(o=n+o);o>=0;o--)if(o in t&&t[o]===e)return o||0;return-1}})},function(e,t,n){var o=n(0);o(o.P,"Array",{copyWithin:n(135)}),n(52)("copyWithin")},function(e,t,n){var o=n(0);o(o.P,"Array",{fill:n(105)}),n(52)("fill")},function(e,t,n){"use strict";var o=n(0),r=n(36)(5),i=!0;"find"in[]&&Array(1).find((function(){i=!1})),o(o.P+o.F*i,"Array",{find:function(e){return r(this,e,arguments.length>1?arguments[1]:void 0)}}),n(52)("find")},function(e,t,n){"use strict";var o=n(0),r=n(36)(6),i="findIndex",a=!0;i in[]&&Array(1)[i]((function(){a=!1})),o(o.P+o.F*a,"Array",{findIndex:function(e){return r(this,e,arguments.length>1?arguments[1]:void 0)}}),n(52)(i)},function(e,t,n){n(58)("Array")},function(e,t,n){var o=n(10),r=n(93),i=n(18).f,a=n(50).f,s=n(100),l=n(75),c=o.RegExp,u=c,f=c.prototype,d=/a/g,p=/a/g,h=new c(d)!==d;if(n(17)&&(!h||n(11)((function(){return p[n(14)("match")]=!1,c(d)!=d||c(p)==p||"/a/i"!=c(d,"i")})))){c=function(e,t){var n=this instanceof c,o=s(e),i=void 0===t;return!n&&o&&e.constructor===c&&i?e:r(h?new u(o&&!i?e.source:e,t):u((o=e instanceof c)?e.source:e,o&&i?l.call(e):t),n?this:f,c)};for(var v=function(e){e in c||i(c,e,{configurable:!0,get:function(){return u[e]},set:function(t){u[e]=t}})},g=a(u),y=0;g.length>y;)v(g[y++]);f.constructor=c,c.prototype=f,n(23)(o,"RegExp",c)}n(58)("RegExp")},function(e,t,n){"use strict";n(138);var o=n(12),r=n(75),i=n(17),a=/./.toString,s=function(e){n(23)(RegExp.prototype,"toString",e,!0)};n(11)((function(){return"/a/b"!=a.call({source:"a",flags:"b"})}))?s((function(){var e=o(this);return"/".concat(e.source,"/","flags"in e?e.flags:!i&&e instanceof RegExp?r.call(e):void 0)})):"toString"!=a.name&&s((function(){return a.call(this)}))},function(e,t,n){"use strict";var o=n(12),r=n(15),i=n(108),a=n(76);n(77)("match",1,(function(e,t,n,s){return[function(n){var o=e(this),r=null==n?void 0:n[t];return void 0!==r?r.call(n,o):new RegExp(n)[t](String(o))},function(e){var t=s(n,e,this);if(t.done)return t.value;var l=o(e),c=String(this);if(!l.global)return a(l,c);var u=l.unicode;l.lastIndex=0;for(var f,d=[],p=0;null!==(f=a(l,c));){var h=String(f[0]);d[p]=h,""===h&&(l.lastIndex=i(c,r(l.lastIndex),u)),p++}return 0===p?null:d}]}))},function(e,t,n){"use strict";var o=n(12),r=n(22),i=n(15),a=n(33),s=n(108),l=n(76),c=Math.max,u=Math.min,f=Math.floor,d=/\$([$&`']|\d\d?|<[^>]*>)/g,p=/\$([$&`']|\d\d?)/g;n(77)("replace",2,(function(e,t,n,h){return[function(o,r){var i=e(this),a=null==o?void 0:o[t];return void 0!==a?a.call(o,i,r):n.call(String(i),o,r)},function(e,t){var r=h(n,e,this,t);if(r.done)return r.value;var f=o(e),d=String(this),p="function"==typeof t;p||(t=String(t));var g=f.global;if(g){var y=f.unicode;f.lastIndex=0}for(var k=[];;){var b=l(f,d);if(null===b)break;if(k.push(b),!g)break;""===String(b[0])&&(f.lastIndex=s(d,i(f.lastIndex),y))}for(var m,w="",x=0,C=0;C=x&&(w+=d.slice(x,T)+O,x=T+S.length)}return w+d.slice(x)}];function v(e,t,o,i,a,s){var l=o+e.length,c=i.length,u=p;return void 0!==a&&(a=r(a),u=d),n.call(s,u,(function(n,r){var s;switch(r.charAt(0)){case"$":return"$";case"&":return e;case"`":return t.slice(0,o);case"'":return t.slice(l);case"<":s=a[r.slice(1,-1)];break;default:var u=+r;if(0===u)return n;if(u>c){var d=f(u/10);return 0===d?n:d<=c?void 0===i[d-1]?r.charAt(1):i[d-1]+r.charAt(1):n}s=i[u-1]}return void 0===s?"":s}))}}))},function(e,t,n){"use strict";var o=n(12),r=n(123),i=n(76);n(77)("search",1,(function(e,t,n,a){return[function(n){var o=e(this),r=null==n?void 0:n[t];return void 0!==r?r.call(n,o):new RegExp(n)[t](String(o))},function(e){var t=a(n,e,this);if(t.done)return t.value;var s=o(e),l=String(this),c=s.lastIndex;r(c,0)||(s.lastIndex=0);var u=i(s,l);return r(s.lastIndex,c)||(s.lastIndex=c),null===u?-1:u.index}]}))},function(e,t,n){"use strict";var o=n(100),r=n(12),i=n(65),a=n(108),s=n(15),l=n(76),c=n(107),u=n(11),f=Math.min,d=[].push,p="length",h=!u((function(){RegExp(4294967295,"y")}));n(77)("split",2,(function(e,t,n,u){var v;return v="c"=="abbc".split(/(b)*/)[1]||4!="test".split(/(?:)/,-1)[p]||2!="ab".split(/(?:ab)*/)[p]||4!=".".split(/(.?)(.?)/)[p]||".".split(/()()/)[p]>1||"".split(/.?/)[p]?function(e,t){var r=String(this);if(void 0===e&&0===t)return[];if(!o(e))return n.call(r,e,t);for(var i,a,s,l=[],u=(e.ignoreCase?"i":"")+(e.multiline?"m":"")+(e.unicode?"u":"")+(e.sticky?"y":""),f=0,h=void 0===t?4294967295:t>>>0,v=new RegExp(e.source,u+"g");(i=c.call(v,r))&&!((a=v.lastIndex)>f&&(l.push(r.slice(f,i.index)),i[p]>1&&i.index=h));)v.lastIndex===i.index&&v.lastIndex++;return f===r[p]?!s&&v.test("")||l.push(""):l.push(r.slice(f)),l[p]>h?l.slice(0,h):l}:"0".split(void 0,0)[p]?function(e,t){return void 0===e&&0===t?[]:n.call(this,e,t)}:n,[function(n,o){var r=e(this),i=null==n?void 0:n[t];return void 0!==i?i.call(n,r,o):v.call(String(r),n,o)},function(e,t){var o=u(v,e,this,t,v!==n);if(o.done)return o.value;var c=r(e),d=String(this),p=i(c,RegExp),g=c.unicode,y=(c.ignoreCase?"i":"")+(c.multiline?"m":"")+(c.unicode?"u":"")+(h?"y":"g"),k=new p(h?c:"^(?:"+c.source+")",y),b=void 0===t?4294967295:t>>>0;if(0===b)return[];if(0===d.length)return null===l(k,d)?[d]:[];for(var m=0,w=0,x=[];w0?arguments[0]:void 0)}}),{get:function(e){var t=o.getEntry(r(this,"Map"),e);return t&&t.v},set:function(e,t){return o.def(r(this,"Map"),0===e?0:e,t)}},o,!0)},function(e,t,n){"use strict";var o=n(142),r=n(53);e.exports=n(80)("Set",(function(e){return function(){return e(this,arguments.length>0?arguments[0]:void 0)}}),{add:function(e){return o.def(r(this,"Set"),e=0===e?0:e,e)}},o)},function(e,t,n){"use strict";var o,r=n(10),i=n(36)(0),a=n(23),s=n(43),l=n(122),c=n(143),u=n(13),f=n(53),d=n(53),p=!r.ActiveXObject&&"ActiveXObject"in r,h=s.getWeak,v=Object.isExtensible,g=c.ufstore,y=function(e){return function(){return e(this,arguments.length>0?arguments[0]:void 0)}},k={get:function(e){if(u(e)){var t=h(e);return!0===t?g(f(this,"WeakMap")).get(e):t?t[this._i]:void 0}},set:function(e,t){return c.def(f(this,"WeakMap"),e,t)}},b=e.exports=n(80)("WeakMap",y,k,c,!0,!0);d&&p&&(l((o=c.getConstructor(y,"WeakMap")).prototype,k),s.NEED=!0,i(["delete","has","get","set"],(function(e){var t=b.prototype,n=t[e];a(t,e,(function(t,r){if(u(t)&&!v(t)){this._f||(this._f=new o);var i=this._f[e](t,r);return"set"==e?this:i}return n.call(this,t,r)}))})))},function(e,t,n){"use strict";var o=n(143),r=n(53);n(80)("WeakSet",(function(e){return function(){return e(this,arguments.length>0?arguments[0]:void 0)}}),{add:function(e){return o.def(r(this,"WeakSet"),e,!0)}},o,!1,!0)},function(e,t,n){"use strict";var o=n(0),r=n(81),i=n(110),a=n(12),s=n(48),l=n(15),c=n(13),u=n(10).ArrayBuffer,f=n(65),d=i.ArrayBuffer,p=i.DataView,h=r.ABV&&u.isView,v=d.prototype.slice,g=r.VIEW;o(o.G+o.W+o.F*(u!==d),{ArrayBuffer:d}),o(o.S+o.F*!r.CONSTR,"ArrayBuffer",{isView:function(e){return h&&h(e)||c(e)&&g in e}}),o(o.P+o.U+o.F*n(11)((function(){return!new d(2).slice(1,void 0).byteLength})),"ArrayBuffer",{slice:function(e,t){if(void 0!==v&&void 0===t)return v.call(a(this),e);for(var n=a(this).byteLength,o=s(e,n),r=s(void 0===t?n:t,n),i=new(f(this,d))(l(r-o)),c=new p(this),u=new p(i),h=0;o=t.length)return{value:void 0,done:!0}}while(!((e=t[this._i++])in this._t));return{value:e,done:!1}})),o(o.S,"Reflect",{enumerate:function(e){return new i(e)}})},function(e,t,n){var o=n(34),r=n(51),i=n(26),a=n(0),s=n(13),l=n(12);a(a.S,"Reflect",{get:function e(t,n){var a,c,u=arguments.length<3?t:arguments[2];return l(t)===u?t[n]:(a=o.f(t,n))?i(a,"value")?a.value:void 0!==a.get?a.get.call(u):void 0:s(c=r(t))?e(c,n,u):void 0}})},function(e,t,n){var o=n(34),r=n(0),i=n(12);r(r.S,"Reflect",{getOwnPropertyDescriptor:function(e,t){return o.f(i(e),t)}})},function(e,t,n){var o=n(0),r=n(51),i=n(12);o(o.S,"Reflect",{getPrototypeOf:function(e){return r(i(e))}})},function(e,t,n){var o=n(0);o(o.S,"Reflect",{has:function(e,t){return t in e}})},function(e,t,n){var o=n(0),r=n(12),i=Object.isExtensible;o(o.S,"Reflect",{isExtensible:function(e){return r(e),!i||i(e)}})},function(e,t,n){var o=n(0);o(o.S,"Reflect",{ownKeys:n(145)})},function(e,t,n){var o=n(0),r=n(12),i=Object.preventExtensions;o(o.S,"Reflect",{preventExtensions:function(e){r(e);try{return i&&i(e),!0}catch(e){return!1}}})},function(e,t,n){var o=n(18),r=n(34),i=n(51),a=n(26),s=n(0),l=n(44),c=n(12),u=n(13);s(s.S,"Reflect",{set:function e(t,n,s){var f,d,p=arguments.length<4?t:arguments[3],h=r.f(c(t),n);if(!h){if(u(d=i(t)))return e(d,n,s,p);h=l(0)}if(a(h,"value")){if(!1===h.writable||!u(p))return!1;if(f=r.f(p,n)){if(f.get||f.set||!1===f.writable)return!1;f.value=s,o.f(p,n,f)}else o.f(p,n,l(0,s));return!0}return void 0!==h.set&&(h.set.call(p,s),!0)}})},function(e,t,n){var o=n(0),r=n(91);r&&o(o.S,"Reflect",{setPrototypeOf:function(e,t){r.check(e,t);try{return r.set(e,t),!0}catch(e){return!1}}})},function(e,t,n){n(334),e.exports=n(16).Array.includes},function(e,t,n){"use strict";var o=n(0),r=n(71)(!0);o(o.P,"Array",{includes:function(e){return r(this,e,arguments.length>1?arguments[1]:void 0)}}),n(52)("includes")},function(e,t,n){n(336),e.exports=n(16).Array.flatMap},function(e,t,n){"use strict";var o=n(0),r=n(337),i=n(22),a=n(15),s=n(32),l=n(133);o(o.P,"Array",{flatMap:function(e){var t,n,o=i(this);return s(e),t=a(o.length),n=l(o,0),r(n,o,o,t,0,1,e,arguments[1]),n}}),n(52)("flatMap")},function(e,t,n){"use strict";var o=n(73),r=n(13),i=n(15),a=n(31),s=n(14)("isConcatSpreadable");e.exports=function e(t,n,l,c,u,f,d,p){for(var h,v,g=u,y=0,k=!!d&&a(d,p,3);y0)g=e(t,n,h,i(h.length),g,f-1)-1;else{if(g>=9007199254740991)throw TypeError();t[g]=h}g++}y++}return g}},function(e,t,n){n(339),e.exports=n(16).String.padStart},function(e,t,n){"use strict";var o=n(0),r=n(146),i=n(79),a=/Version\/10\.\d+(\.\d+)?( Mobile\/\w+)? Safari\//.test(i);o(o.P+o.F*a,"String",{padStart:function(e){return r(this,e,arguments.length>1?arguments[1]:void 0,!0)}})},function(e,t,n){n(341),e.exports=n(16).String.padEnd},function(e,t,n){"use strict";var o=n(0),r=n(146),i=n(79),a=/Version\/10\.\d+(\.\d+)?( Mobile\/\w+)? Safari\//.test(i);o(o.P+o.F*a,"String",{padEnd:function(e){return r(this,e,arguments.length>1?arguments[1]:void 0,!1)}})},function(e,t,n){n(343),e.exports=n(16).String.trimLeft},function(e,t,n){"use strict";n(56)("trimLeft",(function(e){return function(){return e(this,1)}}),"trimStart")},function(e,t,n){n(345),e.exports=n(16).String.trimRight},function(e,t,n){"use strict";n(56)("trimRight",(function(e){return function(){return e(this,2)}}),"trimEnd")},function(e,t,n){n(347),e.exports=n(87).f("asyncIterator")},function(e,t,n){n(118)("asyncIterator")},function(e,t,n){n(349),e.exports=n(16).Object.getOwnPropertyDescriptors},function(e,t,n){var o=n(0),r=n(145),i=n(28),a=n(34),s=n(103);o(o.S,"Object",{getOwnPropertyDescriptors:function(e){for(var t,n,o=i(e),l=a.f,c=r(o),u={},f=0;c.length>f;)void 0!==(n=l(o,t=c[f++]))&&s(u,t,n);return u}})},function(e,t,n){n(351),e.exports=n(16).Object.values},function(e,t,n){var o=n(0),r=n(147)(!1);o(o.S,"Object",{values:function(e){return r(e)}})},function(e,t,n){n(353),e.exports=n(16).Object.entries},function(e,t,n){var o=n(0),r=n(147)(!0);o(o.S,"Object",{entries:function(e){return r(e)}})},function(e,t,n){"use strict";n(139),n(355),e.exports=n(16).Promise.finally},function(e,t,n){"use strict";var o=n(0),r=n(16),i=n(10),a=n(65),s=n(141);o(o.P+o.R,"Promise",{finally:function(e){var t=a(this,r.Promise||i.Promise),n="function"==typeof e;return this.then(n?function(n){return s(t,e()).then((function(){return n}))}:e,n?function(n){return s(t,e()).then((function(){throw n}))}:e)}})},function(e,t,n){n(357),n(358),n(359),e.exports=n(16)},function(e,t,n){var o=n(10),r=n(0),i=n(79),a=[].slice,s=/MSIE .\./.test(i),l=function(e){return function(t,n){var o=arguments.length>2,r=!!o&&a.call(arguments,2);return e(o?function(){("function"==typeof t?t:Function(t)).apply(this,r)}:t,n)}};r(r.G+r.B+r.F*s,{setTimeout:l(o.setTimeout),setInterval:l(o.setInterval)})},function(e,t,n){var o=n(0),r=n(109);o(o.G+o.B,{setImmediate:r.set,clearImmediate:r.clear})},function(e,t,n){for(var o=n(106),r=n(47),i=n(23),a=n(10),s=n(27),l=n(57),c=n(14),u=c("iterator"),f=c("toStringTag"),d=l.Array,p={CSSRuleList:!0,CSSStyleDeclaration:!1,CSSValueList:!1,ClientRectList:!1,DOMRectList:!1,DOMStringList:!1,DOMTokenList:!0,DataTransferItemList:!1,FileList:!1,HTMLAllCollection:!1,HTMLCollection:!1,HTMLFormElement:!1,HTMLSelectElement:!1,MediaList:!0,MimeTypeArray:!1,NamedNodeMap:!1,NodeList:!0,PaintRequestList:!1,Plugin:!1,PluginArray:!1,SVGLengthList:!1,SVGNumberList:!1,SVGPathSegList:!1,SVGPointList:!1,SVGStringList:!1,SVGTransformList:!1,SourceBufferList:!1,StyleSheetList:!0,TextTrackCueList:!1,TextTrackList:!1,TouchList:!1},h=r(p),v=0;v=0;--r){var i=this.tryEntries[r],a=i.completion;if("root"===i.tryLoc)return o("end");if(i.tryLoc<=this.prev){var s=n.call(i,"catchLoc"),l=n.call(i,"finallyLoc");if(s&&l){if(this.prev=0;--o){var r=this.tryEntries[o];if(r.tryLoc<=this.prev&&n.call(r,"finallyLoc")&&this.prev=0;--t){var n=this.tryEntries[t];if(n.finallyLoc===e)return this.complete(n.completion,n.afterLoc),S(n),f}},catch:function(e){for(var t=this.tryEntries.length-1;t>=0;--t){var n=this.tryEntries[t];if(n.tryLoc===e){var o=n.completion;if("throw"===o.type){var r=o.arg;S(n)}return r}}throw new Error("illegal catch attempt")},delegateYield:function(e,t,n){return this.delegate={iterator:E(e),resultName:t,nextLoc:n},"next"===this.method&&(this.arg=void 0),f}},e}(e.exports);try{regeneratorRuntime=o}catch(e){"object"==typeof globalThis?globalThis.regeneratorRuntime=o:Function("r","regeneratorRuntime = r")(o)}},function(e,t,n){var o,r,i,a=n(7);"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self&&self,r=[t,n(30),n(2),n(3),n(365),n(366),n(367),n(8)],void 0===(i="function"==typeof(o=function(o,r,i,s,l,c,u,f){"use strict";var d=n(1);function p(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(p=function(e){return e?n:t})(e)}Object.defineProperty(o,"__esModule",{value:!0}),o.default=void 0,r=d(r),i=d(i),s=d(s),u=d(u),f=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!==a(e)&&"function"!=typeof e)return{default:e};var n=p(t);if(n&&n.has(e))return n.get(e);var o={},r=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var i in e)if("default"!==i&&Object.prototype.hasOwnProperty.call(e,i)){var s=r?Object.getOwnPropertyDescriptor(e,i):null;s&&(s.get||s.set)?Object.defineProperty(o,i,s):o[i]=e[i]}return o.default=e,n&&n.set(e,o),o}(f);var h=function(){function e(t){var n=this;(0,i.default)(this,e);var o=function(){};f.isObject(t)&&f.isFunction(t.onReady)&&(o=t.onReady);var r=new u.default(t);this.isReady=r.isReady.then((function(){n.exportAPI(r),o()}))}return(0,s.default)(e,[{key:"exportAPI",value:function(e){var t=this;["configuration"].forEach((function(n){t[n]=e[n]})),this.destroy=function(){for(var n in Object.values(e.moduleInstances).forEach((function(e){f.isFunction(e.destroy)&&e.destroy(),e.listeners.removeAll()})),e=null,t)Object.prototype.hasOwnProperty.call(t,n)&&delete t[n];Object.setPrototypeOf(t,null)},Object.setPrototypeOf(this,e.moduleInstances.API.methods),delete this.exportAPI,Object.entries({blocks:{clear:"clear",render:"render"},caret:{focus:"focus"},events:{on:"on",off:"off",emit:"emit"},saver:{save:"save"}}).forEach((function(n){var o=(0,r.default)(n,2),i=o[0],a=o[1];Object.entries(a).forEach((function(n){var o=(0,r.default)(n,2),a=o[0],s=o[1];t[s]=e.moduleInstances.API.methods[i][a]}))}))}}],[{key:"version",get:function(){return"2.26.5"}}]),e}();o.default=h,h.displayName="EditorJS",e.exports=t.default})?o.apply(t,r):o)||(e.exports=i)},function(e,t){e.exports=function(e){if(Array.isArray(e))return e},e.exports.__esModule=!0,e.exports.default=e.exports},function(e,t){e.exports=function(e,t){var n=null==e?null:"undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null!=n){var o,r,i=[],a=!0,s=!1;try{for(n=n.call(e);!(a=(o=n.next()).done)&&(i.push(o.value),!t||i.length!==t);a=!0);}catch(e){s=!0,r=e}finally{try{a||null==n.return||n.return()}finally{if(s)throw r}}return i}},e.exports.__esModule=!0,e.exports.default=e.exports},function(e,t){e.exports=function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")},e.exports.__esModule=!0,e.exports.default=e.exports},function(e,t){function n(){}e.exports=Object.assign(n,{default:n,register:n,revert:function(){},__esModule:!0})},function(e,t,n){var o,r,i;"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self&&self,r=[],void 0===(i="function"==typeof(o=function(){"use strict";Element.prototype.matches||(Element.prototype.matches=Element.prototype.matchesSelector||Element.prototype.mozMatchesSelector||Element.prototype.msMatchesSelector||Element.prototype.oMatchesSelector||Element.prototype.webkitMatchesSelector||function(e){for(var t=(this.document||this.ownerDocument).querySelectorAll(e),n=t.length;--n>=0&&t.item(n)!==this;);return n>-1}),Element.prototype.closest||(Element.prototype.closest=function(e){var t=this;if(!document.documentElement.contains(t))return null;do{if(t.matches(e))return t;t=t.parentElement||t.parentNode}while(null!==t);return null}),Element.prototype.prepend||(Element.prototype.prepend=function(e){var t=document.createDocumentFragment();Array.isArray(e)||(e=[e]),e.forEach((function(e){var n=e instanceof Node;t.appendChild(n?e:document.createTextNode(e))})),this.insertBefore(t,this.firstChild)}),Element.prototype.scrollIntoViewIfNeeded||(Element.prototype.scrollIntoViewIfNeeded=function(e){e=0===arguments.length||!!e;var t=this.parentNode,n=window.getComputedStyle(t,null),o=parseInt(n.getPropertyValue("border-top-width")),r=parseInt(n.getPropertyValue("border-left-width")),i=this.offsetTop-t.offsetTopt.scrollTop+t.clientHeight,s=this.offsetLeft-t.offsetLeftt.scrollLeft+t.clientWidth,c=i&&!a;(i||a)&&e&&(t.scrollTop=this.offsetTop-t.offsetTop-t.clientHeight/2-o+this.clientHeight/2),(s||l)&&e&&(t.scrollLeft=this.offsetLeft-t.offsetLeft-t.clientWidth/2-r+this.clientWidth/2),(i||a||s||l)&&!e&&this.scrollIntoView(c)})})?o.apply(t,r):o)||(e.exports=i)},function(e,t,n){var o,r,i,a=n(7);"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self&&self,r=[t,n(20),n(21),n(2),n(3),n(19),n(8),n(54),n(151),n(82)],void 0===(i="function"==typeof(o=function(o,r,i,s,l,c,u,f,d,p){"use strict";var h=n(1);function v(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(v=function(e){return e?n:t})(e)}Object.defineProperty(o,"__esModule",{value:!0}),o.default=void 0,r=h(r),i=h(i),s=h(s),l=h(l),c=h(c),u=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!==a(e)&&"function"!=typeof e)return{default:e};var n=v(t);if(n&&n.has(e))return n.get(e);var o={},r=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var i in e)if("default"!==i&&Object.prototype.hasOwnProperty.call(e,i)){var s=r?Object.getOwnPropertyDescriptor(e,i):null;s&&(s.get||s.set)?Object.defineProperty(o,i,s):o[i]=e[i]}return o.default=e,n&&n.set(e,o),o}(u),f=h(f),p=h(p);var g=n(376),y=[];g.keys().forEach((function(e){e.match(/^\.\/[^_][\w/]*\.([tj])s$/)&&y.push(g(e))}));var k=function(){function e(t){var n,o,a=this;(0,s.default)(this,e),this.moduleInstances={},this.eventsDispatcher=new p.default,this.isReady=new Promise((function(e,t){n=e,o=t})),Promise.resolve().then((0,i.default)(r.default.mark((function e(){return r.default.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return a.configuration=t,e.next=3,a.validate();case 3:return e.next=5,a.init();case 5:return e.next=7,a.start();case 7:u.logLabeled("I'm ready! (ノ◕ヮ◕)ノ*:・゚✧","log","","color: #E24A75"),setTimeout((0,i.default)(r.default.mark((function e(){var t,o,i;return r.default.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,a.render();case 2:a.configuration.autofocus&&(t=a.moduleInstances,o=t.BlockManager,(i=t.Caret).setToBlock(o.blocks[0],i.positions.START),o.highlightCurrentNode()),a.moduleInstances.UI.removeLoader(),n();case 5:case"end":return e.stop()}}),e)}))),500);case 9:case"end":return e.stop()}}),e)})))).catch((function(e){u.log("Editor.js is not ready because of ".concat(e),"error"),o(e)}))}var t,n;return(0,l.default)(e,[{key:"configuration",get:function(){return this.config},set:function(e){var t,n;u.isObject(e)?this.config=Object.assign({},e):this.config={holder:e},u.deprecationAssert(!!this.config.holderId,"config.holderId","config.holder"),this.config.holderId&&!this.config.holder&&(this.config.holder=this.config.holderId,this.config.holderId=null),null==this.config.holder&&(this.config.holder="editorjs"),this.config.logLevel||(this.config.logLevel=u.LogLevels.VERBOSE),u.setLogLevel(this.config.logLevel),u.deprecationAssert(Boolean(this.config.initialBlock),"config.initialBlock","config.defaultBlock"),this.config.defaultBlock=this.config.defaultBlock||this.config.initialBlock||"paragraph",this.config.minHeight=void 0!==this.config.minHeight?this.config.minHeight:300;var o={type:this.config.defaultBlock,data:{}};this.config.placeholder=this.config.placeholder||!1,this.config.sanitizer=this.config.sanitizer||{p:!0,b:!0,a:!0},this.config.hideToolbar=!!this.config.hideToolbar&&this.config.hideToolbar,this.config.tools=this.config.tools||{},this.config.i18n=this.config.i18n||{},this.config.data=this.config.data||{blocks:[]},this.config.onReady=this.config.onReady||function(){},this.config.onChange=this.config.onChange||function(){},this.config.inlineToolbar=void 0===this.config.inlineToolbar||this.config.inlineToolbar,!u.isEmpty(this.config.data)&&this.config.data.blocks&&0!==this.config.data.blocks.length||(this.config.data={blocks:[o]}),this.config.readOnly=this.config.readOnly||!1,(null===(t=this.config.i18n)||void 0===t?void 0:t.messages)&&f.default.setDictionary(this.config.i18n.messages),this.config.i18n.direction=(null===(n=this.config.i18n)||void 0===n?void 0:n.direction)||"ltr"}},{key:"validate",value:(n=(0,i.default)(r.default.mark((function e(){var t,n,o;return r.default.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(t=this.config,n=t.holderId,o=t.holder,!n||!o){e.next=3;break}throw Error("«holderId» and «holder» param can't assign at the same time.");case 3:if(!u.isString(o)||c.default.get(o)){e.next=5;break}throw Error("element with ID «".concat(o,"» is missing. Pass correct holder's ID."));case 5:if(!o||!u.isObject(o)||c.default.isElement(o)){e.next=7;break}throw Error("«holder» value must be an Element node");case 7:case"end":return e.stop()}}),e,this)}))),function(){return n.apply(this,arguments)})},{key:"init",value:function(){this.constructModules(),this.configureModules()}},{key:"start",value:(t=(0,i.default)(r.default.mark((function e(){var t,n=this;return r.default.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=["Tools","UI","BlockManager","Paste","BlockSelection","RectangleSelection","CrossBlockSelection","ReadOnly"],e.next=3,t.reduce((function(e,t){return e.then((0,i.default)(r.default.mark((function e(){return r.default.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.prev=0,e.next=3,n.moduleInstances[t].prepare();case 3:e.next=10;break;case 5:if(e.prev=5,e.t0=e.catch(0),!(e.t0 instanceof d.CriticalError)){e.next=9;break}throw new Error(e.t0.message);case 9:u.log("Module ".concat(t," was skipped because of %o"),"warn",e.t0);case 10:case"end":return e.stop()}}),e,null,[[0,5]])}))))}),Promise.resolve());case 3:case"end":return e.stop()}}),e)}))),function(){return t.apply(this,arguments)})},{key:"render",value:function(){return this.moduleInstances.Renderer.render(this.config.data.blocks)}},{key:"constructModules",value:function(){var e=this;y.forEach((function(t){var n=u.isFunction(t)?t:t.default;try{e.moduleInstances[n.displayName]=new n({config:e.configuration,eventsDispatcher:e.eventsDispatcher})}catch(e){u.log("Module ".concat(n.displayName," skipped because"),"error",e)}}))}},{key:"configureModules",value:function(){for(var e in this.moduleInstances)Object.prototype.hasOwnProperty.call(this.moduleInstances,e)&&(this.moduleInstances[e].state=this.getModulesDiff(e))}},{key:"getModulesDiff",value:function(e){var t={};for(var n in this.moduleInstances)n!==e&&(t[n]=this.moduleInstances[n]);return t}}]),e}();o.default=k,k.displayName="Core",e.exports=t.default})?o.apply(t,r):o)||(e.exports=i)},function(e,t,n){var o=n(7).default;function r(){"use strict";e.exports=r=function(){return t},e.exports.__esModule=!0,e.exports.default=e.exports;var t={},n=Object.prototype,i=n.hasOwnProperty,a=Object.defineProperty||function(e,t,n){e[t]=n.value},s="function"==typeof Symbol?Symbol:{},l=s.iterator||"@@iterator",c=s.asyncIterator||"@@asyncIterator",u=s.toStringTag||"@@toStringTag";function f(e,t,n){return Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}),e[t]}try{f({},"")}catch(e){f=function(e,t,n){return e[t]=n}}function d(e,t,n,o){var r=t&&t.prototype instanceof v?t:v,i=Object.create(r.prototype),s=new M(o||[]);return a(i,"_invoke",{value:S(e,n,s)}),i}function p(e,t,n){try{return{type:"normal",arg:e.call(t,n)}}catch(e){return{type:"throw",arg:e}}}t.wrap=d;var h={};function v(){}function g(){}function y(){}var k={};f(k,l,(function(){return this}));var b=Object.getPrototypeOf,m=b&&b(b(_([])));m&&m!==n&&i.call(m,l)&&(k=m);var w=y.prototype=v.prototype=Object.create(k);function x(e){["next","throw","return"].forEach((function(t){f(e,t,(function(e){return this._invoke(t,e)}))}))}function C(e,t){var n;a(this,"_invoke",{value:function(r,a){function s(){return new t((function(n,s){!function n(r,a,s,l){var c=p(e[r],e,a);if("throw"!==c.type){var u=c.arg,f=u.value;return f&&"object"==o(f)&&i.call(f,"__await")?t.resolve(f.__await).then((function(e){n("next",e,s,l)}),(function(e){n("throw",e,s,l)})):t.resolve(f).then((function(e){u.value=e,s(u)}),(function(e){return n("throw",e,s,l)}))}l(c.arg)}(r,a,n,s)}))}return n=n?n.then(s,s):s()}})}function S(e,t,n){var o="suspendedStart";return function(r,i){if("executing"===o)throw new Error("Generator is already running");if("completed"===o){if("throw"===r)throw i;return O()}for(n.method=r,n.arg=i;;){var a=n.delegate;if(a){var s=T(a,n);if(s){if(s===h)continue;return s}}if("next"===n.method)n.sent=n._sent=n.arg;else if("throw"===n.method){if("suspendedStart"===o)throw o="completed",n.arg;n.dispatchException(n.arg)}else"return"===n.method&&n.abrupt("return",n.arg);o="executing";var l=p(e,t,n);if("normal"===l.type){if(o=n.done?"completed":"suspendedYield",l.arg===h)continue;return{value:l.arg,done:n.done}}"throw"===l.type&&(o="completed",n.method="throw",n.arg=l.arg)}}}function T(e,t){var n=e.iterator[t.method];if(void 0===n){if(t.delegate=null,"throw"===t.method){if(e.iterator.return&&(t.method="return",t.arg=void 0,T(e,t),"throw"===t.method))return h;t.method="throw",t.arg=new TypeError("The iterator does not provide a 'throw' method")}return h}var o=p(n,e.iterator,t.arg);if("throw"===o.type)return t.method="throw",t.arg=o.arg,t.delegate=null,h;var r=o.arg;return r?r.done?(t[e.resultName]=r.value,t.next=e.nextLoc,"return"!==t.method&&(t.method="next",t.arg=void 0),t.delegate=null,h):r:(t.method="throw",t.arg=new TypeError("iterator result is not an object"),t.delegate=null,h)}function E(e){var t={tryLoc:e[0]};1 in e&&(t.catchLoc=e[1]),2 in e&&(t.finallyLoc=e[2],t.afterLoc=e[3]),this.tryEntries.push(t)}function B(e){var t=e.completion||{};t.type="normal",delete t.arg,e.completion=t}function M(e){this.tryEntries=[{tryLoc:"root"}],e.forEach(E,this),this.reset(!0)}function _(e){if(e){var t=e[l];if(t)return t.call(e);if("function"==typeof e.next)return e;if(!isNaN(e.length)){var n=-1,o=function t(){for(;++n=0;--o){var r=this.tryEntries[o],a=r.completion;if("root"===r.tryLoc)return n("end");if(r.tryLoc<=this.prev){var s=i.call(r,"catchLoc"),l=i.call(r,"finallyLoc");if(s&&l){if(this.prev=0;--n){var o=this.tryEntries[n];if(o.tryLoc<=this.prev&&i.call(o,"finallyLoc")&&this.prev=0;--t){var n=this.tryEntries[t];if(n.finallyLoc===e)return this.complete(n.completion,n.afterLoc),B(n),h}},catch:function(e){for(var t=this.tryEntries.length-1;t>=0;--t){var n=this.tryEntries[t];if(n.tryLoc===e){var o=n.completion;if("throw"===o.type){var r=o.arg;B(n)}return r}}throw new Error("illegal catch attempt")},delegateYield:function(e,t,n){return this.delegate={iterator:_(e),resultName:t,nextLoc:n},"next"===this.method&&(this.arg=void 0),h}},t}e.exports=r,e.exports.__esModule=!0,e.exports.default=e.exports},function(e,t,n){var o=n(149);e.exports=function(e){if(Array.isArray(e))return o(e)},e.exports.__esModule=!0,e.exports.default=e.exports},function(e,t){e.exports=function(e){if("undefined"!=typeof Symbol&&null!=e[Symbol.iterator]||null!=e["@@iterator"])return Array.from(e)},e.exports.__esModule=!0,e.exports.default=e.exports},function(e,t){e.exports=function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")},e.exports.__esModule=!0,e.exports.default=e.exports},function(e,t){e.exports=function(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e},e.exports.__esModule=!0,e.exports.default=e.exports},function(e,t){e.exports=function(e){return-1!==Function.toString.call(e).indexOf("[native code]")},e.exports.__esModule=!0,e.exports.default=e.exports},function(e,t,n){var o=n(111),r=n(375);function i(t,n,a){return r()?(e.exports=i=Reflect.construct.bind(),e.exports.__esModule=!0,e.exports.default=e.exports):(e.exports=i=function(e,t,n){var r=[null];r.push.apply(r,t);var i=new(Function.bind.apply(e,r));return n&&o(i,n.prototype),i},e.exports.__esModule=!0,e.exports.default=e.exports),i.apply(null,arguments)}e.exports=i,e.exports.__esModule=!0,e.exports.default=e.exports},function(e,t){e.exports=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}},e.exports.__esModule=!0,e.exports.default=e.exports},function(e,t,n){var o={"./api":83,"./api/":83,"./api/blocks":154,"./api/blocks.ts":154,"./api/caret":155,"./api/caret.ts":155,"./api/events":156,"./api/events.ts":156,"./api/i18n":157,"./api/i18n.ts":157,"./api/index":83,"./api/index.ts":83,"./api/inlineToolbar":158,"./api/inlineToolbar.ts":158,"./api/listeners":159,"./api/listeners.ts":159,"./api/notifier":160,"./api/notifier.ts":160,"./api/readonly":161,"./api/readonly.ts":161,"./api/sanitizer":162,"./api/sanitizer.ts":162,"./api/saver":163,"./api/saver.ts":163,"./api/selection":164,"./api/selection.ts":164,"./api/styles":165,"./api/styles.ts":165,"./api/toolbar":166,"./api/toolbar.ts":166,"./api/tooltip":167,"./api/tooltip.ts":167,"./api/ui":168,"./api/ui.ts":168,"./blockEvents":169,"./blockEvents.ts":169,"./blockManager":170,"./blockManager.ts":170,"./blockSelection":171,"./blockSelection.ts":171,"./caret":172,"./caret.ts":172,"./crossBlockSelection":173,"./crossBlockSelection.ts":173,"./dragNDrop":174,"./dragNDrop.ts":174,"./modificationsObserver":175,"./modificationsObserver.ts":175,"./paste":176,"./paste.ts":176,"./readonly":177,"./readonly.ts":177,"./rectangleSelection":178,"./rectangleSelection.ts":178,"./renderer":179,"./renderer.ts":179,"./saver":180,"./saver.ts":180,"./toolbar":84,"./toolbar/":84,"./toolbar/blockSettings":181,"./toolbar/blockSettings.ts":181,"./toolbar/conversion":182,"./toolbar/conversion.ts":182,"./toolbar/index":84,"./toolbar/index.ts":84,"./toolbar/inline":183,"./toolbar/inline.ts":183,"./tools":184,"./tools.ts":184,"./ui":186,"./ui.ts":186};function r(e){var t=i(e);return n(t)}function i(e){if(!n.o(o,e)){var t=new Error("Cannot find module '"+e+"'");throw t.code="MODULE_NOT_FOUND",t}return o[e]}r.keys=function(){return Object.keys(o)},r.resolve=i,e.exports=r,r.id=376},function(e,t,n){var o=n(4);e.exports=function(e,t){for(;!Object.prototype.hasOwnProperty.call(e,t)&&null!==(e=o(e)););return e},e.exports.__esModule=!0,e.exports.default=e.exports},function(e,t,n){var o,r,i;"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self&&self,r=[t,n(2),n(3),n(379)],void 0===(i="function"==typeof(o=function(o,r,i,a){"use strict";var s=n(1);Object.defineProperty(o,"__esModule",{value:!0}),o.default=void 0,r=s(r),i=s(i),a=s(a);var l=function(){function e(){(0,r.default)(this,e)}return(0,i.default)(e,[{key:"show",value:function(e){a.default.show(e)}}]),e}();o.default=l,l.displayName="Notifier",e.exports=t.default})?o.apply(t,r):o)||(e.exports=i)},function(e,t,n){window,e.exports=function(e){var t={};function n(o){if(t[o])return t[o].exports;var r=t[o]={i:o,l:!1,exports:{}};return e[o].call(r.exports,r,r.exports,n),r.l=!0,r.exports}return n.m=e,n.c=t,n.d=function(e,t,o){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:o})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var o=Object.create(null);if(n.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)n.d(o,r,function(t){return e[t]}.bind(null,r));return o},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/",n(n.s=0)}([function(e,t,n){"use strict";n(1),e.exports=function(){var e=n(6),t=null;return{show:function(n){if(n.message){!function(){if(t)return!0;t=e.getWrapper(),document.body.appendChild(t)}();var o=null,r=n.time||8e3;switch(n.type){case"confirm":o=e.confirm(n);break;case"prompt":o=e.prompt(n);break;default:o=e.alert(n),window.setTimeout((function(){o.remove()}),r)}t.appendChild(o),o.classList.add("cdx-notify--bounce-in")}}}}()},function(e,t,n){var o=n(2);"string"==typeof o&&(o=[[e.i,o,""]]),n(4)(o,{hmr:!0,transform:void 0,insertInto:void 0}),o.locals&&(e.exports=o.locals)},function(e,t,n){(e.exports=n(3)(!1)).push([e.i,'.cdx-notify--error{background:#fffbfb!important}.cdx-notify--error::before{background:#fb5d5d!important}.cdx-notify__input{max-width:130px;padding:5px 10px;background:#f7f7f7;border:0;border-radius:3px;font-size:13px;color:#656b7c;outline:0}.cdx-notify__input:-ms-input-placeholder{color:#656b7c}.cdx-notify__input::placeholder{color:#656b7c}.cdx-notify__input:focus:-ms-input-placeholder{color:rgba(101,107,124,.3)}.cdx-notify__input:focus::placeholder{color:rgba(101,107,124,.3)}.cdx-notify__button{border:none;border-radius:3px;font-size:13px;padding:5px 10px;cursor:pointer}.cdx-notify__button:last-child{margin-left:10px}.cdx-notify__button--cancel{background:#f2f5f7;box-shadow:0 2px 1px 0 rgba(16,19,29,0);color:#656b7c}.cdx-notify__button--cancel:hover{background:#eee}.cdx-notify__button--confirm{background:#34c992;box-shadow:0 1px 1px 0 rgba(18,49,35,.05);color:#fff}.cdx-notify__button--confirm:hover{background:#33b082}.cdx-notify__btns-wrapper{display:-ms-flexbox;display:flex;-ms-flex-flow:row nowrap;flex-flow:row nowrap;margin-top:5px}.cdx-notify__cross{position:absolute;top:5px;right:5px;width:10px;height:10px;padding:5px;opacity:.54;cursor:pointer}.cdx-notify__cross::after,.cdx-notify__cross::before{content:\'\';position:absolute;left:9px;top:5px;height:12px;width:2px;background:#575d67}.cdx-notify__cross::before{transform:rotate(-45deg)}.cdx-notify__cross::after{transform:rotate(45deg)}.cdx-notify__cross:hover{opacity:1}.cdx-notifies{position:fixed;z-index:2;bottom:20px;left:20px;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,"Fira Sans","Droid Sans","Helvetica Neue",sans-serif}.cdx-notify{position:relative;width:220px;margin-top:15px;padding:13px 16px;background:#fff;box-shadow:0 11px 17px 0 rgba(23,32,61,.13);border-radius:5px;font-size:14px;line-height:1.4em;word-wrap:break-word}.cdx-notify::before{content:\'\';position:absolute;display:block;top:0;left:0;width:3px;height:calc(100% - 6px);margin:3px;border-radius:5px;background:0 0}@keyframes bounceIn{0%{opacity:0;transform:scale(.3)}50%{opacity:1;transform:scale(1.05)}70%{transform:scale(.9)}100%{transform:scale(1)}}.cdx-notify--bounce-in{animation-name:bounceIn;animation-duration:.6s;animation-iteration-count:1}.cdx-notify--success{background:#fafffe!important}.cdx-notify--success::before{background:#41ffb1!important}',""])},function(e,t){e.exports=function(e){var t=[];return t.toString=function(){return this.map((function(t){var n=function(e,t){var n,o=e[1]||"",r=e[3];if(!r)return o;if(t&&"function"==typeof btoa){var i=(n=r,"/*# sourceMappingURL=data:application/json;charset=utf-8;base64,"+btoa(unescape(encodeURIComponent(JSON.stringify(n))))+" */"),a=r.sources.map((function(e){return"/*# sourceURL="+r.sourceRoot+e+" */"}));return[o].concat(a).concat([i]).join("\n")}return[o].join("\n")}(t,e);return t[2]?"@media "+t[2]+"{"+n+"}":n})).join("")},t.i=function(e,n){"string"==typeof e&&(e=[[null,e,""]]);for(var o={},r=0;r=0&&u.splice(t,1)}function g(e){var t=document.createElement("style");return void 0===e.attrs.type&&(e.attrs.type="text/css"),y(t,e.attrs),h(e,t),t}function y(e,t){Object.keys(t).forEach((function(n){e.setAttribute(n,t[n])}))}function k(e,t){var n,o,r,i;if(t.transform&&e.css){if(!(i=t.transform(e.css)))return function(){};e.css=i}if(t.singleton){var a=c++;n=l||(l=g(t)),o=w.bind(null,n,a,!1),r=w.bind(null,n,a,!0)}else e.sourceMap&&"function"==typeof URL&&"function"==typeof URL.createObjectURL&&"function"==typeof URL.revokeObjectURL&&"function"==typeof Blob&&"function"==typeof btoa?(n=function(e){var t=document.createElement("link");return void 0===e.attrs.type&&(e.attrs.type="text/css"),e.attrs.rel="stylesheet",y(t,e.attrs),h(e,t),t}(t),o=function(e,t,n){var o=n.css,r=n.sourceMap,i=void 0===t.convertToAbsoluteUrls&&r;(t.convertToAbsoluteUrls||i)&&(o=f(o)),r&&(o+="\n/*# sourceMappingURL=data:application/json;base64,"+btoa(unescape(encodeURIComponent(JSON.stringify(r))))+" */");var a=new Blob([o],{type:"text/css"}),s=e.href;e.href=URL.createObjectURL(a),s&&URL.revokeObjectURL(s)}.bind(null,n,t),r=function(){v(n),n.href&&URL.revokeObjectURL(n.href)}):(n=g(t),o=function(e,t){var n=t.css,o=t.media;if(o&&e.setAttribute("media",o),e.styleSheet)e.styleSheet.cssText=n;else{for(;e.firstChild;)e.removeChild(e.firstChild);e.appendChild(document.createTextNode(n))}}.bind(null,n),r=function(){v(n)});return o(e),function(t){if(t){if(t.css===e.css&&t.media===e.media&&t.sourceMap===e.sourceMap)return;o(e=t)}else r()}}e.exports=function(e,t){if("undefined"!=typeof DEBUG&&DEBUG&&"object"!=typeof document)throw new Error("The style-loader cannot be used in a non-browser environment");(t=t||{}).attrs="object"==typeof t.attrs?t.attrs:{},t.singleton||"boolean"==typeof t.singleton||(t.singleton=a()),t.insertInto||(t.insertInto="head"),t.insertAt||(t.insertAt="bottom");var n=p(e,t);return d(n,t),function(e){for(var o=[],r=0;r0;)t.insertBefore(l.childNodes[0],l);t.removeChild(l),this._sanitize(e,t);break}for(var v=0;v{this.showed&&this.hide(!0)},this.loadStyles(),this.prepare(),window.addEventListener("scroll",this.handleWindowScroll,{passive:!0})}get CSS(){return{tooltip:"ct",tooltipContent:"ct__content",tooltipShown:"ct--shown",placement:{left:"ct--left",bottom:"ct--bottom",right:"ct--right",top:"ct--top"}}}show(e,t,n){this.nodes.wrapper||this.prepare(),this.hidingTimeout&&clearTimeout(this.hidingTimeout);const o=Object.assign({placement:"bottom",marginTop:0,marginLeft:0,marginRight:0,marginBottom:0,delay:70,hidingDelay:0},n);if(o.hidingDelay&&(this.hidingDelay=o.hidingDelay),this.nodes.content.innerHTML="","string"==typeof t)this.nodes.content.appendChild(document.createTextNode(t));else{if(!(t instanceof Node))throw Error("[CodeX Tooltip] Wrong type of «content» passed. It should be an instance of Node or String. But "+typeof t+" given.");this.nodes.content.appendChild(t)}switch(this.nodes.wrapper.classList.remove(...Object.values(this.CSS.placement)),o.placement){case"top":this.placeTop(e,o);break;case"left":this.placeLeft(e,o);break;case"right":this.placeRight(e,o);break;case"bottom":default:this.placeBottom(e,o)}o&&o.delay?this.showingTimeout=setTimeout(()=>{this.nodes.wrapper.classList.add(this.CSS.tooltipShown),this.showed=!0},o.delay):(this.nodes.wrapper.classList.add(this.CSS.tooltipShown),this.showed=!0)}hide(e=!1){if(this.hidingDelay&&!e)return this.hidingTimeout&&clearTimeout(this.hidingTimeout),void(this.hidingTimeout=setTimeout(()=>{this.hide(!0)},this.hidingDelay));this.nodes.wrapper.classList.remove(this.CSS.tooltipShown),this.showed=!1,this.showingTimeout&&clearTimeout(this.showingTimeout)}onHover(e,t,n){e.addEventListener("mouseenter",()=>{this.show(e,t,n)}),e.addEventListener("mouseleave",()=>{this.hide()})}destroy(){this.nodes.wrapper.remove(),window.removeEventListener("scroll",this.handleWindowScroll)}prepare(){this.nodes.wrapper=this.make("div",this.CSS.tooltip),this.nodes.content=this.make("div",this.CSS.tooltipContent),this.append(this.nodes.wrapper,this.nodes.content),this.append(document.body,this.nodes.wrapper)}loadStyles(){const e="codex-tooltips-style";if(document.getElementById(e))return;const t=n(2),o=this.make("style",null,{textContent:t.toString(),id:e});this.prepend(document.head,o)}placeBottom(e,t){const n=e.getBoundingClientRect(),o=n.left+e.clientWidth/2-this.nodes.wrapper.offsetWidth/2,r=n.bottom+window.pageYOffset+this.offsetTop+t.marginTop;this.applyPlacement("bottom",o,r)}placeTop(e,t){const n=e.getBoundingClientRect(),o=n.left+e.clientWidth/2-this.nodes.wrapper.offsetWidth/2,r=n.top+window.pageYOffset-this.nodes.wrapper.clientHeight-this.offsetTop;this.applyPlacement("top",o,r)}placeLeft(e,t){const n=e.getBoundingClientRect(),o=n.left-this.nodes.wrapper.offsetWidth-this.offsetLeft-t.marginLeft,r=n.top+window.pageYOffset+e.clientHeight/2-this.nodes.wrapper.offsetHeight/2;this.applyPlacement("left",o,r)}placeRight(e,t){const n=e.getBoundingClientRect(),o=n.right+this.offsetRight+t.marginRight,r=n.top+window.pageYOffset+e.clientHeight/2-this.nodes.wrapper.offsetHeight/2;this.applyPlacement("right",o,r)}applyPlacement(e,t,n){this.nodes.wrapper.classList.add(this.CSS.placement[e]),this.nodes.wrapper.style.left=t+"px",this.nodes.wrapper.style.top=n+"px"}make(e,t=null,n={}){const o=document.createElement(e);Array.isArray(t)?o.classList.add(...t):t&&o.classList.add(t);for(const e in n)n.hasOwnProperty(e)&&(o[e]=n[e]);return o}append(e,t){Array.isArray(t)?t.forEach(t=>e.appendChild(t)):e.appendChild(t)}prepend(e,t){Array.isArray(t)?(t=t.reverse()).forEach(t=>e.prepend(t)):e.prepend(t)}}},function(e,t){e.exports='.ct{z-index:999;opacity:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;pointer-events:none;-webkit-transition:opacity 50ms ease-in,-webkit-transform 70ms cubic-bezier(.215,.61,.355,1);transition:opacity 50ms ease-in,-webkit-transform 70ms cubic-bezier(.215,.61,.355,1);transition:opacity 50ms ease-in,transform 70ms cubic-bezier(.215,.61,.355,1);transition:opacity 50ms ease-in,transform 70ms cubic-bezier(.215,.61,.355,1),-webkit-transform 70ms cubic-bezier(.215,.61,.355,1);will-change:opacity,top,left;-webkit-box-shadow:0 8px 12px 0 rgba(29,32,43,.17),0 4px 5px -3px rgba(5,6,12,.49);box-shadow:0 8px 12px 0 rgba(29,32,43,.17),0 4px 5px -3px rgba(5,6,12,.49);border-radius:9px}.ct,.ct:before{position:absolute;top:0;left:0}.ct:before{content:"";bottom:0;right:0;background-color:#1d202b;z-index:-1;border-radius:4px}@supports(-webkit-mask-box-image:url("")){.ct:before{border-radius:0;-webkit-mask-box-image:url(\'data:image/svg+xml;charset=utf-8,\') 48% 41% 37.9% 53.3%}}@media (--mobile){.ct{display:none}}.ct__content{padding:6px 10px;color:#cdd1e0;font-size:12px;text-align:center;letter-spacing:.02em;line-height:1em}.ct:after{content:"";width:8px;height:8px;position:absolute;background-color:#1d202b;z-index:-1}.ct--bottom{-webkit-transform:translateY(5px);transform:translateY(5px)}.ct--bottom:after{top:-3px;left:50%;-webkit-transform:translateX(-50%) rotate(-45deg);transform:translateX(-50%) rotate(-45deg)}.ct--top{-webkit-transform:translateY(-5px);transform:translateY(-5px)}.ct--top:after{top:auto;bottom:-3px;left:50%;-webkit-transform:translateX(-50%) rotate(-45deg);transform:translateX(-50%) rotate(-45deg)}.ct--left{-webkit-transform:translateX(-5px);transform:translateX(-5px)}.ct--left:after{top:50%;left:auto;right:0;-webkit-transform:translate(41.6%,-50%) rotate(-45deg);transform:translate(41.6%,-50%) rotate(-45deg)}.ct--right{-webkit-transform:translateX(5px);transform:translateX(5px)}.ct--right:after{top:50%;left:0;-webkit-transform:translate(-41.6%,-50%) rotate(-45deg);transform:translate(-41.6%,-50%) rotate(-45deg)}.ct--shown{opacity:1;-webkit-transform:none;transform:none}'}]).default},function(e,t,n){var o,r,i,a=n(7);"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self&&self,r=[t,n(2),n(3),n(19),n(8),n(25)],void 0===(i="function"==typeof(o=function(o,r,i,s,l,c){"use strict";var u=n(1);function f(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(f=function(e){return e?n:t})(e)}Object.defineProperty(o,"__esModule",{value:!0}),o.default=void 0,r=u(r),i=u(i),s=u(s),l=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!==a(e)&&"function"!=typeof e)return{default:e};var n=f(t);if(n&&n.has(e))return n.get(e);var o={},r=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var i in e)if("default"!==i&&Object.prototype.hasOwnProperty.call(e,i)){var s=r?Object.getOwnPropertyDescriptor(e,i):null;s&&(s.get||s.set)?Object.defineProperty(o,i,s):o[i]=e[i]}return o.default=e,n&&n.set(e,o),o}(l),c=u(c);var d=function(){function e(t,n){(0,r.default)(this,e),this.cursor=-1,this.items=[],this.items=t||[],this.focusedCssClass=n}return(0,i.default)(e,[{key:"currentItem",get:function(){return-1===this.cursor?null:this.items[this.cursor]}},{key:"setCursor",value:function(e){e=-1&&(this.dropCursor(),this.cursor=e,this.items[this.cursor].classList.add(this.focusedCssClass))}},{key:"setItems",value:function(e){this.items=e}},{key:"next",value:function(){this.cursor=this.leafNodesAndReturnIndex(e.directions.RIGHT)}},{key:"previous",value:function(){this.cursor=this.leafNodesAndReturnIndex(e.directions.LEFT)}},{key:"dropCursor",value:function(){-1!==this.cursor&&(this.items[this.cursor].classList.remove(this.focusedCssClass),this.cursor=-1)}},{key:"leafNodesAndReturnIndex",value:function(t){var n=this;if(0===this.items.length)return this.cursor;var o=this.cursor;return-1===o?o=t===e.directions.RIGHT?-1:0:this.items[o].classList.remove(this.focusedCssClass),o=t===e.directions.RIGHT?(o+1)%this.items.length:(this.items.length+o-1)%this.items.length,s.default.canSetCaret(this.items[o])&&l.delay((function(){return c.default.setCursor(n.items[o])}),50)(),this.items[o].classList.add(this.focusedCssClass),o}}]),e}();o.default=d,d.displayName="DomIterator",d.directions={RIGHT:"right",LEFT:"left"},e.exports=t.default})?o.apply(t,r):o)||(e.exports=i)},function(e,t,n){var o,r,i,a=n(7);"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self&&self,r=[t,n(2),n(3),n(8),n(19),n(61)],void 0===(i="function"==typeof(o=function(o,r,i,s,l,c){"use strict";var u=n(1);function f(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(f=function(e){return e?n:t})(e)}Object.defineProperty(o,"__esModule",{value:!0}),o.default=void 0,r=u(r),i=u(i),s=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!==a(e)&&"function"!=typeof e)return{default:e};var n=f(t);if(n&&n.has(e))return n.get(e);var o={},r=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var i in e)if("default"!==i&&Object.prototype.hasOwnProperty.call(e,i)){var s=r?Object.getOwnPropertyDescriptor(e,i):null;s&&(s.get||s.set)?Object.defineProperty(o,i,s):o[i]=e[i]}return o.default=e,n&&n.set(e,o),o}(s),l=u(l);var d=function(){function e(t){(0,r.default)(this,e),this.blocks=[],this.workingArea=t}return(0,i.default)(e,[{key:"length",get:function(){return this.blocks.length}},{key:"array",get:function(){return this.blocks}},{key:"nodes",get:function(){return s.array(this.workingArea.children)}},{key:"push",value:function(e){this.blocks.push(e),this.insertToDOM(e)}},{key:"swap",value:function(e,t){var n=this.blocks[t];l.default.swap(this.blocks[e].holder,n.holder),this.blocks[t]=this.blocks[e],this.blocks[e]=n}},{key:"move",value:function(e,t){var n=this.blocks.splice(t,1)[0],o=e-1,r=Math.max(0,o),i=this.blocks[r];e>0?this.insertToDOM(n,"afterend",i):this.insertToDOM(n,"beforebegin",i),this.blocks.splice(e,0,n);var a=this.composeBlockEvent("move",{fromIndex:t,toIndex:e});n.call(c.BlockToolAPI.MOVED,a)}},{key:"insert",value:function(e,t){var n=arguments.length>2&&void 0!==arguments[2]&&arguments[2];if(this.length){e>this.length&&(e=this.length),n&&(this.blocks[e].holder.remove(),this.blocks[e].call(c.BlockToolAPI.REMOVED));var o=n?1:0;if(this.blocks.splice(e,o,t),e>0){var r=this.blocks[e-1];this.insertToDOM(t,"afterend",r)}else{var i=this.blocks[e+1];i?this.insertToDOM(t,"beforebegin",i):this.insertToDOM(t)}}else this.push(t)}},{key:"remove",value:function(e){isNaN(e)&&(e=this.length-1),this.blocks[e].holder.remove(),this.blocks[e].call(c.BlockToolAPI.REMOVED),this.blocks.splice(e,1)}},{key:"removeAll",value:function(){this.workingArea.innerHTML="",this.blocks.forEach((function(e){return e.call(c.BlockToolAPI.REMOVED)})),this.blocks.length=0}},{key:"insertAfter",value:function(e,t){var n=this.blocks.indexOf(e);this.insert(n+1,t)}},{key:"get",value:function(e){return this.blocks[e]}},{key:"indexOf",value:function(e){return this.blocks.indexOf(e)}},{key:"insertToDOM",value:function(e,t,n){t?n.holder.insertAdjacentElement(t,e.holder):this.workingArea.appendChild(e.holder),e.call(c.BlockToolAPI.RENDERED)}},{key:"composeBlockEvent",value:function(e,t){return new CustomEvent(e,{detail:t})}}],[{key:"set",value:function(e,t,n){return isNaN(Number(t))?(Reflect.set(e,t,n),!0):(e.insert(+t,n),!0)}},{key:"get",value:function(e,t){return isNaN(Number(t))?Reflect.get(e,t):e.get(+t)}}]),e}();o.default=d,d.displayName="Blocks",e.exports=t.default})?o.apply(t,r):o)||(e.exports=i)},function(e,t,n){var o,r,i;"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self&&self,r=[t],void 0===(i="function"==typeof(o=function(e){"use strict";var t;Object.defineProperty(e,"__esModule",{value:!0}),e.BlockMutationType=void 0,e.BlockMutationType=t,function(e){e.Added="block-added",e.Removed="block-removed",e.Moved="block-moved",e.Changed="block-changed"}(t||(e.BlockMutationType=t={}))})?o.apply(t,r):o)||(e.exports=i)},function(e,t,n){window,e.exports=function(e){var t={};function n(o){if(t[o])return t[o].exports;var r=t[o]={i:o,l:!1,exports:{}};return e[o].call(r.exports,r,r.exports,n),r.l=!0,r.exports}return n.m=e,n.c=t,n.d=function(e,t,o){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:o})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var o=Object.create(null);if(n.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)n.d(o,r,function(t){return e[t]}.bind(null,r));return o},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=0)}([function(e,t,n){"use strict";function o(e,t){for(var n=0;n=0;s--)(r=e[s])&&(a=(i<3?r(a):i>3?r(t,n,a):r(t,n))||a);return i>3&&a&&Object.defineProperty(t,n,a),a};e.ToolboxEvent=C,function(e){e.Opened="toolbox-opened",e.Closed="toolbox-closed",e.BlockAdded="toolbox-block-added"}(C||(e.ToolboxEvent=C={}));var T=function(e){(0,l.default)(c,e);var n,a=x(c);function c(e){var t,n=e.api,o=e.tools,i=e.i18nLabels;return(0,r.default)(this,c),(t=a.call(this)).opened=!1,t.nodes={toolbox:null},t.onOverlayClicked=function(){t.close()},t.api=n,t.tools=o,t.i18nLabels=i,t}return(0,i.default)(c,[{key:"isEmpty",get:function(){return 0===this.toolsToBeDisplayed.length}},{key:"make",value:function(){return this.popover=new g.default({scopeElement:this.api.ui.nodes.redactor,className:c.CSS.toolbox,searchable:!0,filterLabel:this.i18nLabels.filter,nothingFoundLabel:this.i18nLabels.nothingFound,items:this.toolboxItemsToBeDisplayed}),this.popover.on(g.PopoverEvent.OverlayClicked,this.onOverlayClicked),this.enableShortcuts(),this.nodes.toolbox=this.popover.getElement(),this.nodes.toolbox}},{key:"hasFocus",value:function(){var e;return null===(e=this.popover)||void 0===e?void 0:e.hasFocus()}},{key:"destroy",value:function(){var e;(0,s.default)((0,u.default)(c.prototype),"destroy",this).call(this),this.nodes&&this.nodes.toolbox&&(this.nodes.toolbox.remove(),this.nodes.toolbox=null),this.removeAllShortcuts(),null===(e=this.popover)||void 0===e||e.off(g.PopoverEvent.OverlayClicked,this.onOverlayClicked)}},{key:"toolButtonActivated",value:function(e,t){this.insertNewBlock(e,t)}},{key:"open",value:function(){var e;this.isEmpty||(null===(e=this.popover)||void 0===e||e.show(),this.opened=!0,this.emit(C.Opened))}},{key:"close",value:function(){var e;null===(e=this.popover)||void 0===e||e.hide(),this.opened=!1,this.emit(C.Closed)}},{key:"toggle",value:function(){this.opened?this.close():this.open()}},{key:"toolsToBeDisplayed",get:function(){var e=[];return this.tools.forEach((function(t){t.toolbox&&e.push(t)})),e}},{key:"toolboxItemsToBeDisplayed",get:function(){var e=this,t=function(t,n){return{icon:t.icon,title:y.default.t(k.I18nInternalNS.toolNames,t.title||d.capitalize(n.name)),name:n.name,onActivate:function(){e.toolButtonActivated(n.name,t.data)},secondaryLabel:n.shortcut?d.beautifyShortcut(n.shortcut):""}};return this.toolsToBeDisplayed.reduce((function(e,n){return Array.isArray(n.toolbox)?n.toolbox.forEach((function(o){e.push(t(o,n))})):void 0!==n.toolbox&&e.push(t(n.toolbox,n)),e}),[])}},{key:"enableShortcuts",value:function(){var e=this;this.toolsToBeDisplayed.forEach((function(t){var n=t.shortcut;n&&e.enableShortcutForTool(t.name,n)}))}},{key:"enableShortcutForTool",value:function(e,t){var n=this;h.default.add({name:t,on:this.api.ui.nodes.redactor,handler:function(t){t.preventDefault(),n.insertNewBlock(e)}})}},{key:"removeAllShortcuts",value:function(){var e=this;this.toolsToBeDisplayed.forEach((function(t){var n=t.shortcut;n&&h.default.remove(e.api.ui.nodes.redactor,n)}))}},{key:"insertNewBlock",value:(n=(0,o.default)(t.default.mark((function e(n,o){var r,i,a,s,l,c;return t.default.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(r=this.api.blocks.getCurrentBlockIndex(),i=this.api.blocks.getBlockByIndex(r)){e.next=4;break}return e.abrupt("return");case 4:if(a=i.isEmpty?r:r+1,!o){e.next=10;break}return e.next=8,this.api.blocks.composeBlockData(n);case 8:l=e.sent,s=Object.assign(l,o);case 10:(c=this.api.blocks.insert(n,s,void 0,a,void 0,i.isEmpty)).call(p.BlockToolAPI.APPEND_CALLBACK),this.api.caret.setToBlock(a),this.emit(C.BlockAdded,{block:c}),this.api.toolbar.close();case 15:case"end":return e.stop()}}),e,this)}))),function(e,t){return n.apply(this,arguments)})}],[{key:"CSS",get:function(){return{toolbox:"ce-toolbox"}}}]),c}(v.default);e.default=T,T.displayName="Toolbox",S([d.cacheable],T.prototype,"toolsToBeDisplayed",null),S([d.cacheable],T.prototype,"toolboxItemsToBeDisplayed",null)})?o.apply(t,r):o)||(e.exports=i)},function(e,t,n){var o,r,i;"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self&&self,r=[t,n(2),n(3),n(19),n(112),n(37)],void 0===(i="function"==typeof(o=function(o,r,i,a,s,l){"use strict";var c=n(1);Object.defineProperty(o,"__esModule",{value:!0}),o.default=void 0,r=c(r),i=c(i),a=c(a),s=c(s);var u=function(){function e(t){var n=t.items,o=t.onSearch,i=t.placeholder;(0,r.default)(this,e),this.listeners=new s.default,this.items=n,this.onSearch=o,this.render(i)}return(0,i.default)(e,[{key:"getElement",value:function(){return this.wrapper}},{key:"focus",value:function(){this.input.focus()}},{key:"clear",value:function(){this.input.value="",this.searchQuery="",this.onSearch(this.foundItems)}},{key:"destroy",value:function(){this.listeners.removeAll()}},{key:"render",value:function(t){var n=this;this.wrapper=a.default.make("div",e.CSS.wrapper);var o=a.default.make("div",e.CSS.icon,{innerHTML:l.IconSearch});this.input=a.default.make("input",e.CSS.input,{placeholder:t}),this.wrapper.appendChild(o),this.wrapper.appendChild(this.input),this.listeners.on(this.input,"input",(function(){n.searchQuery=n.input.value,n.onSearch(n.foundItems)}))}},{key:"foundItems",get:function(){var e=this;return this.items.filter((function(t){return e.checkItem(t)}))}},{key:"checkItem",value:function(e){var t,n=(null===(t=e.title)||void 0===t?void 0:t.toLowerCase())||"",o=this.searchQuery.toLowerCase();return n.includes(o)}}],[{key:"CSS",get:function(){return{wrapper:"cdx-search-field",icon:"cdx-search-field__icon",input:"cdx-search-field__input"}}}]),e}();o.default=u,u.displayName="SearchInput",e.exports=t.default})?o.apply(t,r):o)||(e.exports=i)},function(e,t,n){var o,r,i;"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self&&self,r=[t,n(2),n(3),n(8)],void 0===(i="function"==typeof(o=function(o,r,i,a){"use strict";var s=n(1);Object.defineProperty(o,"__esModule",{value:!0}),o.default=void 0,r=s(r),i=s(i);var l=function(){function e(){(0,r.default)(this,e)}return(0,i.default)(e,[{key:"lock",value:function(){a.isIosDevice?this.lockHard():document.body.classList.add(e.CSS.scrollLocked)}},{key:"unlock",value:function(){a.isIosDevice?this.unlockHard():document.body.classList.remove(e.CSS.scrollLocked)}},{key:"lockHard",value:function(){this.scrollPosition=window.pageYOffset,document.documentElement.style.setProperty("--window-scroll-offset","".concat(this.scrollPosition,"px")),document.body.classList.add(e.CSS.scrollLockedHard)}},{key:"unlockHard",value:function(){document.body.classList.remove(e.CSS.scrollLockedHard),null!==this.scrollPosition&&window.scrollTo(0,this.scrollPosition),this.scrollPosition=null}}]),e}();o.default=l,l.displayName="ScrollLocker",l.CSS={scrollLocked:"ce-scroll-locked",scrollLockedHard:"ce-scroll-locked--hard"},e.exports=t.default})?o.apply(t,r):o)||(e.exports=i)},function(e,t,n){var o,r,i;"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self&&self,r=[t],void 0===(i="function"==typeof(o=function(e){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.resolveAliases=function(e,t){var n={};return Object.keys(e).forEach((function(o){var r=t[o];void 0!==r?n[r]=e[o]:n[o]=e[o]})),n}})?o.apply(t,r):o)||(e.exports=i)},function(e,t,n){window,e.exports=function(e){var t={};function n(o){if(t[o])return t[o].exports;var r=t[o]={i:o,l:!1,exports:{}};return e[o].call(r.exports,r,r.exports,n),r.l=!0,r.exports}return n.m=e,n.c=t,n.d=function(e,t,o){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:o})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var o=Object.create(null);if(n.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)n.d(o,r,function(t){return e[t]}.bind(null,r));return o},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/",n(n.s=4)}([function(e,t,n){var o=n(1),r=n(2);"string"==typeof(r=r.__esModule?r.default:r)&&(r=[[e.i,r,""]]),o(r,{insert:"head",singleton:!1}),e.exports=r.locals||{}},function(e,t,n){"use strict";var o,r=function(){var e={};return function(t){if(void 0===e[t]){var n=document.querySelector(t);if(window.HTMLIFrameElement&&n instanceof window.HTMLIFrameElement)try{n=n.contentDocument.head}catch(e){n=null}e[t]=n}return e[t]}}(),i=[];function a(e){for(var t=-1,n=0;n',title:"Text"}}}]),e}()}]).default},function(e,t,n){var o,r,i;"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self&&self,r=[t,n(2),n(3),n(37)],void 0===(i="function"==typeof(o=function(o,r,i,a){"use strict";var s=n(1);Object.defineProperty(o,"__esModule",{value:!0}),o.default=void 0,r=s(r),i=s(i);var l=function(){function e(){(0,r.default)(this,e),this.commandName="bold",this.CSS={button:"ce-inline-tool",buttonActive:"ce-inline-tool--active",buttonModifier:"ce-inline-tool--bold"},this.nodes={button:void 0}}return(0,i.default)(e,[{key:"render",value:function(){return this.nodes.button=document.createElement("button"),this.nodes.button.type="button",this.nodes.button.classList.add(this.CSS.button,this.CSS.buttonModifier),this.nodes.button.innerHTML=a.IconBold,this.nodes.button}},{key:"surround",value:function(){document.execCommand(this.commandName)}},{key:"checkState",value:function(){var e=document.queryCommandState(this.commandName);return this.nodes.button.classList.toggle(this.CSS.buttonActive,e),e}},{key:"shortcut",get:function(){return"CMD+B"}}],[{key:"sanitize",get:function(){return{b:{}}}}]),e}();o.default=l,l.displayName="BoldInlineTool",l.isInline=!0,l.title="Bold",e.exports=t.default})?o.apply(t,r):o)||(e.exports=i)},function(e,t,n){var o,r,i;"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self&&self,r=[t,n(2),n(3),n(37)],void 0===(i="function"==typeof(o=function(o,r,i,a){"use strict";var s=n(1);Object.defineProperty(o,"__esModule",{value:!0}),o.default=void 0,r=s(r),i=s(i);var l=function(){function e(){(0,r.default)(this,e),this.commandName="italic",this.CSS={button:"ce-inline-tool",buttonActive:"ce-inline-tool--active",buttonModifier:"ce-inline-tool--italic"},this.nodes={button:null}}return(0,i.default)(e,[{key:"render",value:function(){return this.nodes.button=document.createElement("button"),this.nodes.button.type="button",this.nodes.button.classList.add(this.CSS.button,this.CSS.buttonModifier),this.nodes.button.innerHTML=a.IconItalic,this.nodes.button}},{key:"surround",value:function(){document.execCommand(this.commandName)}},{key:"checkState",value:function(){var e=document.queryCommandState(this.commandName);return this.nodes.button.classList.toggle(this.CSS.buttonActive,e),e}},{key:"shortcut",get:function(){return"CMD+I"}}],[{key:"sanitize",get:function(){return{i:{}}}}]),e}();o.default=l,l.displayName="ItalicInlineTool",l.isInline=!0,l.title="Italic",e.exports=t.default})?o.apply(t,r):o)||(e.exports=i)},function(e,t,n){var o,r,i,a=n(7);"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self&&self,r=[t,n(2),n(3),n(25),n(8),n(37)],void 0===(i="function"==typeof(o=function(o,r,i,s,l,c){"use strict";var u=n(1);function f(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(f=function(e){return e?n:t})(e)}Object.defineProperty(o,"__esModule",{value:!0}),o.default=void 0,r=u(r),i=u(i),s=u(s),l=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!==a(e)&&"function"!=typeof e)return{default:e};var n=f(t);if(n&&n.has(e))return n.get(e);var o={},r=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var i in e)if("default"!==i&&Object.prototype.hasOwnProperty.call(e,i)){var s=r?Object.getOwnPropertyDescriptor(e,i):null;s&&(s.get||s.set)?Object.defineProperty(o,i,s):o[i]=e[i]}return o.default=e,n&&n.set(e,o),o}(l);var d=function(){function e(t){var n=t.api;(0,r.default)(this,e),this.commandLink="createLink",this.commandUnlink="unlink",this.ENTER_KEY=13,this.CSS={button:"ce-inline-tool",buttonActive:"ce-inline-tool--active",buttonModifier:"ce-inline-tool--link",buttonUnlink:"ce-inline-tool--unlink",input:"ce-inline-tool-input",inputShowed:"ce-inline-tool-input--showed"},this.nodes={button:null,input:null},this.inputOpened=!1,this.toolbar=n.toolbar,this.inlineToolbar=n.inlineToolbar,this.notifier=n.notifier,this.i18n=n.i18n,this.selection=new s.default}return(0,i.default)(e,[{key:"render",value:function(){return this.nodes.button=document.createElement("button"),this.nodes.button.type="button",this.nodes.button.classList.add(this.CSS.button,this.CSS.buttonModifier),this.nodes.button.innerHTML=c.IconLink,this.nodes.button}},{key:"renderActions",value:function(){var e=this;return this.nodes.input=document.createElement("input"),this.nodes.input.placeholder=this.i18n.t("Add a link"),this.nodes.input.classList.add(this.CSS.input),this.nodes.input.addEventListener("keydown",(function(t){t.keyCode===e.ENTER_KEY&&e.enterPressed(t)})),this.nodes.input}},{key:"surround",value:function(e){if(e){this.inputOpened?(this.selection.restore(),this.selection.removeFakeBackground()):(this.selection.setFakeBackground(),this.selection.save());var t=this.selection.findParentTag("A");if(t)return this.selection.expandToTag(t),this.unlink(),this.closeActions(),this.checkState(),void this.toolbar.close()}this.toggleActions()}},{key:"checkState",value:function(){var e=this.selection.findParentTag("A");if(e){this.nodes.button.innerHTML=c.IconUnlink,this.nodes.button.classList.add(this.CSS.buttonUnlink),this.nodes.button.classList.add(this.CSS.buttonActive),this.openActions();var t=e.getAttribute("href");this.nodes.input.value="null"!==t?t:"",this.selection.save()}else this.nodes.button.innerHTML=c.IconLink,this.nodes.button.classList.remove(this.CSS.buttonUnlink),this.nodes.button.classList.remove(this.CSS.buttonActive);return!!e}},{key:"clear",value:function(){this.closeActions()}},{key:"shortcut",get:function(){return"CMD+K"}},{key:"toggleActions",value:function(){this.inputOpened?this.closeActions(!1):this.openActions(!0)}},{key:"openActions",value:function(){var e=arguments.length>0&&void 0!==arguments[0]&&arguments[0];this.nodes.input.classList.add(this.CSS.inputShowed),e&&this.nodes.input.focus(),this.inputOpened=!0}},{key:"closeActions",value:function(){var e=!(arguments.length>0&&void 0!==arguments[0])||arguments[0];if(this.selection.isFakeBackgroundEnabled){var t=new s.default;t.save(),this.selection.restore(),this.selection.removeFakeBackground(),t.restore()}this.nodes.input.classList.remove(this.CSS.inputShowed),this.nodes.input.value="",e&&this.selection.clearSaved(),this.inputOpened=!1}},{key:"enterPressed",value:function(e){var t=this.nodes.input.value||"";return t.trim()?this.validateURL(t)?(t=this.prepareLink(t),this.selection.restore(),this.selection.removeFakeBackground(),this.insertLink(t),e.preventDefault(),e.stopPropagation(),e.stopImmediatePropagation(),this.selection.collapseToEnd(),void this.inlineToolbar.close()):(this.notifier.show({message:"Pasted link is not valid.",style:"error"}),void l.log("Incorrect Link pasted","warn",t)):(this.selection.restore(),this.unlink(),e.preventDefault(),void this.closeActions())}},{key:"validateURL",value:function(e){return!/\s/.test(e)}},{key:"prepareLink",value:function(e){return e=e.trim(),e=this.addProtocol(e)}},{key:"addProtocol",value:function(e){if(/^(\w+):(\/\/)?/.test(e))return e;var t=/^\/[^/\s]/.test(e),n="#"===e.substring(0,1),o=/^\/\/[^/\s]/.test(e);return t||n||o||(e="http://"+e),e}},{key:"insertLink",value:function(e){var t=this.selection.findParentTag("A");t&&this.selection.expandToTag(t),document.execCommand(this.commandLink,!1,e)}},{key:"unlink",value:function(){document.execCommand(this.commandUnlink)}}],[{key:"sanitize",get:function(){return{a:{href:!0,target:"_blank",rel:"nofollow"}}}}]),e}();o.default=d,d.displayName="LinkInlineTool",d.isInline=!0,d.title="Link",e.exports=t.default})?o.apply(t,r):o)||(e.exports=i)},function(e,t,n){var o,r,i;"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self&&self,r=[t,n(2),n(3),n(19)],void 0===(i="function"==typeof(o=function(o,r,i,a){"use strict";var s=n(1);Object.defineProperty(o,"__esModule",{value:!0}),o.default=void 0,r=s(r),i=s(i),a=s(a);var l=function(){function e(t){var n=t.data,o=t.api;(0,r.default)(this,e),this.CSS={wrapper:"ce-stub",info:"ce-stub__info",title:"ce-stub__title",subtitle:"ce-stub__subtitle"},this.api=o,this.title=n.title||this.api.i18n.t("Error"),this.subtitle=this.api.i18n.t("The block can not be displayed correctly."),this.savedData=n.savedData,this.wrapper=this.make()}return(0,i.default)(e,[{key:"render",value:function(){return this.wrapper}},{key:"save",value:function(){return this.savedData}},{key:"make",value:function(){var e=a.default.make("div",this.CSS.wrapper),t=a.default.make("div",this.CSS.info),n=a.default.make("div",this.CSS.title,{textContent:this.title}),o=a.default.make("div",this.CSS.subtitle,{textContent:this.subtitle});return e.innerHTML='',t.appendChild(n),t.appendChild(o),e.appendChild(t),e}}]),e}();o.default=l,l.displayName="Stub",l.isReadOnlySupported=!0,e.exports=t.default})?o.apply(t,r):o)||(e.exports=i)},function(e,t,n){var o,r,i;"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self&&self,r=[t,n(2),n(3),n(69),n(396),n(397),n(398)],void 0===(i="function"==typeof(o=function(o,r,i,a,s,l,c){"use strict";var u=n(1);Object.defineProperty(o,"__esModule",{value:!0}),o.default=void 0,r=u(r),i=u(i),s=u(s),l=u(l),c=u(c);var f=function(e,t){var n={};for(var o in e)Object.prototype.hasOwnProperty.call(e,o)&&t.indexOf(o)<0&&(n[o]=e[o]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols){var r=0;for(o=Object.getOwnPropertySymbols(e);r=0;s--)(r=e[s])&&(a=(i<3?r(a):i>3?r(t,n,a):r(t,n))||a);return i>3&&a&&Object.defineProperty(t,n,a),a},m=function(e){(0,l.default)(n,e);var t=k(n);function n(){var e;return(0,r.default)(this,n),(e=t.apply(this,arguments)).type=d.ToolType.Block,e.inlineTools=new h.default,e.tunes=new h.default,e}return(0,i.default)(n,[{key:"create",value:function(e,t,n){return new this.constructable({data:e,block:t,readOnly:n,api:this.api.getMethodsForTool(this),config:this.settings})}},{key:"isReadOnlySupported",get:function(){return!0===this.constructable[d.InternalBlockToolSettings.IsReadOnlySupported]}},{key:"isLineBreaksEnabled",get:function(){return this.constructable[d.InternalBlockToolSettings.IsEnabledLineBreaks]}},{key:"toolbox",get:function(){var e=this.constructable[d.InternalBlockToolSettings.Toolbox],t=this.config[d.UserSettings.Toolbox];if(!p.isEmpty(e)&&!1!==t)return t?Array.isArray(e)?Array.isArray(t)?t.map((function(t,n){var o=e[n];return o?Object.assign(Object.assign({},o),t):t})):[t]:Array.isArray(t)?t:[Object.assign(Object.assign({},e),t)]:Array.isArray(e)?e:[e]}},{key:"conversionConfig",get:function(){return this.constructable[d.InternalBlockToolSettings.ConversionConfig]}},{key:"enabledInlineTools",get:function(){return this.config[d.UserSettings.EnabledInlineTools]||!1}},{key:"enabledBlockTunes",get:function(){return this.config[d.UserSettings.EnabledBlockTunes]}},{key:"pasteConfig",get:function(){return this.constructable[d.InternalBlockToolSettings.PasteConfig]||{}}},{key:"sanitizeConfig",get:function(){var e=(0,s.default)((0,u.default)(n.prototype),"sanitizeConfig",this),t=this.baseSanitizeConfig;if(p.isEmpty(e))return t;var o={};for(var r in e)if(Object.prototype.hasOwnProperty.call(e,r)){var i=e[r];p.isObject(i)?o[r]=Object.assign({},t,i):o[r]=i}return o}},{key:"baseSanitizeConfig",get:function(){var e={};return Array.from(this.inlineTools.values()).forEach((function(t){return Object.assign(e,t.sanitizeConfig)})),Array.from(this.tunes.values()).forEach((function(t){return Object.assign(e,t.sanitizeConfig)})),e}}]),n}(d.default);o.default=m,m.displayName="BlockTool",b([p.cacheable],m.prototype,"sanitizeConfig",null),b([p.cacheable],m.prototype,"baseSanitizeConfig",null),e.exports=t.default})?o.apply(t,r):o)||(e.exports=i)},function(e,t,n){var o,r,i;"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self&&self,r=[t,n(2),n(3),n(85),n(37)],void 0===(i="function"==typeof(o=function(o,r,i,a,s){"use strict";var l=n(1);Object.defineProperty(o,"__esModule",{value:!0}),o.default=void 0,r=l(r),i=l(i),a=l(a);var c=function(){function e(t){var n=t.api;(0,r.default)(this,e),this.CSS={animation:"wobble"},this.api=n}return(0,i.default)(e,[{key:"render",value:function(){var e=this;return{icon:s.IconChevronDown,title:this.api.i18n.t("Move down"),onActivate:function(t,n){return e.handleClick(n)},name:"move-down"}}},{key:"handleClick",value:function(e){var t=this,n=this.api.blocks.getCurrentBlockIndex(),o=this.api.blocks.getBlockByIndex(n+1);if(!o){var r=e.target.closest("."+a.default.CSS.item).querySelector("."+a.default.CSS.itemIcon);return r.classList.add(this.CSS.animation),void window.setTimeout((function(){r.classList.remove(t.CSS.animation)}),500)}var i=o.holder,s=i.getBoundingClientRect(),l=Math.abs(window.innerHeight-i.offsetHeight);s.top0?Math.abs(u.top)-Math.abs(f.top):window.innerHeight-Math.abs(u.top)+Math.abs(f.top),window.scrollBy(0,-1*s),this.api.blocks.move(n-1),this.api.toolbar.toggleBlockSettings(!0)}}]),e}();o.default=c,c.displayName="MoveUpTune",c.isTune=!0,e.exports=t.default})?o.apply(t,r):o)||(e.exports=i)},function(e,t){e.exports='.codex-editor{position:relative;-webkit-box-sizing:border-box;box-sizing:border-box;z-index:1}.codex-editor .hide,.codex-editor__redactor--hidden{display:none}.codex-editor__redactor [contenteditable]:empty:after{content:"\\feff "}@media (min-width:651px){.codex-editor--narrow .codex-editor__redactor{margin-right:50px}}@media (min-width:651px){.codex-editor--narrow.codex-editor--rtl .codex-editor__redactor{margin-left:50px;margin-right:0}}@media (min-width:651px){.codex-editor--narrow .ce-toolbar__actions{right:-5px}}.codex-editor__loader{position:relative;height:30vh}.codex-editor__loader:before{content:"";position:absolute;left:50%;top:50%;width:30px;height:30px;margin-top:-15px;margin-left:-15px;border-radius:50%;border:2px solid rgba(201,201,204,.48);border-top-color:transparent;-webkit-box-sizing:border-box;box-sizing:border-box;-webkit-animation:editor-loader-spin .8s linear infinite;animation:editor-loader-spin .8s linear infinite;will-change:transform}.codex-editor-copyable{position:absolute;height:1px;width:1px;top:-400%;opacity:.001}.codex-editor-overlay{position:fixed;top:0;left:0;right:0;bottom:0;z-index:999;pointer-events:none;overflow:hidden}.codex-editor-overlay__container{position:relative;pointer-events:auto;z-index:0}.codex-editor-overlay__rectangle{position:absolute;pointer-events:none;background-color:rgba(46,170,220,.2);border:1px solid transparent}.codex-editor svg{max-height:100%}.codex-editor path{stroke:currentColor}::-moz-selection{background-color:#d4ecff}::selection{background-color:#d4ecff}.codex-editor--toolbox-opened [contentEditable=true][data-placeholder]:focus:before{opacity:0!important}@-webkit-keyframes editor-loader-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@keyframes editor-loader-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}.ce-scroll-locked{overflow:hidden}.ce-scroll-locked--hard{overflow:hidden;top:calc(var(--window-scroll-offset)*-1);position:fixed;width:100%}.ce-toolbar{position:absolute;left:0;right:0;top:0;-webkit-transition:opacity .1s ease;transition:opacity .1s ease;will-change:opacity,top;display:none}.ce-toolbar--opened{display:block}.ce-toolbar__content{max-width:650px;margin:0 auto;position:relative}.ce-toolbar__plus{color:#1d202b;cursor:pointer;width:26px;height:26px;border-radius:7px;display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}@media (max-width:650px){.ce-toolbar__plus{width:36px;height:36px}}@media (hover:hover){.ce-toolbar__plus:hover{background-color:#eff2f5}}.ce-toolbar__plus--active{background-color:#eff2f5;-webkit-animation:bounceIn .75s 1;animation:bounceIn .75s 1;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards}.ce-toolbar__plus{-ms-flex-negative:0;flex-shrink:0}.ce-toolbar__plus-shortcut{opacity:.6;word-spacing:-2px;margin-top:5px}@media (max-width:650px){.ce-toolbar__plus{position:absolute;background-color:#fff;border:1px solid #e8e8eb;-webkit-box-shadow:0 3px 15px -3px rgba(13,20,33,.13);box-shadow:0 3px 15px -3px rgba(13,20,33,.13);border-radius:6px;z-index:2}.ce-toolbar__plus--left-oriented:before{left:15px;margin-left:0}.ce-toolbar__plus--right-oriented:before{left:auto;right:15px;margin-left:0}.ce-toolbar__plus{position:static}}.ce-toolbar__actions{position:absolute;right:100%;opacity:0;display:-webkit-box;display:-ms-flexbox;display:flex;padding-right:5px}.ce-toolbar__actions--opened{opacity:1}@media (max-width:650px){.ce-toolbar__actions{right:auto}}.ce-toolbar__settings-btn{color:#1d202b;width:26px;height:26px;border-radius:7px;display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center}@media (max-width:650px){.ce-toolbar__settings-btn{width:36px;height:36px}}@media (hover:hover){.ce-toolbar__settings-btn:hover{background-color:#eff2f5}}.ce-toolbar__settings-btn--active{background-color:#eff2f5;-webkit-animation:bounceIn .75s 1;animation:bounceIn .75s 1;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards}.ce-toolbar__settings-btn{margin-left:3px;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}@media (min-width:651px){.ce-toolbar__settings-btn{width:24px}}.ce-toolbar__settings-btn--hidden{display:none}@media (max-width:650px){.ce-toolbar__settings-btn{position:absolute;background-color:#fff;border:1px solid #e8e8eb;-webkit-box-shadow:0 3px 15px -3px rgba(13,20,33,.13);box-shadow:0 3px 15px -3px rgba(13,20,33,.13);border-radius:6px;z-index:2}.ce-toolbar__settings-btn--left-oriented:before{left:15px;margin-left:0}.ce-toolbar__settings-btn--right-oriented:before{left:auto;right:15px;margin-left:0}.ce-toolbar__settings-btn{position:static}}.ce-toolbar__plus svg,.ce-toolbar__settings-btn svg{width:24px;height:24px}@media (min-width:651px){.codex-editor--narrow .ce-toolbar__plus{left:5px}}.ce-toolbox{--gap:8px}@media (min-width:651px){.ce-toolbox{position:absolute;top:calc(26px + var(--gap));left:0}.ce-toolbox--opened-top{top:calc(var(--gap)*-1 + var(--popover-height)*-1)}}@media (min-width:651px){.codex-editor--narrow .ce-toolbox{left:auto;right:0}.codex-editor--narrow .ce-toolbox .ce-popover{right:0}}.ce-inline-toolbar{--y-offset:8px;position:absolute;background-color:#fff;border:1px solid #e8e8eb;-webkit-box-shadow:0 3px 15px -3px rgba(13,20,33,.13);box-shadow:0 3px 15px -3px rgba(13,20,33,.13);border-radius:6px;z-index:2}.ce-inline-toolbar--left-oriented:before{left:15px;margin-left:0}.ce-inline-toolbar--right-oriented:before{left:auto;right:15px;margin-left:0}.ce-inline-toolbar{-webkit-transform:translateX(-50%) translateY(8px) scale(.94);transform:translateX(-50%) translateY(8px) scale(.94);opacity:0;visibility:hidden;-webkit-transition:opacity .25s ease,-webkit-transform .15s ease;transition:opacity .25s ease,-webkit-transform .15s ease;transition:transform .15s ease,opacity .25s ease;transition:transform .15s ease,opacity .25s ease,-webkit-transform .15s ease;will-change:transform,opacity;top:0;left:0;z-index:3}.ce-inline-toolbar--showed{opacity:1;visibility:visible;-webkit-transform:translateX(-50%);transform:translateX(-50%)}.ce-inline-toolbar--left-oriented{-webkit-transform:translateX(-23px) translateY(8px) scale(.94);transform:translateX(-23px) translateY(8px) scale(.94)}.ce-inline-toolbar--left-oriented.ce-inline-toolbar--showed{-webkit-transform:translateX(-23px);transform:translateX(-23px)}.ce-inline-toolbar--right-oriented{-webkit-transform:translateX(-100%) translateY(8px) scale(.94);transform:translateX(-100%) translateY(8px) scale(.94);margin-left:23px}.ce-inline-toolbar--right-oriented.ce-inline-toolbar--showed{-webkit-transform:translateX(-100%);transform:translateX(-100%)}.ce-inline-toolbar [hidden]{display:none!important}.ce-inline-toolbar__toggler-and-button-wrapper{width:100%;padding:0 6px}.ce-inline-toolbar__buttons,.ce-inline-toolbar__toggler-and-button-wrapper{display:-webkit-box;display:-ms-flexbox;display:flex}.ce-inline-toolbar__dropdown{display:-webkit-box;display:-ms-flexbox;display:flex;padding:6px;margin:0 6px 0 -6px;-webkit-box-align:center;-ms-flex-align:center;align-items:center;cursor:pointer;border-right:1px solid rgba(201,201,204,.48);-webkit-box-sizing:border-box;box-sizing:border-box}@media (hover:hover){.ce-inline-toolbar__dropdown:hover{background:#eff2f5}}.ce-inline-toolbar__dropdown--hidden{display:none}.ce-inline-toolbar__dropdown-arrow,.ce-inline-toolbar__dropdown-content{display:-webkit-box;display:-ms-flexbox;display:flex}.ce-inline-toolbar__dropdown-arrow svg,.ce-inline-toolbar__dropdown-content svg{width:20px;height:20px}.ce-inline-toolbar__shortcut{opacity:.6;word-spacing:-3px;margin-top:3px}.ce-inline-tool{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;padding:6px 1px;border-radius:3px;cursor:pointer;border:0;outline:none;background-color:transparent;vertical-align:bottom;color:inherit;margin:0}.ce-inline-tool svg{width:20px;height:20px}@media (max-width:650px){.ce-inline-tool svg{width:28px;height:28px}}@media (hover:hover){.ce-inline-tool:hover{background-color:#eff2f5}}.ce-inline-tool--active{color:#388ae5}.ce-inline-tool--focused{-webkit-box-shadow:inset 0 0 0 1px rgba(7,161,227,.08);box-shadow:inset 0 0 0 1px rgba(7,161,227,.08);background:rgba(34,186,255,.08)!important}.ce-inline-tool--focused-animated{-webkit-animation-name:buttonClicked;animation-name:buttonClicked;-webkit-animation-duration:.25s;animation-duration:.25s}.ce-inline-tool{border-radius:0;line-height:normal}.ce-inline-tool--link .icon--unlink,.ce-inline-tool--unlink .icon--link{display:none}.ce-inline-tool--unlink .icon--unlink{display:inline-block;margin-bottom:-1px}.ce-inline-tool-input{outline:none;border:0;border-radius:0 0 4px 4px;margin:0;font-size:13px;padding:10px;width:100%;-webkit-box-sizing:border-box;box-sizing:border-box;display:none;font-weight:500;border-top:1px solid rgba(201,201,204,.48);-webkit-appearance:none;font-family:inherit}@media (max-width:650px){.ce-inline-tool-input{font-size:15px;font-weight:500}}.ce-inline-tool-input::-webkit-input-placeholder{color:#707684}.ce-inline-tool-input::-moz-placeholder{color:#707684}.ce-inline-tool-input:-ms-input-placeholder{color:#707684}.ce-inline-tool-input::-ms-input-placeholder{color:#707684}.ce-inline-tool-input::placeholder{color:#707684}.ce-inline-tool-input--showed{display:block}.ce-conversion-toolbar{position:absolute;background-color:#fff;border:1px solid #e8e8eb;-webkit-box-shadow:0 3px 15px -3px rgba(13,20,33,.13);box-shadow:0 3px 15px -3px rgba(13,20,33,.13);border-radius:6px;z-index:2}.ce-conversion-toolbar--left-oriented:before{left:15px;margin-left:0}.ce-conversion-toolbar--right-oriented:before{left:auto;right:15px;margin-left:0}.ce-conversion-toolbar{opacity:0;visibility:hidden;will-change:transform,opacity;-webkit-transition:opacity .1s ease,-webkit-transform .1s ease;transition:opacity .1s ease,-webkit-transform .1s ease;transition:transform .1s ease,opacity .1s ease;transition:transform .1s ease,opacity .1s ease,-webkit-transform .1s ease;-webkit-transform:translateY(-8px);transform:translateY(-8px);left:-1px;width:150px;margin-top:5px;-webkit-box-sizing:content-box;box-sizing:content-box}.ce-conversion-toolbar--showed{opacity:1;visibility:visible;-webkit-transform:none;transform:none}.ce-conversion-toolbar [hidden]{display:none!important}.ce-conversion-toolbar__buttons{display:-webkit-box;display:-ms-flexbox;display:flex}.ce-conversion-toolbar__label{color:#707684;font-size:11px;font-weight:500;letter-spacing:.33px;padding:10px 10px 5px;text-transform:uppercase}.ce-conversion-tool{display:-webkit-box;display:-ms-flexbox;display:flex;padding:5px 10px;font-size:14px;line-height:20px;font-weight:500;cursor:pointer;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.ce-conversion-tool--hidden{display:none}.ce-conversion-tool--focused{-webkit-box-shadow:inset 0 0 0 1px rgba(7,161,227,.08);box-shadow:inset 0 0 0 1px rgba(7,161,227,.08);background:rgba(34,186,255,.08)!important}.ce-conversion-tool--focused-animated{-webkit-animation-name:buttonClicked;animation-name:buttonClicked;-webkit-animation-duration:.25s;animation-duration:.25s}.ce-conversion-tool:hover{background:#eff2f5}.ce-conversion-tool__icon{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;width:26px;height:26px;-webkit-box-shadow:0 0 0 1px rgba(201,201,204,.48);box-shadow:0 0 0 1px rgba(201,201,204,.48);border-radius:5px;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;background:#fff;-webkit-box-sizing:content-box;box-sizing:content-box;-ms-flex-negative:0;flex-shrink:0;margin-right:10px}.ce-conversion-tool__icon svg{width:20px;height:20px}@media (max-width:650px){.ce-conversion-tool__icon{width:36px;height:36px;border-radius:8px}.ce-conversion-tool__icon svg{width:28px;height:28px}}.ce-conversion-tool--last{margin-right:0!important}.ce-conversion-tool--active{color:#388ae5!important;-webkit-animation:bounceIn .75s 1;animation:bounceIn .75s 1;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards}.ce-settings{position:absolute;z-index:2;--gap:8px}@media (min-width:651px){.ce-settings{position:absolute;top:calc(26px + var(--gap));left:0}.ce-settings--opened-top{top:calc(var(--gap)*-1 + var(--popover-height)*-1)}}.ce-settings__button{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;padding:6px 1px;border-radius:3px;cursor:pointer;border:0;outline:none;background-color:transparent;vertical-align:bottom;color:inherit;margin:0}.ce-settings__button svg{width:20px;height:20px}@media (max-width:650px){.ce-settings__button svg{width:28px;height:28px}}@media (hover:hover){.ce-settings__button:hover{background-color:#eff2f5}}.ce-settings__button--active{color:#388ae5}.ce-settings__button--focused{-webkit-box-shadow:inset 0 0 0 1px rgba(7,161,227,.08);box-shadow:inset 0 0 0 1px rgba(7,161,227,.08);background:rgba(34,186,255,.08)!important}.ce-settings__button--focused-animated{-webkit-animation-name:buttonClicked;animation-name:buttonClicked;-webkit-animation-duration:.25s;animation-duration:.25s}.ce-settings__button:not(:nth-child(3n+3)){margin-right:3px}.ce-settings__button:nth-child(n+4){margin-top:3px}.ce-settings__button{line-height:32px}.ce-settings__button--disabled{cursor:not-allowed!important;opacity:.3}.ce-settings__button--selected{color:#388ae5}@-webkit-keyframes fade-in{0%{opacity:0}to{opacity:1}}@keyframes fade-in{0%{opacity:0}to{opacity:1}}.ce-block{-webkit-animation:fade-in .3s ease;animation:fade-in .3s ease;-webkit-animation-fill-mode:none;-webkit-animation-fill-mode:initial;animation-fill-mode:none}.ce-block:first-of-type{margin-top:0}.ce-block--selected .ce-block__content{background:#e1f2ff}.ce-block--selected .ce-block__content [contenteditable]{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.ce-block--selected .ce-block__content .ce-stub,.ce-block--selected .ce-block__content img{opacity:.55}.ce-block--stretched .ce-block__content{max-width:none}.ce-block__content{position:relative;max-width:650px;margin:0 auto;-webkit-transition:background-color .15s ease;transition:background-color .15s ease}.ce-block--drop-target .ce-block__content:before{content:"";position:absolute;top:100%;left:-20px;margin-top:-1px;height:8px;width:8px;border:solid #388ae5;border-width:1px 1px 0 0;-webkit-transform-origin:right;transform-origin:right;-webkit-transform:rotate(45deg);transform:rotate(45deg)}.ce-block--drop-target .ce-block__content:after{content:"";position:absolute;top:100%;height:1px;width:100%;color:#388ae5;background:repeating-linear-gradient(90deg,#388ae5,#388ae5 1px,#fff 0,#fff 6px)}.ce-block a{cursor:pointer;text-decoration:underline}.ce-block b{font-weight:700}.ce-block i{font-style:italic}@media (min-width:651px){.codex-editor--narrow .ce-block--focused{margin-right:-50px;padding-right:50px}}.wobble{-webkit-animation-name:wobble;animation-name:wobble;-webkit-animation-duration:.4s;animation-duration:.4s}@-webkit-keyframes wobble{0%{-webkit-transform:translateZ(0);transform:translateZ(0)}15%{-webkit-transform:translate3d(-5%,0,0) rotate(-5deg);transform:translate3d(-5%,0,0) rotate(-5deg)}30%{-webkit-transform:translate3d(2%,0,0) rotate(3deg);transform:translate3d(2%,0,0) rotate(3deg)}45%{-webkit-transform:translate3d(-3%,0,0) rotate(-3deg);transform:translate3d(-3%,0,0) rotate(-3deg)}60%{-webkit-transform:translate3d(2%,0,0) rotate(2deg);transform:translate3d(2%,0,0) rotate(2deg)}75%{-webkit-transform:translate3d(-1%,0,0) rotate(-1deg);transform:translate3d(-1%,0,0) rotate(-1deg)}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}@keyframes wobble{0%{-webkit-transform:translateZ(0);transform:translateZ(0)}15%{-webkit-transform:translate3d(-5%,0,0) rotate(-5deg);transform:translate3d(-5%,0,0) rotate(-5deg)}30%{-webkit-transform:translate3d(2%,0,0) rotate(3deg);transform:translate3d(2%,0,0) rotate(3deg)}45%{-webkit-transform:translate3d(-3%,0,0) rotate(-3deg);transform:translate3d(-3%,0,0) rotate(-3deg)}60%{-webkit-transform:translate3d(2%,0,0) rotate(2deg);transform:translate3d(2%,0,0) rotate(2deg)}75%{-webkit-transform:translate3d(-1%,0,0) rotate(-1deg);transform:translate3d(-1%,0,0) rotate(-1deg)}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}@-webkit-keyframes bounceIn{0%,20%,40%,60%,80%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{-webkit-transform:scale3d(.9,.9,.9);transform:scale3d(.9,.9,.9)}20%{-webkit-transform:scale3d(1.03,1.03,1.03);transform:scale3d(1.03,1.03,1.03)}60%{-webkit-transform:scaleX(1);transform:scaleX(1)}}@keyframes bounceIn{0%,20%,40%,60%,80%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{-webkit-transform:scale3d(.9,.9,.9);transform:scale3d(.9,.9,.9)}20%{-webkit-transform:scale3d(1.03,1.03,1.03);transform:scale3d(1.03,1.03,1.03)}60%{-webkit-transform:scaleX(1);transform:scaleX(1)}}@-webkit-keyframes selectionBounce{0%,20%,40%,60%,80%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}50%{-webkit-transform:scale3d(1.01,1.01,1.01);transform:scale3d(1.01,1.01,1.01)}70%{-webkit-transform:scaleX(1);transform:scaleX(1)}}@keyframes selectionBounce{0%,20%,40%,60%,80%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}50%{-webkit-transform:scale3d(1.01,1.01,1.01);transform:scale3d(1.01,1.01,1.01)}70%{-webkit-transform:scaleX(1);transform:scaleX(1)}}@-webkit-keyframes buttonClicked{0%,20%,40%,60%,80%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{-webkit-transform:scale3d(.95,.95,.95);transform:scale3d(.95,.95,.95)}60%{-webkit-transform:scale3d(1.02,1.02,1.02);transform:scale3d(1.02,1.02,1.02)}80%{-webkit-transform:scaleX(1);transform:scaleX(1)}}@keyframes buttonClicked{0%,20%,40%,60%,80%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{-webkit-transform:scale3d(.95,.95,.95);transform:scale3d(.95,.95,.95)}60%{-webkit-transform:scale3d(1.02,1.02,1.02);transform:scale3d(1.02,1.02,1.02)}80%{-webkit-transform:scaleX(1);transform:scaleX(1)}}@-webkit-keyframes panelShowing{0%{opacity:0;-webkit-transform:translateY(-8px) scale(.9);transform:translateY(-8px) scale(.9)}70%{opacity:1;-webkit-transform:translateY(2px);transform:translateY(2px)}to{-webkit-transform:translateY(0);transform:translateY(0)}}@keyframes panelShowing{0%{opacity:0;-webkit-transform:translateY(-8px) scale(.9);transform:translateY(-8px) scale(.9)}70%{opacity:1;-webkit-transform:translateY(2px);transform:translateY(2px)}to{-webkit-transform:translateY(0);transform:translateY(0)}}@-webkit-keyframes panelShowingMobile{0%{opacity:0;-webkit-transform:translateY(14px) scale(.98);transform:translateY(14px) scale(.98)}70%{opacity:1;-webkit-transform:translateY(-4px);transform:translateY(-4px)}to{-webkit-transform:translateY(0);transform:translateY(0)}}@keyframes panelShowingMobile{0%{opacity:0;-webkit-transform:translateY(14px) scale(.98);transform:translateY(14px) scale(.98)}70%{opacity:1;-webkit-transform:translateY(-4px);transform:translateY(-4px)}to{-webkit-transform:translateY(0);transform:translateY(0)}}.cdx-block{padding:.4em 0}.cdx-block::-webkit-input-placeholder{line-height:normal!important}.cdx-input{border:1px solid rgba(201,201,204,.48);-webkit-box-shadow:inset 0 1px 2px 0 rgba(35,44,72,.06);box-shadow:inset 0 1px 2px 0 rgba(35,44,72,.06);border-radius:3px;padding:10px 12px;outline:none;width:100%;-webkit-box-sizing:border-box;box-sizing:border-box}.cdx-input[data-placeholder]:before{position:static!important;display:inline-block;width:0;white-space:nowrap;pointer-events:none}.cdx-settings-button{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;padding:6px 1px;border-radius:3px;cursor:pointer;border:0;outline:none;background-color:transparent;vertical-align:bottom;color:inherit;margin:0}.cdx-settings-button svg{width:20px;height:20px}@media (max-width:650px){.cdx-settings-button svg{width:28px;height:28px}}@media (hover:hover){.cdx-settings-button:hover{background-color:#eff2f5}}.cdx-settings-button--focused{-webkit-box-shadow:inset 0 0 0 1px rgba(7,161,227,.08);box-shadow:inset 0 0 0 1px rgba(7,161,227,.08);background:rgba(34,186,255,.08)!important}.cdx-settings-button--focused-animated{-webkit-animation-name:buttonClicked;animation-name:buttonClicked;-webkit-animation-duration:.25s;animation-duration:.25s}.cdx-settings-button{min-width:26px;min-height:26px}.cdx-settings-button--active{color:#388ae5}.cdx-settings-button svg{width:auto;height:auto}@media (max-width:650px){.cdx-settings-button{width:36px;height:36px;border-radius:8px}}.cdx-loader{position:relative;border:1px solid rgba(201,201,204,.48)}.cdx-loader:before{content:"";position:absolute;left:50%;top:50%;width:18px;height:18px;margin:-11px 0 0 -11px;border:2px solid rgba(201,201,204,.48);border-left-color:#388ae5;border-radius:50%;-webkit-animation:cdxRotation 1.2s linear infinite;animation:cdxRotation 1.2s linear infinite}@-webkit-keyframes cdxRotation{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@keyframes cdxRotation{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}.cdx-button{padding:13px;border-radius:3px;border:1px solid rgba(201,201,204,.48);font-size:14.9px;background:#fff;-webkit-box-shadow:0 2px 2px 0 rgba(18,30,57,.04);box-shadow:0 2px 2px 0 rgba(18,30,57,.04);color:#707684;text-align:center;cursor:pointer}@media (hover:hover){.cdx-button:hover{background:#fbfcfe;-webkit-box-shadow:0 1px 3px 0 rgba(18,30,57,.08);box-shadow:0 1px 3px 0 rgba(18,30,57,.08)}}.cdx-button svg{height:20px;margin-right:.2em;margin-top:-2px}.ce-stub{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;width:100%;padding:3.5em 0;margin:17px 0;border-radius:3px;background:#fcf7f7;color:#b46262}.ce-stub__info{margin-left:20px}.ce-stub__title{margin-bottom:3px;font-weight:600;font-size:18px;text-transform:capitalize}.ce-stub__subtitle{font-size:16px}.codex-editor.codex-editor--rtl{direction:rtl}.codex-editor.codex-editor--rtl .cdx-list{padding-left:0;padding-right:40px}.codex-editor.codex-editor--rtl .ce-toolbar__plus{right:-26px;left:auto}.codex-editor.codex-editor--rtl .ce-toolbar__actions{right:auto;left:-26px}@media (max-width:650px){.codex-editor.codex-editor--rtl .ce-toolbar__actions{margin-left:0;margin-right:auto;padding-right:0;padding-left:10px}}.codex-editor.codex-editor--rtl .ce-settings{left:5px;right:auto}.codex-editor.codex-editor--rtl .ce-settings:before{right:auto;left:25px}.codex-editor.codex-editor--rtl .ce-settings__button:not(:nth-child(3n+3)){margin-left:3px;margin-right:0}.codex-editor.codex-editor--rtl .ce-conversion-tool__icon{margin-right:0;margin-left:10px}.codex-editor.codex-editor--rtl .ce-inline-toolbar__dropdown{border-right:0 solid transparent;border-left:1px solid rgba(201,201,204,.48);margin:0 -6px 0 6px}.codex-editor.codex-editor--rtl .ce-inline-toolbar__dropdown .icon--toggler-down{margin-left:0;margin-right:4px}@media (min-width:651px){.codex-editor--narrow.codex-editor--rtl .ce-toolbar__plus{left:0;right:5px}}@media (min-width:651px){.codex-editor--narrow.codex-editor--rtl .ce-toolbar__actions{left:-5px}}.ce-popover{opacity:0;will-change:opacity,transform;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;padding:6px;min-width:200px;width:200px;overflow:hidden;-webkit-box-sizing:border-box;box-sizing:border-box;-ms-flex-negative:0;flex-shrink:0;max-height:0;pointer-events:none;position:absolute;background-color:#fff;border:1px solid #e8e8eb;-webkit-box-shadow:0 3px 15px -3px rgba(13,20,33,.13);box-shadow:0 3px 15px -3px rgba(13,20,33,.13);border-radius:6px;z-index:2}.ce-popover--left-oriented:before{left:15px;margin-left:0}.ce-popover--right-oriented:before{left:auto;right:15px;margin-left:0}.ce-popover{z-index:4;-ms-flex-wrap:nowrap;flex-wrap:nowrap}.ce-popover--opened{opacity:1;max-height:270px;pointer-events:auto;-webkit-animation:panelShowing .1s ease;animation:panelShowing .1s ease}@media (max-width:650px){.ce-popover--opened{-webkit-animation:panelShowingMobile .25s ease;animation:panelShowingMobile .25s ease}}.ce-popover::-webkit-scrollbar{width:7px}.ce-popover::-webkit-scrollbar-thumb{-webkit-box-sizing:border-box;box-sizing:border-box;-webkit-box-shadow:inset 0 0 2px 2px #eff2f5;box-shadow:inset 0 0 2px 2px #eff2f5;border-color:transparent;border-style:solid;border-width:4px 3px 4px 0}@media (max-width:650px){.ce-popover{--offset:5px;position:fixed;max-width:none;min-width:calc(100% - var(--offset)*2);left:var(--offset);right:var(--offset);bottom:calc(var(--offset) + env(safe-area-inset-bottom));top:auto;border-radius:10px}}.ce-popover__items{overflow-y:auto;-ms-scroll-chaining:none;overscroll-behavior:contain}@media (min-width:651px){.ce-popover__items{margin-top:5px}}.ce-popover__item{display:grid;grid-template-columns:auto auto 1fr;grid-template-rows:auto;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:start;white-space:nowrap;padding:3px;font-size:14px;line-height:20px;font-weight:500;cursor:pointer;-webkit-box-align:center;-ms-flex-align:center;align-items:center;border-radius:6px}.ce-popover__item:not(:last-of-type){margin-bottom:1px}@media (max-width:650px){.ce-popover__item{font-size:16px;padding:4px}}@media (hover:hover){.ce-popover__item:hover:not(.ce-popover__item--no-visible-hover){background-color:#eff2f5}.ce-popover__item:hover .ce-popover__item-icon{-webkit-box-shadow:none;box-shadow:none}}.ce-popover__item--disabled{color:#707684;cursor:default;pointer-events:none}.ce-popover__item--disabled .ce-popover__item-icon{-webkit-box-shadow:0 0 0 1px #eff0f1;box-shadow:0 0 0 1px #eff0f1}.ce-popover__item--focused:not(.ce-popover__item--no-visible-focus){-webkit-box-shadow:inset 0 0 0 1px rgba(7,161,227,.08);box-shadow:inset 0 0 0 1px rgba(7,161,227,.08);background:rgba(34,186,255,.08)!important}.ce-popover__item--hidden{display:none}.ce-popover__item--active{background:rgba(56,138,229,.1);color:#388ae5}.ce-popover__item--confirmation{background:#e24a4a}.ce-popover__item--confirmation .ce-popover__item-icon{color:#e24a4a}.ce-popover__item--confirmation .ce-popover__item-label{color:#fff}@media (hover:hover){.ce-popover__item--confirmation:not(.ce-popover__item--no-visible-hover):hover{background:#ce4343}}.ce-popover__item--confirmation:not(.ce-popover__item--no-visible-focus).ce-popover__item--focused{background:#ce4343!important}.ce-popover__item-icon{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;width:26px;height:26px;-webkit-box-shadow:0 0 0 1px rgba(201,201,204,.48);box-shadow:0 0 0 1px rgba(201,201,204,.48);border-radius:5px;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;background:#fff;-webkit-box-sizing:content-box;box-sizing:content-box;-ms-flex-negative:0;flex-shrink:0;margin-right:10px}.ce-popover__item-icon svg{width:20px;height:20px}@media (max-width:650px){.ce-popover__item-icon{width:36px;height:36px;border-radius:8px}.ce-popover__item-icon svg{width:28px;height:28px}}.ce-popover__item-label{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.ce-popover__item-label:after{content:"";width:25px;display:inline-block}.ce-popover__item-secondary-label{color:#707684;font-size:12px;margin-left:auto;white-space:nowrap;letter-spacing:-.1em;padding-right:5px;margin-bottom:-2px;opacity:.6}@media (max-width:650px){.ce-popover__item-secondary-label{display:none}}.ce-popover__item--active .ce-popover__item-icon,.ce-popover__item--confirmation .ce-popover__item-icon,.ce-popover__item--focused .ce-popover__item-icon{-webkit-box-shadow:none;box-shadow:none}.ce-popover__no-found{display:grid;grid-template-columns:auto auto 1fr;grid-template-rows:auto;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:start;white-space:nowrap;padding:3px;font-size:14px;line-height:20px;font-weight:500;cursor:pointer;-webkit-box-align:center;-ms-flex-align:center;align-items:center;border-radius:6px}.ce-popover__no-found:not(:last-of-type){margin-bottom:1px}@media (max-width:650px){.ce-popover__no-found{font-size:16px;padding:4px}}.ce-popover__no-found{color:#707684;display:none;cursor:default}.ce-popover__no-found--shown{display:block}@media (max-width:650px){.ce-popover__overlay{position:fixed;top:0;bottom:0;left:0;right:0;background:#1d202b;opacity:.5;z-index:3;-webkit-transition:opacity .12s ease-in;transition:opacity .12s ease-in;will-change:opacity;visibility:visible}.ce-popover .cdx-search-field{display:none}}.ce-popover__overlay--hidden{z-index:0;opacity:0;visibility:hidden}.ce-popover__custom-content:not(:empty){padding:4px}@media (min-width:651px){.ce-popover__custom-content:not(:empty){margin-top:5px;padding:0}}.ce-popover__custom-content--hidden{display:none}.cdx-search-field{--icon-margin-right:10px;background:rgba(232,232,235,.49);border:1px solid rgba(226,226,229,.2);border-radius:6px;padding:2px;display:grid;grid-template-columns:auto auto 1fr;grid-template-rows:auto}.cdx-search-field__icon{width:26px;height:26px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;margin-right:var(--icon-margin-right)}.cdx-search-field__icon svg{width:20px;height:20px;color:#707684}.cdx-search-field__input{font-size:14px;outline:none;font-weight:500;font-family:inherit;border:0;background:transparent;margin:0;padding:0;line-height:22px;min-width:calc(100% - 26px - var(--icon-margin-right))}.cdx-search-field__input::-webkit-input-placeholder{color:#707684;font-weight:500}.cdx-search-field__input::-moz-placeholder{color:#707684;font-weight:500}.cdx-search-field__input:-ms-input-placeholder{color:#707684;font-weight:500}.cdx-search-field__input::-ms-input-placeholder{color:#707684;font-weight:500}.cdx-search-field__input::placeholder{color:#707684;font-weight:500}'},function(e,t,n){"use strict";n.r(t),n.d(t,"nanoid",(function(){return s})),n.d(t,"customAlphabet",(function(){return a})),n.d(t,"customRandom",(function(){return i})),n.d(t,"urlAlphabet",(function(){return o})),n.d(t,"random",(function(){return r}));let o="useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";let r=e=>crypto.getRandomValues(new Uint8Array(e)),i=(e,t,n)=>{let o=(2<{let i="";for(;;){let a=n(r),s=r;for(;s--;)if(i+=e[a[s]&o]||"",i.length===t)return i}}},a=(e,t)=>i(e,t,r),s=(e=21)=>{let t="",n=crypto.getRandomValues(new Uint8Array(e));for(;e--;){let o=63&n[e];t+=o<36?o.toString(36):o<62?(o-26).toString(36).toUpperCase():o<63?"_":"-"}return t}}])})); \ No newline at end of file diff --git a/src/webui/public/js/compact/editor.table.min.js b/src/webui/public/js/compact/editor.table.min.js new file mode 100644 index 0000000..5ba5aef --- /dev/null +++ b/src/webui/public/js/compact/editor.table.min.js @@ -0,0 +1,8 @@ +/** + * Skipped minification because the original files appears to be already minified. + * Original file: /npm/@editorjs/table@2.4.5/dist/table.umd.js + * + * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files + */ +(function(){var r;"use strict";try{if(typeof document<"u"){var o=document.createElement("style");o.nonce=(r=document.head.querySelector("meta[property=csp-nonce]"))==null?void 0:r.content,o.appendChild(document.createTextNode('.tc-wrap{--color-background:#f9f9fb;--color-text-secondary:#7b7e89;--color-border:#e8e8eb;--cell-size:34px;--toolbox-icon-size:18px;--toolbox-padding:6px;--toolbox-aiming-field-size:calc(var(--toolbox-icon-size) + var(--toolbox-padding)*2);border-left:0;position:relative;height:100%;width:100%;margin-top:var(--toolbox-icon-size);box-sizing:border-box;display:grid;grid-template-columns:calc(100% - var(--cell-size)) var(--cell-size);z-index:0}.tc-wrap--readonly{grid-template-columns:100% var(--cell-size)}.tc-wrap svg{vertical-align:top}@media print{.tc-wrap{border-left-color:var(--color-border);border-left-style:solid;border-left-width:1px;grid-template-columns:100% var(--cell-size)}}@media print{.tc-wrap .tc-row:after{display:none}}.tc-table{position:relative;width:100%;height:100%;display:grid;font-size:14px;border-top:1px solid var(--color-border);line-height:1.4}.tc-table:after{width:calc(var(--cell-size));height:100%;left:calc(var(--cell-size)*-1);top:0}.tc-table:after,.tc-table:before{position:absolute;content:""}.tc-table:before{width:100%;height:var(--toolbox-aiming-field-size);top:calc(var(--toolbox-aiming-field-size)*-1);left:0}.tc-table--heading .tc-row:first-child{font-weight:600;border-bottom:2px solid var(--color-border);position:sticky;top:0;z-index:2;background:var(--color-background)}.tc-table--heading .tc-row:first-child [contenteditable]:empty:before{content:attr(heading);color:var(--color-text-secondary)}.tc-table--heading .tc-row:first-child:after{bottom:-2px;border-bottom:2px solid var(--color-border)}.tc-add-column,.tc-add-row{display:flex;color:var(--color-text-secondary)}@media print{.tc-add{display:none}}.tc-add-column{display:grid;border-top:1px solid var(--color-border);grid-template-columns:var(--cell-size);grid-auto-rows:var(--cell-size);place-items:center}.tc-add-column svg{padding:5px;position:sticky;top:0;background-color:var(--color-background)}.tc-add-column--disabled{visibility:hidden}@media print{.tc-add-column{display:none}}.tc-add-row{height:var(--cell-size);align-items:center;padding-left:4px;position:relative}.tc-add-row--disabled{display:none}.tc-add-row:before{content:"";position:absolute;right:calc(var(--cell-size)*-1);width:var(--cell-size);height:100%}@media print{.tc-add-row{display:none}}.tc-add-column,.tc-add-row{transition:0s;cursor:pointer;will-change:background-color}.tc-add-column:hover,.tc-add-row:hover{transition:background-color .1s ease;background-color:var(--color-background)}.tc-add-row{margin-top:1px}.tc-add-row:hover:before{transition:.1s;background-color:var(--color-background)}.tc-row{display:grid;grid-template-columns:repeat(auto-fit,minmax(10px,1fr));position:relative;border-bottom:1px solid var(--color-border)}.tc-row:after{content:"";pointer-events:none;position:absolute;width:var(--cell-size);height:100%;bottom:-1px;right:calc(var(--cell-size)*-1);border-bottom:1px solid var(--color-border)}.tc-row--selected{background:var(--color-background)}.tc-row--selected:after{background:var(--color-background)}.tc-cell{border-right:1px solid var(--color-border);padding:6px 12px;overflow:hidden;outline:none;line-break:normal}.tc-cell--selected{background:var(--color-background)}.tc-wrap--readonly .tc-row:after{display:none}.tc-toolbox{--toolbox-padding:6px;--popover-margin:30px;--toggler-click-zone-size:30px;--toggler-dots-color:#7b7e89;--toggler-dots-color-hovered:#1d202b;position:absolute;cursor:pointer;z-index:1;opacity:0;transition:opacity .1s;will-change:left,opacity}.tc-toolbox--column{top:calc(var(--toggler-click-zone-size)*-1);transform:translate(calc(var(--toggler-click-zone-size)*-1/2));will-change:left,opacity}.tc-toolbox--row{left:calc(var(--popover-margin)*-1);transform:translateY(calc(var(--toggler-click-zone-size)*-1/2));margin-top:-1px;will-change:top,opacity}.tc-toolbox--showed{opacity:1}.tc-toolbox .tc-popover{position:absolute;top:0;left:var(--popover-margin)}.tc-toolbox__toggler{display:flex;align-items:center;justify-content:center;width:var(--toggler-click-zone-size);height:var(--toggler-click-zone-size);color:var(--toggler-dots-color);opacity:0;transition:opacity .15s ease;will-change:opacity}.tc-toolbox__toggler:hover{color:var(--toggler-dots-color-hovered)}.tc-toolbox__toggler svg{fill:currentColor}.tc-wrap:hover .tc-toolbox__toggler{opacity:1}.tc-settings .cdx-settings-button{width:50%;margin:0}.tc-popover{--color-border:#eaeaea;--color-background:#fff;--color-background-hover:rgba(232,232,235,.49);--color-background-confirm:#e24a4a;--color-background-confirm-hover:#d54040;--color-text-confirm:#fff;background:var(--color-background);border:1px solid var(--color-border);box-shadow:0 3px 15px -3px #0d142121;border-radius:6px;padding:6px;display:none;will-change:opacity,transform}.tc-popover--opened{display:block;animation:menuShowing .1s cubic-bezier(.215,.61,.355,1) forwards}.tc-popover__item{display:flex;align-items:center;padding:2px 14px 2px 2px;border-radius:5px;cursor:pointer;white-space:nowrap;-webkit-user-select:none;-moz-user-select:none;user-select:none}.tc-popover__item:hover{background:var(--color-background-hover)}.tc-popover__item:not(:last-of-type){margin-bottom:2px}.tc-popover__item-icon{display:inline-flex;width:26px;height:26px;align-items:center;justify-content:center;background:var(--color-background);border-radius:5px;border:1px solid var(--color-border);margin-right:8px}.tc-popover__item-label{line-height:22px;font-size:14px;font-weight:500}.tc-popover__item--confirm{background:var(--color-background-confirm);color:var(--color-text-confirm)}.tc-popover__item--confirm:hover{background-color:var(--color-background-confirm-hover)}.tc-popover__item--confirm .tc-popover__item-icon{background:var(--color-background-confirm);border-color:#0000001a}.tc-popover__item--confirm .tc-popover__item-icon svg{transition:transform .2s ease-in;transform:rotate(90deg) scale(1.2)}.tc-popover__item--hidden{display:none}@keyframes menuShowing{0%{opacity:0;transform:translateY(-8px) scale(.9)}70%{opacity:1;transform:translateY(2px)}to{transform:translateY(0)}}')),document.head.appendChild(o)}}catch(e){console.error("vite-plugin-css-injected-by-js",e)}})(); +(function(d,p){typeof exports=="object"&&typeof module<"u"?module.exports=p():typeof define=="function"&&define.amd?define(p):(d=typeof globalThis<"u"?globalThis:d||self,d.Table=p())})(this,function(){"use strict";function d(a,t,e={}){const o=document.createElement(a);Array.isArray(t)?o.classList.add(...t):t&&o.classList.add(t);for(const i in e)Object.prototype.hasOwnProperty.call(e,i)&&(o[i]=e[i]);return o}function p(a){const t=a.getBoundingClientRect();return{y1:Math.floor(t.top+window.pageYOffset),x1:Math.floor(t.left+window.pageXOffset),x2:Math.floor(t.right+window.pageXOffset),y2:Math.floor(t.bottom+window.pageYOffset)}}function g(a,t){const e=p(a),o=p(t);return{fromTopBorder:o.y1-e.y1,fromLeftBorder:o.x1-e.x1,fromRightBorder:e.x2-o.x2,fromBottomBorder:e.y2-o.y2}}function k(a,t){const e=a.getBoundingClientRect(),{width:o,height:i,x:n,y:r}=e,{clientX:h,clientY:l}=t;return{width:o,height:i,x:h-n,y:l-r}}function m(a,t){return t.parentNode.insertBefore(a,t)}function C(a,t=!0){const e=document.createRange(),o=window.getSelection();e.selectNodeContents(a),e.collapse(t),o.removeAllRanges(),o.addRange(e)}class c{constructor({items:t}){this.items=t,this.wrapper=void 0,this.itemEls=[]}static get CSS(){return{popover:"tc-popover",popoverOpened:"tc-popover--opened",item:"tc-popover__item",itemHidden:"tc-popover__item--hidden",itemConfirmState:"tc-popover__item--confirm",itemIcon:"tc-popover__item-icon",itemLabel:"tc-popover__item-label"}}render(){return this.wrapper=d("div",c.CSS.popover),this.items.forEach((t,e)=>{const o=d("div",c.CSS.item),i=d("div",c.CSS.itemIcon,{innerHTML:t.icon}),n=d("div",c.CSS.itemLabel,{textContent:t.label});o.dataset.index=e,o.appendChild(i),o.appendChild(n),this.wrapper.appendChild(o),this.itemEls.push(o)}),this.wrapper.addEventListener("click",t=>{this.popoverClicked(t)}),this.wrapper}popoverClicked(t){const e=t.target.closest(`.${c.CSS.item}`);if(!e)return;const o=e.dataset.index,i=this.items[o];if(i.confirmationRequired&&!this.hasConfirmationState(e)){this.setConfirmationState(e);return}i.onClick()}setConfirmationState(t){t.classList.add(c.CSS.itemConfirmState)}clearConfirmationState(t){t.classList.remove(c.CSS.itemConfirmState)}hasConfirmationState(t){return t.classList.contains(c.CSS.itemConfirmState)}get opened(){return this.wrapper.classList.contains(c.CSS.popoverOpened)}open(){this.items.forEach((t,e)=>{typeof t.hideIf=="function"&&this.itemEls[e].classList.toggle(c.CSS.itemHidden,t.hideIf())}),this.wrapper.classList.add(c.CSS.popoverOpened)}close(){this.wrapper.classList.remove(c.CSS.popoverOpened),this.itemEls.forEach(t=>{this.clearConfirmationState(t)})}}const R='',b='',x='',S='',y='',L='',M='',v='',O='',T='',H='',A='';class w{constructor({api:t,items:e,onOpen:o,onClose:i,cssModifier:n=""}){this.api=t,this.items=e,this.onOpen=o,this.onClose=i,this.cssModifier=n,this.popover=null,this.wrapper=this.createToolbox()}static get CSS(){return{toolbox:"tc-toolbox",toolboxShowed:"tc-toolbox--showed",toggler:"tc-toolbox__toggler"}}get element(){return this.wrapper}createToolbox(){const t=d("div",[w.CSS.toolbox,this.cssModifier?`${w.CSS.toolbox}--${this.cssModifier}`:""]);t.dataset.mutationFree="true";const e=this.createPopover(),o=this.createToggler();return t.appendChild(o),t.appendChild(e),t}createToggler(){const t=d("div",w.CSS.toggler,{innerHTML:M});return t.addEventListener("click",()=>{this.togglerClicked()}),t}createPopover(){return this.popover=new c({items:this.items}),this.popover.render()}togglerClicked(){this.popover.opened?(this.popover.close(),this.onClose()):(this.popover.open(),this.onOpen())}show(t){const e=t();Object.entries(e).forEach(([o,i])=>{this.wrapper.style[o]=i}),this.wrapper.classList.add(w.CSS.toolboxShowed)}hide(){this.popover.close(),this.wrapper.classList.remove(w.CSS.toolboxShowed)}}function B(a,t){let e=0;return function(...o){const i=new Date().getTime();if(!(i-e{const r=n.target.closest(`.${s.table}`)!==null,h=n.target.closest(`.${s.wrapper}`)===null;(r||h)&&this.hideToolboxes();const u=n.target.closest(`.${s.addRow}`),f=n.target.closest(`.${s.addColumn}`);u&&u.parentNode===this.wrapper?(this.addRow(void 0,!0),this.hideToolboxes()):f&&f.parentNode===this.wrapper&&(this.addColumn(void 0,!0),this.hideToolboxes())},this.readOnly||this.bindEvents()}getWrapper(){return this.wrapper}bindEvents(){document.addEventListener("click",this.documentClicked),this.table.addEventListener("mousemove",B(150,t=>this.onMouseMoveInTable(t)),{passive:!0}),this.table.onkeypress=t=>this.onKeyPressListener(t),this.table.addEventListener("keydown",t=>this.onKeyDownListener(t)),this.table.addEventListener("focusin",t=>this.focusInTableListener(t))}createColumnToolbox(){return new w({api:this.api,cssModifier:"column",items:[{label:this.api.i18n.t("Add column to left"),icon:S,hideIf:()=>this.numberOfColumns===this.config.maxcols,onClick:()=>{this.addColumn(this.selectedColumn,!0),this.hideToolboxes()}},{label:this.api.i18n.t("Add column to right"),icon:y,hideIf:()=>this.numberOfColumns===this.config.maxcols,onClick:()=>{this.addColumn(this.selectedColumn+1,!0),this.hideToolboxes()}},{label:this.api.i18n.t("Delete column"),icon:b,hideIf:()=>this.numberOfColumns===1,confirmationRequired:!0,onClick:()=>{this.deleteColumn(this.selectedColumn),this.hideToolboxes()}}],onOpen:()=>{this.selectColumn(this.hoveredColumn),this.hideRowToolbox()},onClose:()=>{this.unselectColumn()}})}createRowToolbox(){return new w({api:this.api,cssModifier:"row",items:[{label:this.api.i18n.t("Add row above"),icon:L,hideIf:()=>this.numberOfRows===this.config.maxrows,onClick:()=>{this.addRow(this.selectedRow,!0),this.hideToolboxes()}},{label:this.api.i18n.t("Add row below"),icon:x,hideIf:()=>this.numberOfRows===this.config.maxrows,onClick:()=>{this.addRow(this.selectedRow+1,!0),this.hideToolboxes()}},{label:this.api.i18n.t("Delete row"),icon:b,hideIf:()=>this.numberOfRows===1,confirmationRequired:!0,onClick:()=>{this.deleteRow(this.selectedRow),this.hideToolboxes()}}],onOpen:()=>{this.selectRow(this.hoveredRow),this.hideColumnToolbox()},onClose:()=>{this.unselectRow()}})}moveCursorToNextRow(){this.focusedCell.row!==this.numberOfRows?(this.focusedCell.row+=1,this.focusCell(this.focusedCell)):(this.addRow(),this.focusedCell.row+=1,this.focusCell(this.focusedCell),this.updateToolboxesPosition(0,0))}getCell(t,e){return this.table.querySelectorAll(`.${s.row}:nth-child(${t}) .${s.cell}`)[e-1]}getRow(t){return this.table.querySelector(`.${s.row}:nth-child(${t})`)}getRowByCell(t){return t.parentElement}getRowFirstCell(t){return t.querySelector(`.${s.cell}:first-child`)}setCellContent(t,e,o){const i=this.getCell(t,e);i.innerHTML=o}addColumn(t=-1,e=!1){var n;let o=this.numberOfColumns;if(this.config&&this.config.maxcols&&this.numberOfColumns>=this.config.maxcols)return;for(let r=1;r<=this.numberOfRows;r++){let h;const l=this.createCell();if(t>0&&t<=o?(h=this.getCell(r,t),m(l,h)):h=this.getRow(r).appendChild(l),r===1){const u=this.getCell(r,t>0?t:o+1);u&&e&&C(u)}}const i=this.wrapper.querySelector(`.${s.addColumn}`);(n=this.config)!=null&&n.maxcols&&this.numberOfColumns>this.config.maxcols-1&&i&&i.classList.add(s.addColumnDisabled),this.addHeadingAttrToFirstRow()}addRow(t=-1,e=!1){let o,i=d("div",s.row);this.tunes.withHeadings&&this.removeHeadingAttrFromFirstRow();let n=this.numberOfColumns;if(this.config&&this.config.maxrows&&this.numberOfRows>=this.config.maxrows&&h)return;if(t>0&&t<=this.numberOfRows){let l=this.getRow(t);o=m(i,l)}else o=this.table.appendChild(i);this.fillRow(o,n),this.tunes.withHeadings&&this.addHeadingAttrToFirstRow();const r=this.getRowFirstCell(o);r&&e&&C(r);const h=this.wrapper.querySelector(`.${s.addRow}`);return this.config&&this.config.maxrows&&this.numberOfRows>=this.config.maxrows&&h&&h.classList.add(s.addRowDisabled),o}deleteColumn(t){for(let o=1;o<=this.numberOfRows;o++){const i=this.getCell(o,t);if(!i)return;i.remove()}const e=this.wrapper.querySelector(`.${s.addColumn}`);e&&e.classList.remove(s.addColumnDisabled)}deleteRow(t){this.getRow(t).remove();const e=this.wrapper.querySelector(`.${s.addRow}`);e&&e.classList.remove(s.addRowDisabled),this.addHeadingAttrToFirstRow()}createTableWrapper(){if(this.wrapper=d("div",s.wrapper),this.table=d("div",s.table),this.readOnly&&this.wrapper.classList.add(s.wrapperReadOnly),this.wrapper.appendChild(this.toolboxRow.element),this.wrapper.appendChild(this.toolboxColumn.element),this.wrapper.appendChild(this.table),!this.readOnly){const t=d("div",s.addColumn,{innerHTML:v}),e=d("div",s.addRow,{innerHTML:v});this.wrapper.appendChild(t),this.wrapper.appendChild(e)}}computeInitialSize(){const t=this.data&&this.data.content,e=Array.isArray(t),o=e?t.length:!1,i=e?t.length:void 0,n=o?t[0].length:void 0,r=Number.parseInt(this.config&&this.config.rows),h=Number.parseInt(this.config&&this.config.cols),l=!isNaN(r)&&r>0?r:void 0,u=!isNaN(h)&&h>0?h:void 0;return{rows:i||l||2,cols:n||u||2}}resize(){const{rows:t,cols:e}=this.computeInitialSize();for(let o=0;o0&&e<=this.numberOfColumns&&this.toolboxColumn.show(()=>({left:`calc((100% - var(--cell-size)) / (${this.numberOfColumns} * 2) * (1 + (${e} - 1) * 2))`})),this.isRowMenuShowing||t>0&&t<=this.numberOfRows&&this.toolboxRow.show(()=>{const o=this.getRow(t),{fromTopBorder:i}=g(this.table,o),{height:n}=o.getBoundingClientRect();return{top:`${Math.ceil(i+n/2)}px`}})}setHeadingsSetting(t){this.tunes.withHeadings=t,t?(this.table.classList.add(s.withHeadings),this.addHeadingAttrToFirstRow()):(this.table.classList.remove(s.withHeadings),this.removeHeadingAttrFromFirstRow())}addHeadingAttrToFirstRow(){for(let t=1;t<=this.numberOfColumns;t++){let e=this.getCell(1,t);e&&e.setAttribute("heading",this.api.i18n.t("Heading"))}}removeHeadingAttrFromFirstRow(){for(let t=1;t<=this.numberOfColumns;t++){let e=this.getCell(1,t);e&&e.removeAttribute("heading")}}selectRow(t){const e=this.getRow(t);e&&(this.selectedRow=t,e.classList.add(s.rowSelected))}unselectRow(){if(this.selectedRow<=0)return;const t=this.table.querySelector(`.${s.rowSelected}`);t&&t.classList.remove(s.rowSelected),this.selectedRow=0}selectColumn(t){for(let e=1;e<=this.numberOfRows;e++){const o=this.getCell(e,t);o&&o.classList.add(s.cellSelected)}this.selectedColumn=t}unselectColumn(){if(this.selectedColumn<=0)return;let t=this.table.querySelectorAll(`.${s.cellSelected}`);Array.from(t).forEach(e=>{e.classList.remove(s.cellSelected)}),this.selectedColumn=0}getHoveredCell(t){let e=this.hoveredRow,o=this.hoveredColumn;const{width:i,height:n,x:r,y:h}=k(this.table,t);return r>=0&&(o=this.binSearch(this.numberOfColumns,l=>this.getCell(1,l),({fromLeftBorder:l})=>rr>i-l)),h>=0&&(e=this.binSearch(this.numberOfRows,l=>this.getCell(l,1),({fromTopBorder:l})=>hh>n-l)),{row:e||this.hoveredRow,column:o||this.hoveredColumn}}binSearch(t,e,o,i){let n=0,r=t+1,h=0,l;for(;n!r.textContent.trim())||t.push(i.map(r=>r.innerHTML))}return t}destroy(){document.removeEventListener("click",this.documentClicked)}}class ${static get isReadOnlySupported(){return!0}static get enableLineBreaks(){return!0}constructor({data:t,config:e,api:o,readOnly:i,block:n}){this.api=o,this.readOnly=i,this.config=e,this.data={withHeadings:this.getConfig("withHeadings",!1,t),stretched:this.getConfig("stretched",!1,t),content:t&&t.content?t.content:[]},this.table=null,this.block=n}static get toolbox(){return{icon:A,title:"Table"}}render(){return this.table=new E(this.readOnly,this.api,this.data,this.config),this.container=d("div",this.api.styles.block),this.container.appendChild(this.table.getWrapper()),this.table.setHeadingsSetting(this.data.withHeadings),this.container}renderSettings(){return[{label:this.api.i18n.t("With headings"),icon:T,isActive:this.data.withHeadings,closeOnActivate:!0,toggle:!0,onActivate:()=>{this.data.withHeadings=!0,this.table.setHeadingsSetting(this.data.withHeadings)}},{label:this.api.i18n.t("Without headings"),icon:H,isActive:!this.data.withHeadings,closeOnActivate:!0,toggle:!0,onActivate:()=>{this.data.withHeadings=!1,this.table.setHeadingsSetting(this.data.withHeadings)}},{label:this.data.stretched?this.api.i18n.t("Collapse"):this.api.i18n.t("Stretch"),icon:this.data.stretched?R:O,closeOnActivate:!0,toggle:!0,onActivate:()=>{this.data.stretched=!this.data.stretched,this.block.stretched=this.data.stretched}}]}save(){const t=this.table.getData();return{withHeadings:this.data.withHeadings,stretched:this.data.stretched,content:t}}destroy(){this.table.destroy()}getConfig(t,e=void 0,o=void 0){const i=this.data||o;return i?i[t]?i[t]:e:this.config&&this.config[t]?this.config[t]:e}static get pasteConfig(){return{tags:["TABLE","TR","TH","TD"]}}onPaste(t){const e=t.detail.data,o=e.querySelector(":scope > thead, tr:first-of-type th"),n=Array.from(e.querySelectorAll("tr")).map(r=>Array.from(r.querySelectorAll("th, td")).map(l=>l.innerHTML));this.data={withHeadings:o!==null,content:n},this.table.wrapper&&this.table.wrapper.replaceWith(this.render())}}const I="";return $}); \ No newline at end of file diff --git a/src/webui/public/js/playlist-schedule.js b/src/webui/public/js/playlist-schedule.js new file mode 100644 index 0000000..75e3c6f --- /dev/null +++ b/src/webui/public/js/playlist-schedule.js @@ -0,0 +1,527 @@ +(function () { + var DAY_NAMES = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; + + function formatDays(daysValue) { + var days = []; + if (Array.isArray(daysValue)) { + days = daysValue; + } else if (daysValue) { + try { + var parsedDays = JSON.parse(daysValue); + days = Array.isArray(parsedDays) ? parsedDays : []; + } catch (_error) { + days = []; + } + } + return days + .map(function (day) { + return DAY_NAMES[Number(day)] || ''; + }) + .filter(Boolean) + .join(', '); + } + + function initPlaylistScheduleModal() { + var dialog = document.getElementById('slide-schedule-dialog'); + var frame = document.getElementById('slide-schedule-frame'); + var closeButton = document.getElementById('slide-schedule-close'); + var triggers = document.querySelectorAll('[data-schedule-config]'); + + if (!dialog || !frame || !closeButton || !triggers.length) { + return; + } + + function openScheduleModal(url) { + frame.src = url; + if (typeof dialog.showModal === 'function') { + dialog.showModal(); + } else { + dialog.setAttribute('open', 'open'); + } + } + + function resizeScheduleFrame() { + try { + if (!frame.contentWindow || !frame.contentWindow.document) { + return; + } + var doc = frame.contentWindow.document; + var height = Math.max( + doc.body.scrollHeight, + doc.documentElement.scrollHeight, + doc.body.offsetHeight, + doc.documentElement.offsetHeight + ); + frame.style.height = height + 'px'; + } catch (_error) { + frame.style.height = '70vh'; + } + } + + function closeScheduleModal() { + frame.src = 'about:blank'; + if (typeof dialog.close === 'function') { + dialog.close(); + } else { + dialog.removeAttribute('open'); + } + } + + window.openScheduleModal = openScheduleModal; + window.resizeScheduleFrame = resizeScheduleFrame; + window.closeScheduleModal = closeScheduleModal; + + Array.prototype.forEach.call(triggers, function (trigger) { + trigger.addEventListener('click', function () { + var url = trigger.getAttribute('data-schedule-config'); + var rowKey = trigger.getAttribute('data-schedule-config-row') || ''; + if (rowKey && url.indexOf('row_key=') === -1) { + url += (url.indexOf('?') === -1 ? '?' : '&') + 'row_key=' + encodeURIComponent(rowKey); + } + openScheduleModal(url); + }); + }); + + frame.addEventListener('load', resizeScheduleFrame); + closeButton.addEventListener('click', closeScheduleModal); + } + + function scheduleSummary(values) { + var mode = String(values.schedule_mode || 'always'); + if (mode === 'dates') { + if (values.schedule_start_datetime && values.schedule_end_datetime) { + return 'Dates: ' + values.schedule_start_datetime.replace('T', ' ') + ' to ' + values.schedule_end_datetime.replace('T', ' '); + } + return 'Dates: not set'; + } + if (mode === 'times') { + var days = formatDays(values.schedule_days_json); + if (days && values.schedule_start_time && values.schedule_end_time) { + return 'Times: ' + days + ' ' + values.schedule_start_time.slice(0, 5) + '-' + values.schedule_end_time.slice(0, 5); + } + return 'Times: not set'; + } + return 'Always visible'; + } + + function initPlaylistScheduleForm() { + var form = document.querySelector('form[action*="/config"]'); + var select = document.getElementById('schedule-mode-select'); + if (!form || !select) { + return; + } + + var datesPanel = document.getElementById('schedule-dates-panel'); + var timesPanel = document.getElementById('schedule-times-panel'); + var startDateInput = form.querySelector('[name="schedule_start_datetime"]'); + var endDateInput = form.querySelector('[name="schedule_end_datetime"]'); + var startTimeInput = form.querySelector('[name="schedule_start_time"]'); + var endTimeInput = form.querySelector('[name="schedule_end_time"]'); + var rowKeyInput = form.querySelector('[name="row_key"]'); + var dayCheckboxes = form.querySelectorAll('[name="schedule_days"]'); + + function notifyParentResize() { + if (window.parent && typeof window.parent.resizeScheduleFrame === 'function') { + window.parent.resizeScheduleFrame(); + } + } + + function updateVisibility() { + var mode = select.value; + datesPanel.style.display = mode === 'dates' ? '' : 'none'; + timesPanel.style.display = mode === 'times' ? '' : 'none'; + window.requestAnimationFrame(function () { + notifyParentResize(); + }); + } + + select.addEventListener('change', updateVisibility); + updateVisibility(); + + form.addEventListener('submit', function (event) { + event.preventDefault(); + + var mode = select.value; + var datesIncomplete = mode === 'dates' && (!startDateInput.value || !endDateInput.value); + var timesIncomplete = mode === 'times' && (!startTimeInput.value || !endTimeInput.value || !Array.prototype.some.call(dayCheckboxes, function (checkbox) { + return checkbox.checked; + })); + + if (mode === 'dates' && !datesIncomplete) { + if (new Date(endDateInput.value) < new Date(startDateInput.value)) { + alert('End datetime must be after start datetime.'); + return; + } + } + if (mode === 'times' && !timesIncomplete) { + if (endTimeInput.value < startTimeInput.value) { + alert('End time must be after start time.'); + return; + } + } + + if (datesIncomplete || timesIncomplete) { + mode = 'always'; + select.value = 'always'; + startDateInput.value = ''; + endDateInput.value = ''; + startTimeInput.value = ''; + endTimeInput.value = ''; + Array.prototype.forEach.call(dayCheckboxes, function (checkbox) { + checkbox.checked = false; + }); + updateVisibility(); + } + + var selectedDays = []; + Array.prototype.forEach.call(dayCheckboxes, function (checkbox) { + if (checkbox.checked) { + selectedDays.push(Number(checkbox.value)); + } + }); + + var values = { + row_key: String(rowKeyInput && rowKeyInput.value ? rowKeyInput.value : ''), + schedule_mode: mode, + schedule_start_datetime: startDateInput.value || '', + schedule_end_datetime: endDateInput.value || '', + schedule_start_time: startTimeInput.value || '', + schedule_end_time: endTimeInput.value || '', + schedule_days_json: JSON.stringify(selectedDays.sort()), + summary: '' + }; + values.summary = scheduleSummary(values); + + if (window.parent && typeof window.parent.applyPlaylistScheduleConfig === 'function') { + window.parent.applyPlaylistScheduleConfig(values); + if (typeof window.parent.closeScheduleModal === 'function') { + window.parent.closeScheduleModal(); + } + return; + } + + var payload = new URLSearchParams(new FormData(form)); + fetch(form.action, { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' + }, + body: payload.toString(), + credentials: 'same-origin' + }).then(function (response) { + if (!response.ok) { + return response.text().then(function (text) { + throw new Error(text || 'Unable to save schedule.'); + }); + } + if (window.parent && typeof window.parent.closeScheduleModal === 'function') { + window.parent.closeScheduleModal(); + } + }).catch(function (error) { + alert(error.message || 'Unable to save schedule.'); + }); + }); + } + + function initPlaylistEditStaging() { + var tbody = document.getElementById('playlist-items-body'); + var form = document.getElementById('playlist-edit-form'); + var addSection = document.getElementById('playlist-add-section'); + var addSlideForm = document.getElementById('playlist-add-slide-form'); + var addSlideButton = document.getElementById('playlist-add-slide-button'); + var addSlideSelect = document.getElementById('playlist-add-slide-select'); + var addDurationInput = document.getElementById('playlist-add-duration'); + var addSlideEmpty = document.getElementById('playlist-add-slide-empty'); + + if (!tbody || !form) { + return; + } + + function getRows() { + return Array.prototype.slice.call(tbody.querySelectorAll('tr[data-playlist-slide-row]')); + } + + function findRowByKey(rowKey) { + var rows = getRows(); + for (var i = 0; i < rows.length; i += 1) { + if (rows[i].getAttribute('data-row-key') === rowKey) { + return rows[i]; + } + } + return null; + } + + function updateEmptyState() { + var rows = getRows(); + var placeholder = tbody.querySelector('.playlist-empty-row'); + if (rows.length) { + if (placeholder) { + placeholder.remove(); + } + return; + } + if (!placeholder) { + tbody.innerHTML = 'No slides assigned yet.'; + } + } + + function scheduleSummaryForRow(row) { + var modeInput = row.querySelector('[name="schedule_mode[]"]'); + var startDatetime = row.querySelector('[name="schedule_start_datetime[]"]'); + var endDatetime = row.querySelector('[name="schedule_end_datetime[]"]'); + var startTime = row.querySelector('[name="schedule_start_time[]"]'); + var endTime = row.querySelector('[name="schedule_end_time[]"]'); + var daysJson = row.querySelector('[name="schedule_days_json[]"]'); + var mode = String(modeInput ? modeInput.value : 'always'); + var days = formatDays(daysJson && daysJson.value ? daysJson.value : '[]'); + + if (mode === 'dates') { + if (startDatetime && endDatetime && startDatetime.value && endDatetime.value) { + return 'Dates: ' + startDatetime.value.replace('T', ' ') + ' to ' + endDatetime.value.replace('T', ' '); + } + return 'Dates: not set'; + } + if (mode === 'times') { + if (days && startTime && endTime && startTime.value && endTime.value) { + return 'Times: ' + days + ' ' + startTime.value.slice(0, 5) + '-' + endTime.value.slice(0, 5); + } + return 'Times: not set'; + } + return 'Always visible'; + } + + function syncAddSlideOptions() { + if (!addSlideSelect) { + return; + } + var activeSlideIds = {}; + var activeCanvasSignatures = {}; + getRows().forEach(function (row) { + var slideId = String(row.getAttribute('data-slide-id') || ''); + var canvasSignature = String(row.getAttribute('data-canvas-signature') || ''); + if (slideId) { + activeSlideIds[slideId] = true; + } + if (canvasSignature) { + activeCanvasSignatures[canvasSignature] = true; + } + }); + + var allowedCanvasSignature = ''; + var canvasKeys = Object.keys(activeCanvasSignatures); + if (canvasKeys.length === 1) { + allowedCanvasSignature = canvasKeys[0]; + } + + Array.prototype.forEach.call(addSlideSelect.options, function (option) { + if (!option.value) { + return; + } + var optionCanvasSignature = String(option.getAttribute('data-canvas-signature') || ''); + var isActive = Boolean(activeSlideIds[String(option.value)]); + var isCanvasMismatch = Boolean(allowedCanvasSignature && optionCanvasSignature && optionCanvasSignature !== allowedCanvasSignature); + option.disabled = isActive || isCanvasMismatch; + option.hidden = isActive || isCanvasMismatch; + }); + if ( + addSlideSelect.options[addSlideSelect.selectedIndex] + && (addSlideSelect.options[addSlideSelect.selectedIndex].disabled || addSlideSelect.options[addSlideSelect.selectedIndex].hidden) + ) { + for (var i = 0; i < addSlideSelect.options.length; i += 1) { + if (!addSlideSelect.options[i].disabled && !addSlideSelect.options[i].hidden) { + addSlideSelect.selectedIndex = i; + break; + } + } + } + + var availableCount = 0; + Array.prototype.forEach.call(addSlideSelect.options, function (option) { + if (!option.value) { + return; + } + if (!option.disabled && !option.hidden) { + availableCount += 1; + } + }); + + if (addSlideButton) { + addSlideButton.disabled = availableCount === 0; + } + if (addSlideSelect) { + addSlideSelect.disabled = availableCount === 0; + } + if (addDurationInput) { + addDurationInput.disabled = availableCount === 0; + } + if (addSlideEmpty) { + addSlideEmpty.style.display = availableCount === 0 ? '' : 'none'; + } + if (addSection && addSlideForm && addSlideSelect && addSlideButton) { + var controlsVisible = availableCount > 0; + addSlideForm.style.display = controlsVisible ? '' : 'none'; + } + } + + function updateRowOrder() { + var rows = getRows(); + rows.forEach(function (row, index) { + var orderCell = row.querySelector('.playlist-order-cell'); + var moveUp = row.querySelector('[data-playlist-move="up"]'); + var moveDown = row.querySelector('[data-playlist-move="down"]'); + if (orderCell) { + orderCell.textContent = String(index + 1); + } + if (moveUp) { + moveUp.disabled = index === 0; + } + if (moveDown) { + moveDown.disabled = index === rows.length - 1; + } + }); + syncAddSlideOptions(); + updateEmptyState(); + } + + function setRowSchedule(row, values) { + var modeInput = row.querySelector('[name="schedule_mode[]"]'); + var startDatetime = row.querySelector('[name="schedule_start_datetime[]"]'); + var endDatetime = row.querySelector('[name="schedule_end_datetime[]"]'); + var startTime = row.querySelector('[name="schedule_start_time[]"]'); + var endTime = row.querySelector('[name="schedule_end_time[]"]'); + var daysJson = row.querySelector('[name="schedule_days_json[]"]'); + var summary = row.querySelector('.playlist-schedule-summary'); + + if (modeInput) { + modeInput.value = values.schedule_mode || 'always'; + } + if (startDatetime) { + startDatetime.value = values.schedule_start_datetime || ''; + } + if (endDatetime) { + endDatetime.value = values.schedule_end_datetime || ''; + } + if (startTime) { + startTime.value = values.schedule_start_time || ''; + } + if (endTime) { + endTime.value = values.schedule_end_time || ''; + } + if (daysJson) { + daysJson.value = values.schedule_days_json || '[]'; + } + if (summary) { + summary.textContent = values.summary || scheduleSummaryForRow(row); + } + } + + function createRow(values) { + var row = document.createElement('tr'); + var rowKey = values.row_key || ('new-' + Date.now() + '-' + Math.random().toString(36).slice(2)); + row.setAttribute('data-playlist-slide-row', ''); + row.setAttribute('data-row-key', rowKey); + row.setAttribute('data-slide-id', String(values.slide_id)); + row.setAttribute('data-canvas-signature', String(values.canvas_signature || '')); + row.innerHTML = '' + + '' + + '' + values.title + '' + + '' + + '' + + '
    ' + values.summary + '
    ' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '
    ' + + '' + + '' + + '' + + '' + + '
    '; + return row; + } + + tbody.addEventListener('click', function (event) { + var moveButton = event.target.closest('[data-playlist-move]'); + var removeButton = event.target.closest('[data-playlist-remove-row]'); + var scheduleButton = event.target.closest('[data-schedule-config]'); + var row = event.target.closest('tr[data-playlist-slide-row]'); + + if (!row) { + return; + } + + if (moveButton) { + event.preventDefault(); + var direction = moveButton.getAttribute('data-playlist-move'); + if (direction === 'up' && row.previousElementSibling && row.previousElementSibling.matches('[data-playlist-slide-row]')) { + tbody.insertBefore(row, row.previousElementSibling); + } else if (direction === 'down' && row.nextElementSibling && row.nextElementSibling.matches('[data-playlist-slide-row]')) { + tbody.insertBefore(row.nextElementSibling, row); + } + updateRowOrder(); + return; + } + + if (removeButton) { + event.preventDefault(); + row.remove(); + updateRowOrder(); + return; + } + + if (scheduleButton) { + event.preventDefault(); + if (typeof window.openScheduleModal === 'function') { + window.openScheduleModal(scheduleButton.getAttribute('data-schedule-config') + '?row_key=' + encodeURIComponent(row.getAttribute('data-row-key') || '')); + } + } + }); + + if (addSlideButton && addSlideSelect && addDurationInput) { + addSlideButton.addEventListener('click', function () { + var selectedOption = addSlideSelect.options[addSlideSelect.selectedIndex]; + if (!selectedOption || selectedOption.disabled) { + return; + } + var placeholder = tbody.querySelector('.playlist-empty-row'); + if (placeholder) { + placeholder.remove(); + } + var row = createRow({ + row_key: 'new-' + Date.now(), + slide_id: String(selectedOption.value), + canvas_signature: String(selectedOption.getAttribute('data-canvas-signature') || ''), + title: selectedOption.textContent || 'Slide', + duration_seconds: Math.max(1, Number(addDurationInput.value || 10)), + schedule_mode: 'always', + schedule_start_datetime: '', + schedule_end_datetime: '', + schedule_start_time: '', + schedule_end_time: '', + schedule_days_json: '[]', + summary: 'Always visible' + }); + tbody.appendChild(row); + updateRowOrder(); + }); + } + + window.applyPlaylistScheduleConfig = function (values) { + var row = findRowByKey(String(values.row_key || '')); + if (!row) { + return; + } + setRowSchedule(row, values); + }; + + updateRowOrder(); + } + + initPlaylistScheduleModal(); + initPlaylistScheduleForm(); + initPlaylistEditStaging(); +}()); diff --git a/src/webui/public/js/slide-form.js b/src/webui/public/js/slide-form.js new file mode 100644 index 0000000..455cf24 --- /dev/null +++ b/src/webui/public/js/slide-form.js @@ -0,0 +1,602 @@ +(function () { + var dataElement = document.getElementById('slide-editor-data'); + if (!dataElement) { + return; + } + + var slideEditorData = {}; + try { + slideEditorData = JSON.parse(dataElement.getAttribute('data-json') || dataElement.textContent || '{}') || {}; + } catch (_error) { + slideEditorData = {}; + } + + var templates = Array.isArray(slideEditorData.templates) ? slideEditorData.templates : []; + var existingTemplateId = slideEditorData.existingTemplateId !== undefined ? slideEditorData.existingTemplateId : null; + var existingContent = slideEditorData.existingContent || {}; + var templateSelect = document.getElementById('template-select'); + var templateFields = document.getElementById('template-fields'); + var slideForm = document.getElementById('slide-form'); + var slidePreviewMeta = document.getElementById('slide-preview-meta'); + var slidePreviewStage = document.getElementById('slide-preview-stage'); + var slidePreviewBackground = document.getElementById('slide-preview-background'); + var slidePreviewEmpty = document.getElementById('slide-preview-empty'); + var slidePreviewOverlay = document.getElementById('slide-preview-overlay'); + var editorInstances = new Map(); + var submitting = false; + + function dismissToast(toast) { + if (!toast) { + return; + } + toast.classList.add('toast-hide'); + window.setTimeout(function () { + if (toast && toast.parentNode) { + toast.parentNode.removeChild(toast); + } + }, 220); + } + + function showToast(message) { + var text = String(message || '').trim(); + if (!text) { + return; + } + + var existingToast = document.getElementById('app-toast'); + var existingBody = existingToast ? existingToast.querySelector('.toast-body') : null; + if (existingToast && existingBody) { + existingBody.textContent = text; + existingToast.classList.remove('toast-hide'); + window.clearTimeout(existingToast._dismissTimer); + existingToast._dismissTimer = window.setTimeout(function () { + dismissToast(existingToast); + }, 4000); + return; + } + + var toast = document.createElement('div'); + toast.className = 'toast'; + toast.setAttribute('role', 'status'); + toast.setAttribute('aria-live', 'polite'); + toast.innerHTML = '
    '; + toast.querySelector('.toast-body').textContent = text; + + var closeButton = toast.querySelector('.toast-close'); + closeButton.addEventListener('click', function () { + dismissToast(toast); + }); + + document.body.appendChild(toast); + toast._dismissTimer = window.setTimeout(function () { + dismissToast(toast); + }, 4000); + } + + function escapeHtml(value) { + return String(value ?? '') + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); + } + + function getTemplateById(id) { + return templates.find(function (item) { return Number(item.id) === Number(id); }) || null; + } + + function getCurrentRegionValue(region) { + var current = existingContent[region.region_key]; + return current && current.value !== undefined ? current.value : ''; + } + + function normalizeEditorData(value) { + var raw = String(value || ''); + if (!raw) { + return { time: Date.now(), blocks: [] }; + } + try { + var parsed = JSON.parse(raw); + if (parsed && Array.isArray(parsed.blocks)) { + return parsed; + } + } catch (_error) { + // fall through to legacy HTML/plain text + } + return { + time: Date.now(), + blocks: [{ + type: 'paragraph', + data: { text: raw } + }] + }; + } + + function sanitizePreviewHtml(html) { + var output = String(html || ''); + output = output.replace(//gi, ''); + output = output.replace(//gi, ''); + return output.replace(/<[^>]+>/g, function (tag) { + var match = tag.match(/^<\s*(\/?)\s*([a-z0-9]+)(?:\s[^>]*)?>$/i); + if (!match) { + return ''; + } + var closing = Boolean(match[1]); + var name = String(match[2] || '').toLowerCase(); + var allowed = ['b', 'strong', 'i', 'em', 'u', 'br', 'p', 'div', 'ul', 'ol', 'li', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'pre', 'code', 'table', 'thead', 'tbody', 'tr', 'th', 'td']; + if (allowed.indexOf(name) === -1) { + return ''; + } + if (name === 'br') { + return '
    '; + } + return closing ? '' : '<' + name + '>'; + }); + } + + function renderPreviewListItem(item, tag) { + if (item && typeof item === 'object') { + var content = item.content !== undefined ? item.content : (item.text !== undefined ? item.text : item.value !== undefined ? item.value : ''); + var children = Array.isArray(item.items) ? item.items : Array.isArray(item.subItems) ? item.subItems : Array.isArray(item.children) ? item.children : []; + var nested = children.length ? '<' + tag + ' style="list-style-type:' + (tag === 'ol' ? 'decimal' : 'disc') + ';padding-left:1.4em;">' + children.map(function (child) { return renderPreviewListItem(child, tag); }).join('') + '' : ''; + return '
  • ' + sanitizePreviewHtml(content || '') + nested + '
  • '; + } + return '
  • ' + sanitizePreviewHtml(item || '') + '
  • '; + } + + function renderPreviewTable(data) { + var rows = Array.isArray(data.content) ? data.content : Array.isArray(data.rows) ? data.rows : []; + if (!rows.length) { + return ''; + } + var hasHeadings = Boolean(data.withHeadings); + var body = rows.map(function (row, rowIndex) { + var cells = Array.isArray(row) ? row : []; + var cellTag = hasHeadings && rowIndex === 0 ? 'th' : 'td'; + var cellAttrs = cellTag === 'th' ? ' scope="col"' : ''; + return '' + cells.map(function (cell) { + return '<' + cellTag + cellAttrs + '>' + sanitizePreviewHtml(cell || '') + ''; + }).join('') + ''; + }).join(''); + return '' + body + '
    '; + } + + function renderPreviewBlock(block) { + if (!block || !block.type || !block.data) { + return ''; + } + if (block.type === 'header') { + var level = Math.max(1, Math.min(6, Number(block.data.level || 2))); + return '' + sanitizePreviewHtml(block.data.text || '') + ''; + } + if (block.type === 'list') { + var tag = block.data.style === 'ordered' ? 'ol' : 'ul'; + var items = Array.isArray(block.data.items) ? block.data.items : []; + return '<' + tag + ' style="list-style-type:' + (tag === 'ol' ? 'decimal' : 'disc') + ';padding-left:1.4em;">' + items.map(function (item) { return renderPreviewListItem(item, tag); }).join('') + ''; + } + if (block.type === 'delimiter') { + return '
    '; + } + if (block.type === 'code') { + return '
    ' + sanitizePreviewHtml(block.data.code || '') + '
    '; + } + if (block.type === 'table') { + return renderPreviewTable(block.data); + } + if (block.type === 'paragraph') { + return '

    ' + sanitizePreviewHtml(block.data.text || '') + '

    '; + } + return ''; + } + + function renderPreviewText(value) { + var raw = String(value || ''); + if (!raw) { + return '
    Empty text
    '; + } + try { + var parsed = JSON.parse(raw); + if (parsed && Array.isArray(parsed.blocks)) { + return parsed.blocks.map(renderPreviewBlock).join(''); + } + } catch (_error) { + // fall through to raw text + } + return '

    ' + sanitizePreviewHtml(raw) + '

    '; + } + + function renderPreviewTextRegion(region, value, style, scale) { + var fontFamily = style.font_family ? 'font-family:' + escapeHtml(style.font_family) + ';' : ''; + var fontSize = style.font_size ? 'font-size:' + Math.max(1, Math.round(Number(style.font_size) * scale)) + 'px;' : ''; + var color = style.font_color ? 'color:' + escapeHtml(style.font_color) + ';' : ''; + return '
    ' + renderPreviewText(value) + '
    '; + } + + function renderPreviewWebpageRegion(value) { + var src = String(value || '').trim(); + if (!src) { + return '
    Webpage
    '; + } + return ''; + } + + function renderPreviewHtmlRegion(value) { + var html = String(value || '').trim(); + if (!html) { + return '
    HTML
    '; + } + return ''; + } + + function destroyEditors() { + editorInstances.forEach(function (editor) { + if (editor && typeof editor.destroy === 'function') { + editor.destroy(); + } + }); + editorInstances.clear(); + } + + function getEditorHiddenInput(regionId) { + var card = templateFields.querySelector('[data-region-id="' + regionId + '"]'); + return card ? card.querySelector('input[type="hidden"][name="region_text_' + regionId + '"]') : null; + } + + function getCurrentTextStyle(region) { + var current = existingContent[region.region_key] || {}; + return { + font_family: String(current.font_family || region.font_family || 'Arial').trim() || 'Arial', + font_size: Math.max(8, Number(current.font_size || 24)), + font_color: String(current.font_color || region.font_color || '#000000').trim() || '#000000' + }; + } + + function getTextStyleFromCard(card, region) { + var familyInput = card.querySelector('[name="region_font_family_' + region.id + '"]'); + var sizeInput = card.querySelector('[name="region_font_size_' + region.id + '"]'); + var colorInput = card.querySelector('[name="region_font_color_' + region.id + '"]'); + var current = getCurrentTextStyle(region); + return { + font_family: String(familyInput ? familyInput.value : current.font_family).trim() || current.font_family, + font_size: Math.max(8, Number(sizeInput ? sizeInput.value : current.font_size)), + font_color: String(colorInput ? colorInput.value : current.font_color).trim() || current.font_color + }; + } + + function getSelectedTemplate() { + return templates.find(function (item) { return Number(item.id) === Number(templateSelect.value); }) || null; + } + + function clearImagePreviewUrls() { + templateFields.querySelectorAll('input[type="file"][name^="region_image_"]').forEach(function (input) { + if (input.dataset.previewUrl) { + URL.revokeObjectURL(input.dataset.previewUrl); + delete input.dataset.previewUrl; + } + }); + } + + function getRegionBox(region, stageRect, canvasWidth, canvasHeight) { + return { + left: (Number(region.x || 0) / canvasWidth) * stageRect.width, + top: (Number(region.y || 0) / canvasHeight) * stageRect.height, + width: (Number(region.width || 0) / canvasWidth) * stageRect.width, + height: (Number(region.height || 0) / canvasHeight) * stageRect.height + }; + } + + function renderPreview() { + var template = getSelectedTemplate(); + if (!template) { + slidePreviewMeta.textContent = 'Select a template to preview the slide.'; + slidePreviewBackground.style.display = 'none'; + slidePreviewEmpty.style.display = 'flex'; + slidePreviewEmpty.textContent = 'Select a template to preview the slide.'; + slidePreviewOverlay.innerHTML = ''; + slidePreviewStage.style.aspectRatio = '16 / 9'; + return; + } + + var canvasWidth = Math.max(1, Number(template.canvas_size_width || 1920)); + var canvasHeight = Math.max(1, Number(template.canvas_size_height || 1080)); + slidePreviewStage.style.aspectRatio = canvasWidth + ' / ' + canvasHeight; + slidePreviewMeta.textContent = template.name + ' • ' + canvasWidth + 'x' + canvasHeight; + if (!(template.regions || []).length) { + slidePreviewEmpty.style.display = 'flex'; + slidePreviewEmpty.textContent = 'This template has no regions.'; + slidePreviewBackground.style.display = template.background_image_path ? 'block' : 'none'; + slidePreviewBackground.src = template.background_image_path || ''; + slidePreviewOverlay.innerHTML = ''; + return; + } + + slidePreviewEmpty.style.display = 'none'; + slidePreviewBackground.style.display = template.background_image_path ? 'block' : 'none'; + slidePreviewBackground.src = template.background_image_path || ''; + + var stageRect = slidePreviewStage.getBoundingClientRect(); + var scale = Math.min(stageRect.width / canvasWidth, stageRect.height / canvasHeight) || 1; + var regions = template.regions || []; + slidePreviewOverlay.innerHTML = regions.map(function (region) { + var box = getRegionBox(region, stageRect, canvasWidth, canvasHeight); + var card = templateFields.querySelector('[data-region-id="' + region.id + '"]'); + var hiddenInput = card && card.querySelector('input[type="hidden"][name="region_text_' + region.id + '"]'); + var htmlInput = card && card.querySelector('textarea[name="region_html_' + region.id + '"]'); + var imageHidden = card && card.querySelector('input[type="hidden"][name="existing_region_image_' + region.id + '"]'); + var imageFileInput = card && card.querySelector('input[type="file"][name="region_image_' + region.id + '"]'); + var webpageInput = card && card.querySelector('input[type="url"][name="region_webpage_' + region.id + '"]'); + var value = region.region_type === 'image' + ? ((imageFileInput && imageFileInput.dataset.previewUrl) ? imageFileInput.dataset.previewUrl : (imageHidden ? imageHidden.value : '')) + : region.region_type === 'webpage' + ? (webpageInput ? webpageInput.value : '') + : region.region_type === 'html' + ? (htmlInput ? htmlInput.value : '') + : (hiddenInput ? hiddenInput.value : ''); + var style = region.region_type === 'image' + ? null + : region.region_type === 'webpage' + ? null + : region.region_type === 'html' + ? null + : getTextStyleFromCard(card, region); + var content = region.region_type === 'image' + ? (value ? '' : '
    Image
    ') + : region.region_type === 'webpage' + ? renderPreviewWebpageRegion(value) + : region.region_type === 'html' + ? renderPreviewHtmlRegion(value) + : renderPreviewTextRegion(region, value, style, scale); + var selected = region.region_type === 'image' + ? ' slide-preview-image-region' + : region.region_type === 'webpage' + ? ' slide-preview-webpage-region' + : region.region_type === 'html' + ? ' slide-preview-html-region' + : ' slide-preview-text-region'; + return '
    ' + content + '
    '; + }).join(''); + } + + function renderTextRegion(region) { + var current = getCurrentRegionValue(region); + var style = getCurrentTextStyle(region); + var fontChoices = [ + 'Arial', + 'Courier New', + 'Garamond', + 'Georgia', + 'Helvetica', + 'Impact', + 'Lucida Console', + 'Palatino', + 'Tahoma', + 'Times New Roman', + 'Trebuchet MS', + 'Verdana' + ]; + return '' + + '
    ' + + '
    ' + + '' + escapeHtml(region.label) + '' + + 'Text' + + '
    ' + + '
    ' + + '' + + '' + + '' + + '
    ' + + '
    ' + + '' + + '
    '; + } + + function renderWebpageRegion(region) { + var current = getCurrentRegionValue(region); + return '' + + '
    ' + + '
    ' + + '' + escapeHtml(region.label) + '' + + 'Webpage' + + '
    ' + + '' + + '
    The webpage will be loaded in an iframe.
    ' + + '
    '; + } + + function renderHtmlRegion(region) { + var current = getCurrentRegionValue(region); + return '' + + '
    ' + + '
    ' + + '' + escapeHtml(region.label) + '' + + 'HTML' + + '
    ' + + '' + + '
    HTML is rendered in a sandboxed iframe preview.
    ' + + '
    '; + } + + function renderImageRegion(region) { + var current = getCurrentRegionValue(region); + return '' + + '
    '; + } + + function renderTemplate() { + clearImagePreviewUrls(); + destroyEditors(); + var template = getTemplateById(templateSelect.value); + if (!template) { + templateFields.innerHTML = ''; + renderPreview(); + return; + } + + templateFields.innerHTML = template.regions.length ? template.regions.map(function (region) { + if (region.region_type === 'image') { + return renderImageRegion(region); + } + if (region.region_type === 'webpage') { + return renderWebpageRegion(region); + } + if (region.region_type === 'html') { + return renderHtmlRegion(region); + } + return renderTextRegion(region); + }).join('') : '
    This template has no regions.
    '; + + templateFields.querySelectorAll('.editorjs-holder').forEach(function (holder) { + var regionId = holder.getAttribute('data-region-id'); + var hidden = getEditorHiddenInput(regionId); + var editor = new EditorJS({ + holder: holder.id, + minHeight: 120, + placeholder: 'Write content...', + inlineToolbar: true, + tools: { + header: { + class: Header, + config: { + placeholder: 'Enter a header', + levels: [1, 2, 3, 4], + defaultLevel: 3 + } + }, + list: { + class: EditorjsList, + inlineToolbar: false, + config: { + defaultStyle: 'unordered' + } + }, + table: { + class: Table, + inlineToolbar: true, + config: { + rows: 2, + cols: 3, + maxRows: 5, + maxCols: 5 + } + }, + marker: { + class: Marker + } + }, + data: normalizeEditorData(hidden ? hidden.value : ''), + onChange: function () { + var instance = editorInstances.get(regionId); + if (!instance) { + return; + } + instance.save().then(function (output) { + if (hidden) { + hidden.value = JSON.stringify(output); + } + renderPreview(); + }); + } + }); + editorInstances.set(regionId, editor); + }); + + templateFields.querySelectorAll('input[type="file"][name^="region_image_"]').forEach(function (input) { + input.addEventListener('change', function () { + if (input.dataset.previewUrl) { + URL.revokeObjectURL(input.dataset.previewUrl); + delete input.dataset.previewUrl; + } + if (input.files && input.files.length) { + input.dataset.previewUrl = URL.createObjectURL(input.files[0]); + } + renderPreview(); + }); + }); + renderPreview(); + } + + slideForm.addEventListener('submit', async function (event) { + if (submitting) { + return; + } + event.preventDefault(); + var saves = Array.prototype.map.call(templateFields.querySelectorAll('.editorjs-holder'), function (holder) { + var regionId = holder.getAttribute('data-region-id'); + var editor = editorInstances.get(regionId); + var hidden = getEditorHiddenInput(regionId); + if (!editor || !hidden) { + return Promise.resolve(); + } + return editor.save().then(function (output) { + hidden.value = JSON.stringify(output); + }); + }); + submitting = true; + try { + await Promise.all(saves); + var response = await fetch(slideForm.action, { + method: (slideForm.method || 'POST').toUpperCase(), + headers: { + 'X-Requested-With': 'XMLHttpRequest', + 'Accept': 'text/html, application/json, text/plain, */*' + }, + body: new FormData(slideForm), + credentials: 'same-origin' + }); + + if (!response.ok) { + throw new Error(await response.text() || 'Unable to save slide.'); + } + + var responseText = await response.text(); + var savedMessage = ''; + try { + var doc = new DOMParser().parseFromString(responseText || '', 'text/html'); + var toastBody = doc.querySelector('.toast-body'); + if (toastBody && toastBody.textContent) { + savedMessage = toastBody.textContent.trim(); + } + } catch (_error) { + savedMessage = ''; + } + + showToast(savedMessage || 'Saved slide.'); + } finally { + submitting = false; + } + }); + + templateSelect.addEventListener('change', renderTemplate); + templateFields.addEventListener('change', renderPreview); + templateFields.addEventListener('input', renderPreview); + if (existingTemplateId) { + templateSelect.value = String(existingTemplateId); + } + renderTemplate(); + window.addEventListener('resize', renderPreview); +})(); diff --git a/src/webui/public/js/template-designer.js b/src/webui/public/js/template-designer.js new file mode 100644 index 0000000..11c72b8 --- /dev/null +++ b/src/webui/public/js/template-designer.js @@ -0,0 +1,467 @@ +(function () { + var dataElement = document.getElementById('template-editor-data'); + if (!dataElement) { + return; + } + + var templateData = {}; + try { + templateData = JSON.parse(dataElement.getAttribute('data-json') || dataElement.textContent || '{}') || {}; + } catch (_error) { + templateData = {}; + } + + var existingRegions = Array.isArray(templateData.regions) + ? templateData.regions + : (Array.isArray(templateData) ? templateData : []); + var stage = document.getElementById('designer-stage'); + var overlay = document.getElementById('designer-overlay'); + var regionList = document.getElementById('region-list'); + var regionSelect = document.getElementById('region-select'); + var canvasSizeSelect = document.getElementById('canvas-size-select'); + var canvasSizeIdInput = document.getElementById('canvas-size-id'); + var canvasSizeSummary = document.getElementById('canvas-size-summary'); + var canvasWidthInput = document.getElementById('canvas-width'); + var canvasHeightInput = document.getElementById('canvas-height'); + var backgroundInput = document.getElementById('background-image'); + var backgroundPreview = document.getElementById('background-preview'); + var backgroundEmpty = document.getElementById('background-empty'); + var addTextRegionButton = document.getElementById('add-text-region'); + var addImageRegionButton = document.getElementById('add-image-region'); + var regionsJsonInput = document.getElementById('regions-json'); + var draft = null; + var selectedIndex = -1; + + function escapeHtml(value) { + return String(value === undefined || value === null ? '' : value) + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); + } + + function valueOrDefault(value, fallback) { + return value === undefined || value === null || value === '' ? fallback : value; + } + + function getCanvasSize() { + return { + width: Math.max(1, Number(canvasWidthInput.value || 1920)), + height: Math.max(1, Number(canvasHeightInput.value || 1080)) + }; + } + + function clamp(value, min, max) { + return Math.max(min, Math.min(max, value)); + } + + function getCards() { + return Array.prototype.slice.call(regionList.querySelectorAll('.region-item')); + } + + function cardAt(index) { + return getCards()[index] || null; + } + + function getRegionName(card) { + return String(card.querySelector('[name="region_name[]"]').value || '').trim(); + } + + function syncRegionIdentity(card, value) { + var next = String(value || '').trim(); + card.querySelector('[name="region_name[]"]').value = next; + card.querySelector('[name="region_key[]"]').value = next; + card.querySelector('[name="region_label[]"]').value = next; + } + + function readCard(card) { + var name = getRegionName(card); + return { + region_key: name, + label: name, + region_type: card.querySelector('[name="region_type[]"]').value, + font_family: card.querySelector('[name="font_family[]"]').value, + x: Number(card.querySelector('[name="region_x[]"]').value || 0), + y: Number(card.querySelector('[name="region_y[]"]').value || 0), + width: Number(card.querySelector('[name="region_width[]"]').value || 0), + height: Number(card.querySelector('[name="region_height[]"]').value || 0), + z_index: Number(card.querySelector('[name="region_z[]"]').value || 0) + }; + } + + function writeCard(card, values) { + if (values.region_name !== undefined) { + syncRegionIdentity(card, values.region_name); + } else if (values.region_key !== undefined) { + syncRegionIdentity(card, values.region_key); + } else if (values.label !== undefined) { + syncRegionIdentity(card, values.label); + } + if (values.region_type !== undefined) { card.querySelector('[name="region_type[]"]').value = values.region_type; } + if (values.font_family !== undefined) { card.querySelector('[name="font_family[]"]').value = values.font_family; updateRegionFieldVisibility(card); } + if (values.x !== undefined) { card.querySelector('[name="region_x[]"]').value = Math.round(values.x); } + if (values.y !== undefined) { card.querySelector('[name="region_y[]"]').value = Math.round(values.y); } + if (values.width !== undefined) { card.querySelector('[name="region_width[]"]').value = Math.round(values.width); } + if (values.height !== undefined) { card.querySelector('[name="region_height[]"]').value = Math.round(values.height); } + if (values.z_index !== undefined) { card.querySelector('[name="region_z[]"]').value = Math.round(values.z_index); } + } + + function getOverlayRect() { + return overlay.getBoundingClientRect(); + } + + function toCanvasPoint(event) { + var rect = getOverlayRect(); + var size = getCanvasSize(); + var x = clamp(event.clientX - rect.left, 0, rect.width); + var y = clamp(event.clientY - rect.top, 0, rect.height); + return { + x: Math.round((x / Math.max(rect.width, 1)) * size.width), + y: Math.round((y / Math.max(rect.height, 1)) * size.height) + }; + } + + function canvasRectToPixels(region) { + var rect = getOverlayRect(); + var size = getCanvasSize(); + return { + left: (region.x / size.width) * rect.width, + top: (region.y / size.height) * rect.height, + width: (region.width / size.width) * rect.width, + height: (region.height / size.height) * rect.height + }; + } + + function updateAspectRatio() { + var size = getCanvasSize(); + stage.style.aspectRatio = size.width + ' / ' + size.height; + } + + function updateCanvasSizeSummary() { + var option = canvasSizeSelect.options[canvasSizeSelect.selectedIndex]; + canvasSizeSummary.textContent = option ? option.textContent : ''; + } + + function updateCanvasSizeLock() { + var lockOnExistingTemplate = canvasSizeSelect.dataset.lockOnExistingTemplate === 'true'; + var locked = lockOnExistingTemplate && getCards().length > 0; + canvasSizeSelect.disabled = locked; + canvasSizeSummary.classList.toggle('is-locked', locked); + } + + function syncCanvasSizeSelection() { + var option = canvasSizeSelect.options[canvasSizeSelect.selectedIndex]; + if (!option) { + return; + } + if (canvasSizeIdInput) { + canvasSizeIdInput.value = option.value; + } + canvasWidthInput.value = Math.max(1, Number(option.dataset.width || canvasWidthInput.value || 1920)); + canvasHeightInput.value = Math.max(1, Number(option.dataset.height || canvasHeightInput.value || 1080)); + updateAspectRatio(); + updateCanvasSizeSummary(); + } + + function updateBackgroundPreview(file) { + if (!file) { + return; + } + var reader = new FileReader(); + reader.onload = function () { + backgroundPreview.src = reader.result; + backgroundPreview.style.display = 'block'; + backgroundEmpty.style.display = 'none'; + }; + reader.readAsDataURL(file); + } + + function updateRegionFieldVisibility(card) { + var typeSelect = card.querySelector('[name="region_type[]"]'); + var fontField = card.querySelector('[name="font_family[]"]'); + if (!typeSelect || !fontField) { + return; + } + fontField.value = typeSelect.value === 'text' || typeSelect.value === 'html' ? 'Arial' : ''; + } + + function makeRegionCard(region) { + var card = document.createElement('div'); + card.className = 'region-item'; + var chipLabel = region.region_type === 'image' ? 'Image' : region.region_type === 'webpage' ? 'Webpage' : region.region_type === 'html' ? 'HTML' : 'Text'; + card.innerHTML = '' + + '' + + '' + + '' + + '' + + '' + + '
    ' + escapeHtml(region.label || region.region_key || '') + '' + escapeHtml(chipLabel) + '
    ' + + '
    ' + + '
    ' + + '
    '; + var nameInput = card.querySelector('[name="region_name[]"]'); + var typeSelect = card.querySelector('[name="region_type[]"]'); + nameInput.addEventListener('input', function () { + syncRegionIdentity(card, nameInput.value); + renderRegionSidebar(); + renderOverlay(); + }); + typeSelect.addEventListener('change', function () { + updateRegionFieldVisibility(card); + renderOverlay(); + }); + card.addEventListener('click', function (event) { + if (event.target && event.target.classList && event.target.classList.contains('remove-region')) { + return; + } + setSelected(getCards().indexOf(card)); + }); + card.querySelector('.remove-region').addEventListener('click', function (event) { + event.preventDefault(); + card.remove(); + if (!getCards().length) { + selectedIndex = -1; + } else if (selectedIndex >= getCards().length) { + selectedIndex = getCards().length - 1; + } + renderRegionSidebar(); + renderOverlay(); + }); + updateRegionFieldVisibility(card); + return card; + } + + function renderRegionList(initialRegions) { + regionList.innerHTML = ''; + initialRegions.forEach(function (region) { + regionList.appendChild(makeRegionCard(region)); + }); + } + + function renderRegionSidebar() { + var cards = getCards(); + regionSelect.innerHTML = ''; + if (!cards.length) { + regionSelect.disabled = true; + regionList.innerHTML = ''; + updateCanvasSizeLock(); + return; + } + regionSelect.disabled = false; + if (selectedIndex < 0 || selectedIndex >= cards.length) { + selectedIndex = 0; + } + cards.forEach(function (card, index) { + var option = document.createElement('option'); + option.value = String(index); + option.textContent = getRegionName(card) || ('Region ' + (index + 1)); + if (index === selectedIndex) { + option.selected = true; + } + regionSelect.appendChild(option); + card.hidden = index !== selectedIndex; + }); + regionSelect.value = String(selectedIndex); + updateCanvasSizeLock(); + } + + function renderOverlay() { + var cards = getCards(); + var selectedCard = selectedIndex >= 0 ? cards[selectedIndex] : null; + var selectedNow = selectedCard ? cards.indexOf(selectedCard) : -1; + var regions = cards.map(readCard); + regionsJsonInput.value = JSON.stringify(regions); + overlay.innerHTML = regions.map(function (region, index) { + var box = canvasRectToPixels(region); + var selected = index === selectedNow ? ' selected' : ''; + return '
    ' + escapeHtml(region.label || region.region_key || 'Region') + '
    '; + }).join(''); + if (draft) { + var rect = getOverlayRect(); + var size = getCanvasSize(); + var draftBox = { x: Math.min(draft.start.x, draft.end.x), y: Math.min(draft.start.y, draft.end.y), width: Math.abs(draft.end.x - draft.start.x), height: Math.abs(draft.end.y - draft.start.y) }; + overlay.innerHTML += '
    '; + } + } + + function render() { + updateAspectRatio(); + renderRegionSidebar(); + renderOverlay(); + } + + function setSelected(index) { + var cards = getCards(); + if (!cards.length) { + selectedIndex = -1; + } else if (index < 0) { + selectedIndex = 0; + } else { + selectedIndex = clamp(index, 0, cards.length - 1); + } + renderRegionSidebar(); + renderOverlay(); + } + + function addRegion(region) { + var hint = regionList.querySelector('.muted'); + if (hint) { + hint.remove(); + } + regionList.appendChild(makeRegionCard(region)); + setSelected(getCards().length - 1); + } + + function createDefaultRegion(type) { + var count = getCards().length + 1; + var name = 'region_' + count; + return { + region_key: name, + label: name, + region_type: type, + font_family: type === 'text' || type === 'html' ? 'Arial' : '', + x: 80, + y: 80, + width: type === 'image' || type === 'webpage' || type === 'html' ? 420 : 300, + height: type === 'image' || type === 'webpage' || type === 'html' ? 240 : 120, + z_index: 1 + }; + } + + function clampRegion(region) { + var size = getCanvasSize(); + var minSize = 12; + var x = clamp(region.x, 0, size.width - minSize); + var y = clamp(region.y, 0, size.height - minSize); + var width = Math.max(minSize, region.width); + var height = Math.max(minSize, region.height); + if (x + width > size.width) { + width = size.width - x; + } + if (y + height > size.height) { + height = size.height - y; + } + return { x: Math.round(x), y: Math.round(y), width: Math.round(Math.max(minSize, width)), height: Math.round(Math.max(minSize, height)) }; + } + + function startDraw(event) { + var start = toCanvasPoint(event); + draft = { start: start, end: start }; + renderOverlay(); + function moveHandler(moveEvent) { + draft.end = toCanvasPoint(moveEvent); + renderOverlay(); + } + function upHandler(upEvent) { + draft.end = toCanvasPoint(upEvent); + var width = Math.abs(draft.end.x - draft.start.x); + var height = Math.abs(draft.end.y - draft.start.y); + if (width >= 8 && height >= 8) { + var region = clampRegion({ x: Math.min(draft.start.x, draft.end.x), y: Math.min(draft.start.y, draft.end.y), width: width, height: height }); + addRegion({ region_key: 'region_' + (getCards().length + 1), label: 'Region ' + (getCards().length + 1), region_type: 'text', x: region.x, y: region.y, width: region.width, height: region.height, z_index: 1 }); + } + draft = null; + renderOverlay(); + document.removeEventListener('mousemove', moveHandler); + document.removeEventListener('mouseup', upHandler); + } + document.addEventListener('mousemove', moveHandler); + document.addEventListener('mouseup', upHandler); + } + + function startMove(index, event) { + var startPoint = toCanvasPoint(event); + var startRegion = readCard(cardAt(index)); + function moveHandler(moveEvent) { + var currentPoint = toCanvasPoint(moveEvent); + var dx = currentPoint.x - startPoint.x; + var dy = currentPoint.y - startPoint.y; + var next = clampRegion({ x: startRegion.x + dx, y: startRegion.y + dy, width: startRegion.width, height: startRegion.height }); + writeCard(cardAt(index), { x: next.x, y: next.y }); + renderOverlay(); + } + function upHandler() { + document.removeEventListener('mousemove', moveHandler); + document.removeEventListener('mouseup', upHandler); + } + document.addEventListener('mousemove', moveHandler); + document.addEventListener('mouseup', upHandler); + } + + function resizeFromHandle(index, dir, event) { + var startPoint = toCanvasPoint(event); + var startRegion = readCard(cardAt(index)); + function moveHandler(moveEvent) { + var currentPoint = toCanvasPoint(moveEvent); + var dx = currentPoint.x - startPoint.x; + var dy = currentPoint.y - startPoint.y; + var next = { x: startRegion.x, y: startRegion.y, width: startRegion.width, height: startRegion.height }; + if (dir.indexOf('w') !== -1) { next.x = startRegion.x + dx; next.width = startRegion.width - dx; } + if (dir.indexOf('e') !== -1) { next.width = startRegion.width + dx; } + if (dir.indexOf('n') !== -1) { next.y = startRegion.y + dy; next.height = startRegion.height - dy; } + if (dir.indexOf('s') !== -1) { next.height = startRegion.height + dy; } + if (next.width < 12) { if (dir.indexOf('w') !== -1) { next.x -= 12 - next.width; } next.width = 12; } + if (next.height < 12) { if (dir.indexOf('n') !== -1) { next.y -= 12 - next.height; } next.height = 12; } + next = clampRegion(next); + writeCard(cardAt(index), { x: next.x, y: next.y, width: next.width, height: next.height }); + renderOverlay(); + } + function upHandler() { + document.removeEventListener('mousemove', moveHandler); + document.removeEventListener('mouseup', upHandler); + } + document.addEventListener('mousemove', moveHandler); + document.addEventListener('mouseup', upHandler); + } + + addTextRegionButton.addEventListener('click', function () { addRegion(createDefaultRegion('text')); }); + var addHtmlRegionButton = document.getElementById('add-html-region'); + if (addHtmlRegionButton) { + addHtmlRegionButton.addEventListener('click', function () { addRegion(createDefaultRegion('html')); }); + } + addImageRegionButton.addEventListener('click', function () { addRegion(createDefaultRegion('image')); }); + var addWebpageRegionButton = document.getElementById('add-webpage-region'); + if (addWebpageRegionButton) { + addWebpageRegionButton.addEventListener('click', function () { addRegion(createDefaultRegion('webpage')); }); + } + backgroundInput.addEventListener('change', function () { + var file = backgroundInput.files && backgroundInput.files[0]; + if (file) { + updateBackgroundPreview(file); + } + }); + canvasSizeSelect.addEventListener('change', function () { syncCanvasSizeSelection(); render(); }); + canvasWidthInput.addEventListener('input', render); + canvasHeightInput.addEventListener('input', render); + regionSelect.addEventListener('change', function () { setSelected(Number(regionSelect.value || 0)); }); + overlay.addEventListener('mousedown', function (event) { + var rect = event.target.closest('.designer-rect'); + if (rect) { + var index = Number(rect.getAttribute('data-index')); + var handle = event.target.closest('.resize-handle'); + event.preventDefault(); + setSelected(index); + if (handle) { + resizeFromHandle(index, handle.getAttribute('data-dir'), event); + } else { + startMove(index, event); + } + return; + } + if (event.target !== overlay && !event.target.classList.contains('designer-overlay')) { + return; + } + event.preventDefault(); + setSelected(-1); + startDraw(event); + }); + document.getElementById('template-form').addEventListener('submit', function () { + syncCanvasSizeSelection(); + regionsJsonInput.value = JSON.stringify(getCards().map(readCard)); + }); + + renderRegionList(existingRegions); + syncCanvasSizeSelection(); + render(); +})(); diff --git a/src/webui/routes/account/password.js b/src/webui/routes/account/password.js new file mode 100644 index 0000000..0b6ec1a --- /dev/null +++ b/src/webui/routes/account/password.js @@ -0,0 +1,12 @@ +const { renderView } = require('../../view'); + +module.exports = function renderAccountPage(currentUser, message) { + return renderView('account/password', { + title: 'My account', + active: 'account', + currentUser: currentUser || null, + message: message || '', + username: currentUser ? currentUser.username : '', + name: currentUser ? String(currentUser.name || '') : '' + }); +}; \ No newline at end of file diff --git a/src/webui/routes/auth/login.js b/src/webui/routes/auth/login.js new file mode 100644 index 0000000..4e1ef54 --- /dev/null +++ b/src/webui/routes/auth/login.js @@ -0,0 +1,10 @@ +const { renderView } = require('../../view'); + +module.exports = function renderLoginPage(message) { + return renderView('login', { + title: 'Sign in', + authShell: true, + bodyClass: 'login-page-body', + message: message || '' + }); +}; \ No newline at end of file diff --git a/src/webui/routes/canvas-sizes/add.js b/src/webui/routes/canvas-sizes/add.js new file mode 100644 index 0000000..c176adc --- /dev/null +++ b/src/webui/routes/canvas-sizes/add.js @@ -0,0 +1,21 @@ +const { renderView } = require('../../view'); + +function buildDefaultCanvasSize() { + return { + id: null, + name: '', + width: 1920, + height: 1080 + }; +} + +module.exports = function renderCanvasSizeFormPage(canvasSize, mode, message, currentUser) { + const isEdit = mode === 'edit'; + return renderView(isEdit ? 'canvas-sizes/edit' : 'canvas-sizes/add', { + title: isEdit ? 'Edit canvas size' : 'Add canvas size', + active: 'canvas-sizes', + message: message, + currentUser: currentUser || null, + canvasSize: canvasSize || buildDefaultCanvasSize() + }); +}; diff --git a/src/webui/routes/canvas-sizes/edit.js b/src/webui/routes/canvas-sizes/edit.js new file mode 100644 index 0000000..4524d51 --- /dev/null +++ b/src/webui/routes/canvas-sizes/edit.js @@ -0,0 +1,5 @@ +const renderCanvasSizeFormPage = require('./add'); + +module.exports = function renderCanvasSizeEditPage(canvasSize, _data, message, currentUser) { + return renderCanvasSizeFormPage(canvasSize, 'edit', message, currentUser); +}; diff --git a/src/webui/routes/canvas-sizes/list.js b/src/webui/routes/canvas-sizes/list.js new file mode 100644 index 0000000..fc1d55c --- /dev/null +++ b/src/webui/routes/canvas-sizes/list.js @@ -0,0 +1,19 @@ +const { renderView } = require('../../view'); + +module.exports = function renderCanvasSizesPage(data, message, currentUser) { + const canvasSizes = (data.canvasSizes || []).map((size) => ({ + id: size.id, + name: size.name, + width: size.width, + height: size.height, + templateCount: (data.templates || []).filter((template) => Number(template.canvas_size_id) === Number(size.id)).length + })); + + return renderView('canvas-sizes/list', { + title: 'Canvas sizes', + active: 'canvas-sizes', + message: message, + currentUser: currentUser || null, + canvasSizes: canvasSizes + }); +}; diff --git a/src/webui/routes/clients/list.js b/src/webui/routes/clients/list.js new file mode 100644 index 0000000..2cd965a --- /dev/null +++ b/src/webui/routes/clients/list.js @@ -0,0 +1,11 @@ +const { renderView } = require('../../view'); + +module.exports = function renderConnectedClientsPage(data, message, currentUser) { + return renderView('clients/list', { + title: 'Connected clients', + active: 'clients', + message: message, + currentUser: currentUser || null, + clients: data.clients || [] + }); +}; \ No newline at end of file diff --git a/src/webui/routes/common.js b/src/webui/routes/common.js new file mode 100644 index 0000000..2d1f189 --- /dev/null +++ b/src/webui/routes/common.js @@ -0,0 +1,56 @@ +const PLAYER_PUBLIC_BASE_URL = (process.env.PLAYER_PUBLIC_BASE_URL || process.env.PLAYER_BASE_URL || 'http://localhost:3001').replace(/\/$/, ''); + +function escapeHtml(value) { + return String(value ?? '') + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); +} + +function screenPlayerUrl(slug) { + return `${PLAYER_PUBLIC_BASE_URL}/screen/${encodeURIComponent(slug)}`; +} + +function renderAdminShell(active, title, body, message) { + const navItems = [ + { key: 'dashboard', label: 'Dashboard', href: '/admin' }, + { key: 'playlists', label: 'Playlists', href: '/admin/playlists' }, + { key: 'screens', label: 'Screens', href: '/admin/screens' }, + { key: 'slides', label: 'Slides', href: '/admin/slides' }, + { key: 'templates', label: 'Slide templates', href: '/admin/templates' } + ].map((item) => ` + ${escapeHtml(item.label)} + `).join(''); + + return ` + + + + + ${escapeHtml(title)} - Pulse Signage + + + +
    + +
    + + ${body} +
    +
    + + `; +} + +module.exports = { + escapeHtml, + screenPlayerUrl, + renderAdminShell +}; diff --git a/src/webui/routes/dashboard/index.js b/src/webui/routes/dashboard/index.js new file mode 100644 index 0000000..0c67cf0 --- /dev/null +++ b/src/webui/routes/dashboard/index.js @@ -0,0 +1,15 @@ +const { renderView } = require('../../view'); + +module.exports = function renderDashboardPage(data, message, currentUser) { + return renderView('dashboard/index', { + title: 'Dashboard', + active: 'dashboard', + message: message, + currentUser: currentUser || null, + playlists: data.playlists || [], + screens: data.screens || [], + clients: data.clients || [], + slides: data.slides || [], + connectedClientsCount: Number(data.connectedClientsCount || 0) + }); +}; diff --git a/src/webui/routes/index.js b/src/webui/routes/index.js new file mode 100644 index 0000000..142dbbd --- /dev/null +++ b/src/webui/routes/index.js @@ -0,0 +1,24 @@ +module.exports = { + renderLoginPage: require('./auth/login'), + renderAccountPage: require('./account/password'), + renderUsersPage: require('./users/list'), + renderUsersAddPage: require('./users/add'), + renderUsersEditPage: require('./users/edit'), + renderDashboardPage: require('./dashboard/index'), + renderConnectedClientsPage: require('./clients/list'), + renderPlaylistsPage: require('./playlists/list'), + renderPlaylistFormPage: require('./playlists/add'), + renderPlaylistEditPage: require('./playlists/edit'), + renderPlaylistSlideConfigPage: require('./playlists/slide-config'), + renderScreensPage: require('./screens/list'), + renderScreenFormPage: require('./screens/add'), + renderScreenEditPage: require('./screens/edit'), + renderSlidesPage: require('./slides/list'), + renderSlideFormPage: require('./slides/form'), + renderTemplatesPage: require('./templates/list'), + renderTemplateFormPage: require('./templates/add'), + renderTemplateEditPage: require('./templates/edit'), + renderCanvasSizesPage: require('./canvas-sizes/list'), + renderCanvasSizeFormPage: require('./canvas-sizes/add'), + renderCanvasSizeEditPage: require('./canvas-sizes/edit') +}; diff --git a/src/webui/routes/playlists/add.js b/src/webui/routes/playlists/add.js new file mode 100644 index 0000000..2b041ae --- /dev/null +++ b/src/webui/routes/playlists/add.js @@ -0,0 +1,14 @@ +const { renderView } = require('../../view'); + +module.exports = function renderPlaylistFormPage(message, currentUser) { + return renderView('playlists/add', { + title: 'Create playlist', + active: 'playlists', + playlist: { + fade_between_slides: false + }, + message: message, + currentUser: currentUser || null + }); +}; + diff --git a/src/webui/routes/playlists/edit.js b/src/webui/routes/playlists/edit.js new file mode 100644 index 0000000..2198d4c --- /dev/null +++ b/src/webui/routes/playlists/edit.js @@ -0,0 +1,116 @@ +const { renderView } = require('../../view'); + +function formatDateTime(value) { + if (!value) { + return ''; + } + const date = new Date(value); + if (Number.isNaN(date.getTime())) { + return ''; + } + const year = date.getFullYear(); + const month = String(date.getMonth() + 1).padStart(2, '0'); + const day = String(date.getDate()).padStart(2, '0'); + const hours = String(date.getHours()).padStart(2, '0'); + const minutes = String(date.getMinutes()).padStart(2, '0'); + return `${year}-${month}-${day} ${hours}:${minutes}`; +} + +function formatTime(value) { + return value ? String(value).slice(0, 5) : ''; +} + +function formatDays(daysJson) { + const dayNames = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; + let days = []; + if (Array.isArray(daysJson)) { + days = daysJson; + } else if (daysJson) { + try { + const parsed = JSON.parse(daysJson); + days = Array.isArray(parsed) ? parsed : []; + } catch (_error) { + days = []; + } + } + return days.map((day) => dayNames[Number(day)] || '').filter(Boolean).join(', '); +} + +function scheduleSummary(item) { + const mode = String(item.schedule_mode || 'always'); + if (mode === 'dates') { + const start = formatDateTime(item.schedule_start_datetime); + const end = formatDateTime(item.schedule_end_datetime); + return start && end ? `Dates: ${start} to ${end}` : 'Dates: not set'; + } + if (mode === 'times') { + const days = formatDays(item.schedule_days_json); + const start = formatTime(item.schedule_start_time); + const end = formatTime(item.schedule_end_time); + return days && start && end ? `Times: ${days} ${start}-${end}` : 'Times: not set'; + } + return 'Always visible'; +} + +function getCanvasSignature(width, height) { + const normalizedWidth = Number(width); + const normalizedHeight = Number(height); + if (!Number.isFinite(normalizedWidth) || !Number.isFinite(normalizedHeight)) { + return null; + } + return normalizedWidth + 'x' + normalizedHeight; +} + +module.exports = function renderPlaylistEditPage(playlist, data, message, currentUser) { + const playlistSlides = (data.playlistSlides || []) + .filter((item) => item.playlist_id === playlist.id) + .sort((a, b) => { + if (a.position !== b.position) { + return a.position - b.position; + } + return a.id - b.id; + }) + .map((item, index, items) => Object.assign({}, item, { + displayOrder: index + 1, + isFirst: index === 0, + isLast: index === items.length - 1, + canvasSignature: getCanvasSignature(item.canvas_width, item.canvas_height) || '', + scheduleSummary: scheduleSummary(item) + })); + const assignedSlideIds = new Set(playlistSlides.map((item) => item.slide_id)); + const playlistCanvasSignatures = Array.from(new Set(playlistSlides.map((item) => getCanvasSignature(item.canvas_width, item.canvas_height)).filter(Boolean))); + const playlistCanvasSignature = playlistCanvasSignatures.length === 1 ? playlistCanvasSignatures[0] : null; + const playlistCanvasMismatch = playlistCanvasSignatures.length > 1; + const selectableSlides = (data.slides || []).filter((slide) => { + if (playlistCanvasMismatch) { + return false; + } + if (!playlistCanvasSignature) { + return true; + } + return getCanvasSignature(slide.canvas_width, slide.canvas_height) === playlistCanvasSignature; + }).map((slide) => Object.assign({}, slide, { + canvasSignature: getCanvasSignature(slide.canvas_width, slide.canvas_height) || '', + isAssigned: assignedSlideIds.has(slide.id) + })); + const hasAddableSlides = selectableSlides.some((slide) => !slide.isAssigned); + + return renderView('playlists/edit', { + title: 'Edit playlist', + active: 'playlists', + message: message, + currentUser: currentUser || null, + playlist: playlist, + playlistFadeBetweenSlides: Boolean(playlist.fade_between_slides), + playlistSlides: playlistSlides, + slides: data.slides || [], + hasSlides: (data.slides || []).length > 0, + hasSelectableSlides: selectableSlides.length > 0, + hasAddableSlides: hasAddableSlides, + selectableSlides: selectableSlides, + playlistCanvasSignature: playlistCanvasSignature, + playlistCanvasMismatch: playlistCanvasMismatch, + stylesheets: ['css/admin-editor.css'], + scripts: ['js/playlist-schedule.js'] + }); +}; diff --git a/src/webui/routes/playlists/list.js b/src/webui/routes/playlists/list.js new file mode 100644 index 0000000..8f5a7c3 --- /dev/null +++ b/src/webui/routes/playlists/list.js @@ -0,0 +1,21 @@ +const { renderView } = require('../../view'); + +module.exports = function renderPlaylistsPage(data, message, currentUser) { + const playlistSlides = data.playlistSlides || []; + const playlists = (data.playlists || []).map((playlist) => { + const slideCount = playlistSlides.filter((item) => item.playlist_id === playlist.id).length; + return Object.assign({}, playlist, { + slideCount: slideCount, + slideCountIsOne: slideCount === 1 + }); + }); + + return renderView('playlists/list', { + title: 'Playlists', + active: 'playlists', + message: message, + currentUser: currentUser || null, + playlists: playlists + }); +}; + diff --git a/src/webui/routes/playlists/slide-config.js b/src/webui/routes/playlists/slide-config.js new file mode 100644 index 0000000..439de0f --- /dev/null +++ b/src/webui/routes/playlists/slide-config.js @@ -0,0 +1,65 @@ +const { renderFragment } = require('../../view'); + +const DAY_OPTIONS = [ + { value: 0, label: 'Sun' }, + { value: 1, label: 'Mon' }, + { value: 2, label: 'Tue' }, + { value: 3, label: 'Wed' }, + { value: 4, label: 'Thu' }, + { value: 5, label: 'Fri' }, + { value: 6, label: 'Sat' } +]; + +function formatDateTimeInput(value) { + if (!value) { + return ''; + } + const date = new Date(value); + if (Number.isNaN(date.getTime())) { + return ''; + } + const year = date.getFullYear(); + const month = String(date.getMonth() + 1).padStart(2, '0'); + const day = String(date.getDate()).padStart(2, '0'); + const hours = String(date.getHours()).padStart(2, '0'); + const minutes = String(date.getMinutes()).padStart(2, '0'); + return `${year}-${month}-${day}T${hours}:${minutes}`; +} + +function formatTimeInput(value) { + if (!value) { + return ''; + } + return String(value).slice(0, 5); +} + +module.exports = function renderPlaylistSlideConfigPage(playlist, playlistSlide, message, rowKey, currentUser) { + const scheduleMode = String(playlistSlide.schedule_mode || 'always'); + const selectedDays = new Set(Array.isArray(playlistSlide.schedule_days) ? playlistSlide.schedule_days.map(Number) : []); + + return renderFragment('playlists/slide-config', { + title: 'Slide schedule', + hideFrameHeader: true, + hideThemeToggle: true, + frameBodyClass: 'admin-shell-body--frame-plain', + message: message, + currentUser: currentUser || null, + playlist: playlist, + playlistSlide: playlistSlide, + scheduleMode: scheduleMode, + isAlwaysMode: scheduleMode === 'always', + isDatesMode: scheduleMode === 'dates', + isTimesMode: scheduleMode === 'times', + rowKey: String(rowKey || ''), + startDateTimeValue: formatDateTimeInput(playlistSlide.schedule_start_datetime), + endDateTimeValue: formatDateTimeInput(playlistSlide.schedule_end_datetime), + startTimeValue: formatTimeInput(playlistSlide.schedule_start_time), + endTimeValue: formatTimeInput(playlistSlide.schedule_end_time), + dayOptions: DAY_OPTIONS.map((day) => ({ + ...day, + checked: selectedDays.has(day.value) + })), + stylesheets: ['css/admin-editor.css'], + scripts: ['js/playlist-schedule.js'] + }); +}; diff --git a/src/webui/routes/screens/add.js b/src/webui/routes/screens/add.js new file mode 100644 index 0000000..a21c4e4 --- /dev/null +++ b/src/webui/routes/screens/add.js @@ -0,0 +1,12 @@ +const { renderView } = require('../../view'); + +module.exports = function renderScreenFormPage(data, message, currentUser) { + return renderView('screens/add', { + title: 'Add screen', + active: 'screens', + message: message, + currentUser: currentUser || null, + playlists: data.playlists || [] + }); +}; + diff --git a/src/webui/routes/screens/edit.js b/src/webui/routes/screens/edit.js new file mode 100644 index 0000000..cef934c --- /dev/null +++ b/src/webui/routes/screens/edit.js @@ -0,0 +1,13 @@ +const { renderView } = require('../../view'); + +module.exports = function renderScreenEditPage(screen, data, message, currentUser) { + return renderView('screens/edit', { + title: 'Edit screen', + active: 'screens', + message: message, + currentUser: currentUser || null, + screen: screen, + playlists: data.playlists || [] + }); +}; + diff --git a/src/webui/routes/screens/list.js b/src/webui/routes/screens/list.js new file mode 100644 index 0000000..d969ef6 --- /dev/null +++ b/src/webui/routes/screens/list.js @@ -0,0 +1,12 @@ +const { renderView } = require('../../view'); + +module.exports = function renderScreensPage(data, message, currentUser) { + return renderView('screens/list', { + title: 'Screens', + active: 'screens', + message: message, + currentUser: currentUser || null, + screens: data.screens || [] + }); +}; + diff --git a/src/webui/routes/slides/form.js b/src/webui/routes/slides/form.js new file mode 100644 index 0000000..e5d21ff --- /dev/null +++ b/src/webui/routes/slides/form.js @@ -0,0 +1,27 @@ +const { renderView } = require('../../view'); + +module.exports = function renderSlideFormPage(data, mode, slide, message, currentUser) { + const templateRegions = data.templateRegions || []; + const templates = (data.templates || []).map((template) => ({ + ...template, + canvas_size_width: template.canvas_size_width, + canvas_size_height: template.canvas_size_height, + regions: templateRegions.filter((region) => Number(region.template_id) === Number(template.id)) + })); + return renderView('slides/form', { + title: mode === 'edit' ? 'Edit slide' : 'Create slide', + active: 'slides', + message: message, + isEdit: mode === 'edit', + action: mode === 'edit' ? `/admin/slides/${slide.id}` : '/admin/slides', + slide: slide || { title: '', template_id: null, content: {} }, + templates: templates, + slideEditorData: { + templates: templates, + existingTemplateId: slide && slide.template_id ? slide.template_id : null, + existingContent: slide && slide.content ? slide.content : {} + }, + currentUser: currentUser || null, + stylesheets: ['css/admin-editor.css'] + }); +}; diff --git a/src/webui/routes/slides/list.js b/src/webui/routes/slides/list.js new file mode 100644 index 0000000..f49bb01 --- /dev/null +++ b/src/webui/routes/slides/list.js @@ -0,0 +1,12 @@ +const { renderView } = require('../../view'); + +module.exports = function renderSlidesPage(data, message, currentUser) { + return renderView('slides/list', { + title: 'Slides', + active: 'slides', + message: message, + currentUser: currentUser || null, + slides: data.slides || [] + }); +}; + diff --git a/src/webui/routes/templates/add.js b/src/webui/routes/templates/add.js new file mode 100644 index 0000000..2ecea47 --- /dev/null +++ b/src/webui/routes/templates/add.js @@ -0,0 +1,65 @@ +const { renderView } = require('../../view'); + +function buildDefaultTemplate() { + return { + id: null, + name: '', + canvas_size_id: null, + canvas_size_width: 1920, + canvas_size_height: 1080, + background_image_path: '', + regions: [{ + region_key: 'region_1', + label: 'Region 1', + region_type: 'text', + x: 120, + y: 120, + width: 420, + height: 160, + z_index: 1 + }] + }; +} + +function resolveTemplateCanvasSize(template, canvasSizes) { + const currentWidth = Math.max(1, Number(template.canvas_size_width || 1920)); + const currentHeight = Math.max(1, Number(template.canvas_size_height || 1080)); + const sizeId = template.canvas_size_id ? Number(template.canvas_size_id) : null; + const matched = (canvasSizes || []).find((size) => Number(size.id) === sizeId) + || (canvasSizes || []).find((size) => Number(size.width) === currentWidth && Number(size.height) === currentHeight); + + if (matched) { + return Object.assign({}, template, { + canvas_size_id: matched.id, + canvas_size_label: matched.name, + canvas_size_width: matched.width, + canvas_size_height: matched.height + }); + } + + return Object.assign({}, template, { + canvas_size_id: '', + canvas_size_label: 'Current size (' + currentWidth + 'x' + currentHeight + ')', + canvas_size_width: currentWidth, + canvas_size_height: currentHeight + }); +} + +module.exports = function renderTemplateFormPage(template, mode, message, canvasSizes, currentUser) { + const isEdit = mode === 'edit'; + const current = resolveTemplateCanvasSize(template || buildDefaultTemplate(), canvasSizes || []); + if (!current.regions || !current.regions.length) { + current.regions = buildDefaultTemplate().regions; + } + + return renderView(isEdit ? 'templates/edit' : 'templates/add', { + title: isEdit ? 'Edit template' : 'Create Template', + active: 'templates', + message: message, + currentUser: currentUser || null, + template: current, + canvasSizes: canvasSizes || [], + stylesheets: ['css/template-designer.css'], + scripts: ['js/template-designer.js'] + }); +}; diff --git a/src/webui/routes/templates/edit.js b/src/webui/routes/templates/edit.js new file mode 100644 index 0000000..8671555 --- /dev/null +++ b/src/webui/routes/templates/edit.js @@ -0,0 +1,5 @@ +const renderTemplateFormPage = require('./add'); + +module.exports = function renderTemplateEditPage(template, data, message, currentUser) { + return renderTemplateFormPage(template, 'edit', message, data && data.canvasSizes ? data.canvasSizes : [], currentUser); +}; diff --git a/src/webui/routes/templates/list.js b/src/webui/routes/templates/list.js new file mode 100644 index 0000000..db48291 --- /dev/null +++ b/src/webui/routes/templates/list.js @@ -0,0 +1,19 @@ +const { renderView } = require('../../view'); + +module.exports = function renderTemplatesPage(data, message, currentUser) { + const templates = (data.templates || []).map((template) => ({ + id: template.id, + name: template.name, + canvas_size_width: template.canvas_size_width, + canvas_size_height: template.canvas_size_height, + regionCount: (data.templateRegions || []).filter((region) => region.template_id === template.id).length + })); + + return renderView('templates/list', { + title: 'Slide templates', + active: 'templates', + message: message, + currentUser: currentUser || null, + templates: templates + }); +}; diff --git a/src/webui/routes/users/add.js b/src/webui/routes/users/add.js new file mode 100644 index 0000000..5a9290b --- /dev/null +++ b/src/webui/routes/users/add.js @@ -0,0 +1,10 @@ +const { renderView } = require('../../view'); + +module.exports = function renderUsersAddPage(message, currentUser) { + return renderView('users/add', { + title: 'Add user', + active: 'users', + message: message, + currentUser: currentUser || null + }); +}; \ No newline at end of file diff --git a/src/webui/routes/users/edit.js b/src/webui/routes/users/edit.js new file mode 100644 index 0000000..df95a8c --- /dev/null +++ b/src/webui/routes/users/edit.js @@ -0,0 +1,11 @@ +const { renderView } = require('../../view'); + +module.exports = function renderUsersEditPage(user, message, currentUser) { + return renderView('users/edit', { + title: 'Edit user', + active: 'users', + message: message, + currentUser: currentUser || null, + user: user + }); +}; \ No newline at end of file diff --git a/src/webui/routes/users/list.js b/src/webui/routes/users/list.js new file mode 100644 index 0000000..d1bf23f --- /dev/null +++ b/src/webui/routes/users/list.js @@ -0,0 +1,11 @@ +const { renderView } = require('../../view'); + +module.exports = function renderUsersPage(data, message, currentUser) { + return renderView('users/list', { + title: 'Users', + active: 'users', + message: message, + currentUser: currentUser || null, + users: data.users || [] + }); +}; \ No newline at end of file diff --git a/src/webui/view.js b/src/webui/view.js new file mode 100644 index 0000000..87a54a4 --- /dev/null +++ b/src/webui/view.js @@ -0,0 +1,68 @@ +const fs = require('fs'); +const path = require('path'); +const Handlebars = require('handlebars'); + +const VIEWS_ROOT = path.join(__dirname, 'views'); +const cache = new Map(); + +Handlebars.registerHelper('eq', function (left, right) { + return left === right; +}); + +Handlebars.registerHelper('playerUrl', function (slug) { + const base = (process.env.PLAYER_PUBLIC_BASE_URL || process.env.PLAYER_BASE_URL || 'http://localhost:3001').replace(/\/$/, ''); + return `${base}/screen/${encodeURIComponent(slug)}`; +}); + +Handlebars.registerHelper('json', function (value) { + return new Handlebars.SafeString(JSON.stringify(value).replace(/ +
    +

    My account

    +

    Update your display name and password.

    +
    + + + + + \ No newline at end of file diff --git a/src/webui/views/canvas-sizes/add.hbs b/src/webui/views/canvas-sizes/add.hbs new file mode 100644 index 0000000..c040092 --- /dev/null +++ b/src/webui/views/canvas-sizes/add.hbs @@ -0,0 +1,24 @@ + + +
    +
    + +
    + + +
    + +
    +
    diff --git a/src/webui/views/canvas-sizes/edit.hbs b/src/webui/views/canvas-sizes/edit.hbs new file mode 100644 index 0000000..5eecaf3 --- /dev/null +++ b/src/webui/views/canvas-sizes/edit.hbs @@ -0,0 +1,24 @@ + + +
    +
    + +
    + + +
    + +
    +
    diff --git a/src/webui/views/canvas-sizes/list.hbs b/src/webui/views/canvas-sizes/list.hbs new file mode 100644 index 0000000..420f838 --- /dev/null +++ b/src/webui/views/canvas-sizes/list.hbs @@ -0,0 +1,35 @@ + + +
    +

    Saved sizes

    + + + + {{#if canvasSizes.length}} + {{#each canvasSizes}} + + + + + + + {{/each}} + {{else}} + + {{/if}} + +
    NameDimensionsTemplatesActions
    {{name}}{{width}}x{{height}}{{templateCount}} +
    + Edit +
    + +
    +
    +
    No canvas sizes yet.
    +
    diff --git a/src/webui/views/clients/list.hbs b/src/webui/views/clients/list.hbs new file mode 100644 index 0000000..fdce775 --- /dev/null +++ b/src/webui/views/clients/list.hbs @@ -0,0 +1,98 @@ + + +
    +

    Active connections

    + + + + {{#if clients.length}} + {{#each clients}} + + + + + + + + + {{/each}} + {{else}} + + {{/if}} + +
    Client/Connection IDIPViewportConnectedScreenActions
    + {{#if clientId}} +
    {{clientId}}
    + {{else}} + Unknown + {{/if}} + {{#if id}} +
    {{id}}
    + {{/if}} +
    + {{#if clientIp}} + {{clientIp}} + {{else}} + {{#if remoteAddress}} + {{remoteAddress}} + {{else}} + Unknown + {{/if}} + {{/if}} + + {{#if viewport}} + {{viewport.width}}x{{viewport.height}} + {{else}} + Unknown + {{/if}} + + {{#if connectedAt}} +
    {{connectedAtLabel}}
    + {{#if lastSeenAt}} +
    {{lastSeenAtLabel}}
    + {{/if}} + {{else}} + Unknown + {{/if}} +
    +
    {{screen_name}}
    + {{#if currentSlideTitle}} +
    Showing: {{currentSlideTitle}}
    + {{else}} +
    No slide currently showing
    + {{/if}} +
    +
    +
    + + + +
    +
    + + + +
    +
    + + + +
    +
    + + + +
    +
    + + + +
    +
    +
    No connected clients yet.
    +
    \ No newline at end of file diff --git a/src/webui/views/dashboard/index.hbs b/src/webui/views/dashboard/index.hbs new file mode 100644 index 0000000..95a5bcf --- /dev/null +++ b/src/webui/views/dashboard/index.hbs @@ -0,0 +1,55 @@ + + +
    +
    {{playlists.length}}Playlists
    +
    {{slides.length}}Slides
    +
    {{screens.length}}Screens
    +
    {{connectedClientsCount}}Connected clients
    +
    + +
    +

    Quick actions

    +
    +
    + + +
    +
    + + + +
    +
    +
    + +
    +

    Current screens

    + + + + {{#if screens.length}} + {{#each screens}} + + + + + + + {{/each}} + {{else}} + + {{/if}} + +
    NamePlayer URLPlaylistConnected clients
    {{name}}{{playerUrl slug}}{{playlist_name}} + {{#if player_connection_count}} +
    {{player_connection_count}} connected
    + {{else}} + No clients connected. + {{/if}} +
    No screens yet.
    +
    diff --git a/src/webui/views/frame-layout.hbs b/src/webui/views/frame-layout.hbs new file mode 100644 index 0000000..e5cd9c4 --- /dev/null +++ b/src/webui/views/frame-layout.hbs @@ -0,0 +1,65 @@ + + + + + + {{title}} - Signage + + + {{#if stylesheets.length}} + {{#each stylesheets}} + + {{/each}} + {{/if}} + + +
    +
    + {{#unless hideThemeToggle}} + + {{/unless}} + {{#unless hideFrameHeader}} +
    +
    + Slide workspace +

    {{title}}

    +
    +
    + {{/unless}} +
    +
    + {{{body}}} +
    +
    +
    +
    + + {{#if scripts.length}} + {{#each scripts}} + + {{/each}} + {{/if}} + + diff --git a/src/webui/views/layout.hbs b/src/webui/views/layout.hbs new file mode 100644 index 0000000..59b8eb6 --- /dev/null +++ b/src/webui/views/layout.hbs @@ -0,0 +1,127 @@ + + + + + + {{title}} - Signage + + + {{#if stylesheets.length}} + {{#each stylesheets}} + + {{/each}} + {{/if}} + + + {{#if authShell}} + +
    + {{{body}}} +
    + + {{else}} + {{#if message}} +
    +
    {{message}}
    + +
    + {{/if}} +
    + + + + +
    + + +
    +
    + {{{body}}} +
    +
    +
    +
    + + {{/if}} + {{#if scripts.length}} + {{#each scripts}} + + {{/each}} + {{/if}} + + diff --git a/src/webui/views/login.hbs b/src/webui/views/login.hbs new file mode 100644 index 0000000..ea035c9 --- /dev/null +++ b/src/webui/views/login.hbs @@ -0,0 +1,28 @@ +
    +
    +
    +
    PS
    +
    +

    Sign in

    +

    Sign in with your account.

    +
    +
    + + {{#if message}} +
    {{message}}
    + {{/if}} + +
    + + + +
    + +
    +
    \ No newline at end of file diff --git a/src/webui/views/playlists/add.hbs b/src/webui/views/playlists/add.hbs new file mode 100644 index 0000000..a1aeff6 --- /dev/null +++ b/src/webui/views/playlists/add.hbs @@ -0,0 +1,24 @@ + + +
    +
    + +
    + Fade: + +
    +
    Disabled by default.
    + +
    +
    diff --git a/src/webui/views/playlists/edit.hbs b/src/webui/views/playlists/edit.hbs new file mode 100644 index 0000000..a376dac --- /dev/null +++ b/src/webui/views/playlists/edit.hbs @@ -0,0 +1,119 @@ + + +
    +
    +
    + +
    +
    + Fade: + +
    + + +
    +
    +
    + +
    +

    Playlist items

    + + + + {{#if playlistSlides.length}} + {{#each playlistSlides}} + + + + + + + + {{/each}} + {{else}} + + {{/if}} + +
    OrderSlideDurationScheduleActions
    {{displayOrder}} + {{title}} + + + + +
    {{scheduleSummary}}
    + + + + + + +
    +
    + + + + +
    +
    No slides assigned yet.
    +
    +
    + +
    +

    Add slide to playlist

    + {{#if hasSlides}} + {{#if hasSelectableSlides}} +
    +
    + + +
    + +
    + + {{else}} +
    + {{#if playlistCanvasMismatch}} + This playlist contains slides with different canvas sizes. Remove the mismatched items before adding more. + {{else if playlistCanvasSignature}} + No slides match this playlist canvas size. + {{else}} + No slides are currently available for this playlist. + {{/if}} +
    + {{/if}} + {{else}} +
    Create slides first.
    + {{/if}} +
    + +
    + + +
    +
    + Slide schedule +
    Configure availability for this playlist item.
    +
    + +
    + +
    diff --git a/src/webui/views/playlists/list.hbs b/src/webui/views/playlists/list.hbs new file mode 100644 index 0000000..27b180b --- /dev/null +++ b/src/webui/views/playlists/list.hbs @@ -0,0 +1,34 @@ + + +
    +

    Existing playlists

    + + + + {{#if playlists.length}} + {{#each playlists}} + + + + + + {{/each}} + {{else}} + + {{/if}} + +
    NameSlidesActions
    {{name}}{{slideCount}} slide{{#unless slideCountIsOne}}s{{/unless}} +
    + Edit +
    + +
    +
    +
    No playlists yet.
    +
    diff --git a/src/webui/views/playlists/slide-config.hbs b/src/webui/views/playlists/slide-config.hbs new file mode 100644 index 0000000..6d11d36 --- /dev/null +++ b/src/webui/views/playlists/slide-config.hbs @@ -0,0 +1,48 @@ +
    + + + + +
    +

    Date range

    +
    + + +
    +
    + +
    +

    Time range

    +
    + + +
    +
    + {{#each dayOptions}} + + {{/each}} +
    +
    + +
    + +
    +
    diff --git a/src/webui/views/screens/add.hbs b/src/webui/views/screens/add.hbs new file mode 100644 index 0000000..a2e56ef --- /dev/null +++ b/src/webui/views/screens/add.hbs @@ -0,0 +1,24 @@ + + +
    +
    + + + +
    +
    diff --git a/src/webui/views/screens/edit.hbs b/src/webui/views/screens/edit.hbs new file mode 100644 index 0000000..60aef08 --- /dev/null +++ b/src/webui/views/screens/edit.hbs @@ -0,0 +1,37 @@ + + +
    +

    Edit screen

    +
    + + +
    + +
    + +
    + +
    +
    +
    + +
    +

    Player URL

    +

    {{playerUrl screen.slug}}

    +
    + diff --git a/src/webui/views/screens/list.hbs b/src/webui/views/screens/list.hbs new file mode 100644 index 0000000..1135f28 --- /dev/null +++ b/src/webui/views/screens/list.hbs @@ -0,0 +1,42 @@ + + +
    +

    Existing screens

    + + + + {{#if screens.length}} + {{#each screens}} + + + + + + + + {{/each}} + {{else}} + + {{/if}} + +
    NamePlayer URLPlaylistConnected clientsActions
    {{name}}{{playerUrl slug}}{{playlist_name}} + {{#if player_connection_count}} +
    {{player_connection_count}} connected
    + {{else}} + No clients connected. + {{/if}} +
    +
    + Edit +
    + +
    +
    +
    No screens yet.
    +
    diff --git a/src/webui/views/slides/form.hbs b/src/webui/views/slides/form.hbs new file mode 100644 index 0000000..f53fe88 --- /dev/null +++ b/src/webui/views/slides/form.hbs @@ -0,0 +1,50 @@ + + +
    +
    +
    +
    + + +
    +
    +
    +
    +
    +
    +

    Preview

    +
    Select a template to preview the slide.
    +
    +
    + +
    + +
    Select a template to preview the slide.
    +
    +
    +
    +
    +
    + + + + + + + + diff --git a/src/webui/views/slides/list.hbs b/src/webui/views/slides/list.hbs new file mode 100644 index 0000000..ed4ff2f --- /dev/null +++ b/src/webui/views/slides/list.hbs @@ -0,0 +1,34 @@ + + +
    +

    Existing slides

    + + + + {{#if slides.length}} + {{#each slides}} + + + + + + {{/each}} + {{else}} + + {{/if}} + +
    TitleTemplateActions
    {{title}}{{template_name}} +
    + Edit +
    + +
    +
    +
    No slides yet.
    +
    diff --git a/src/webui/views/templates/add.hbs b/src/webui/views/templates/add.hbs new file mode 100644 index 0000000..ac7d319 --- /dev/null +++ b/src/webui/views/templates/add.hbs @@ -0,0 +1,72 @@ + + +
    +
    +
    + + + +
    +
    + + + + + + + + +
    +
    +
    +
    + +
    No background image selected
    +
    +
    +
    +
    +
    + +
    +
    + + + + +
    +

    Region information

    + +
    +
    +
    +
    +
    + + diff --git a/src/webui/views/templates/edit.hbs b/src/webui/views/templates/edit.hbs new file mode 100644 index 0000000..4334609 --- /dev/null +++ b/src/webui/views/templates/edit.hbs @@ -0,0 +1,72 @@ + + +
    +
    +
    + + + +
    +
    + + + + + + + + +
    +
    +
    +
    + +
    No background image selected
    +
    +
    +
    +
    +
    + +
    +
    + + + + +
    +

    Region information

    + +
    +
    +
    +
    +
    + + diff --git a/src/webui/views/templates/list.hbs b/src/webui/views/templates/list.hbs new file mode 100644 index 0000000..4edff47 --- /dev/null +++ b/src/webui/views/templates/list.hbs @@ -0,0 +1,35 @@ + + +
    +

    Existing templates

    + + + + {{#if templates.length}} + {{#each templates}} + + + + + + + {{/each}} + {{else}} + + {{/if}} + +
    NameCanvasRegionsActions
    {{name}}{{canvas_size_width}}x{{canvas_size_height}}{{regionCount}} +
    + Edit +
    + +
    +
    +
    No templates yet.
    +
    diff --git a/src/webui/views/users/add.hbs b/src/webui/views/users/add.hbs new file mode 100644 index 0000000..31865f3 --- /dev/null +++ b/src/webui/views/users/add.hbs @@ -0,0 +1,43 @@ + + +
    +
    +
    +

    Account details

    +
    + + +
    +
    + +
    +

    Set password

    +
    + + +
    +
    + +
    + + Back to users +
    +
    +
    \ No newline at end of file diff --git a/src/webui/views/users/edit.hbs b/src/webui/views/users/edit.hbs new file mode 100644 index 0000000..b597126 --- /dev/null +++ b/src/webui/views/users/edit.hbs @@ -0,0 +1,49 @@ + + +
    +
    +
    + +
    + {{user.username}} + {{#if user.isCurrentUser}} +
    Current signed-in user
    + {{/if}} +
    +
    + Back to users +
    + +
    +
    +

    Edit user

    + + + +
    + +
    +

    Reset password

    + + + +
    +
    +
    \ No newline at end of file diff --git a/src/webui/views/users/list.hbs b/src/webui/views/users/list.hbs new file mode 100644 index 0000000..bbe316f --- /dev/null +++ b/src/webui/views/users/list.hbs @@ -0,0 +1,62 @@ + + +
    +

    Existing users

    + + + + + + + + + + {{#if users.length}} + {{#each users}} + + + + + + {{/each}} + {{else}} + + {{/if}} + +
    UserNameActions
    +
    + +
    + {{username}} + {{#if isCurrentUser}} +
    Current signed-in user
    + {{/if}} +
    +
    +
    +
    +
    + {{#if name}}{{name}}{{else}}-{{/if}} +
    +
    +
    +
    + {{#unless isCurrentUser}} + Edit +
    + +
    + {{else}} + Protected + {{/unless}} +
    +
    No users found.
    +
    \ No newline at end of file