diff --git a/assets/css/abstracts/_a11y.scss b/assets/css/abstracts/_a11y.scss new file mode 100644 index 0000000..c788b23 --- /dev/null +++ b/assets/css/abstracts/_a11y.scss @@ -0,0 +1,13 @@ +// -------------------------------------------------- +// A11Y +// -------------------------------------------------- + +@mixin visually-hidden { + position: absolute; + width: 1px; + height: 1px; + overflow: hidden; + clip: rect(0 0 0 0); // Legacy declaration for Internet Explorer + clip-path: inset(50%); + white-space: nowrap; +} diff --git a/assets/css/abstracts/_index.scss b/assets/css/abstracts/_index.scss new file mode 100644 index 0000000..a29e856 --- /dev/null +++ b/assets/css/abstracts/_index.scss @@ -0,0 +1,6 @@ +// -------------------------------------------------- +// ABSTRACTS (INDEX) +// -------------------------------------------------- + +@forward "a11y"; +@forward "variables"; diff --git a/assets/css/abstracts/_variables.scss b/assets/css/abstracts/_variables.scss new file mode 100644 index 0000000..fcdbc8f --- /dev/null +++ b/assets/css/abstracts/_variables.scss @@ -0,0 +1,40 @@ +// -------------------------------------------------- +// VARIABLES +// -------------------------------------------------- + +// ----- FONTS + +$default-font-family: Helvetica, Arial, sans-serif; +$default-font-size: 0.9rem; +$default-line-height: 1.15; + +// ----- COLORS + +$black: #000; +$white: #fff; + +// ----- DIMENSIONS (viewport range for fluid dimensions: 48rem <=> 120rem) + +// Media queries +$tablet-media-query: 48rem; // => 768px +$desktop-media-query: 62rem; // => 992px + +// Content +$content-width-xs: 40rem; // => 640px +$content-width-s: 60rem; // => 960px +$content-width-m: 80rem; // => 1280px +$content-width-l: 100rem; // => 1600px +$content-width-xl: 120rem; // => 1920px + +// Logo +$logo-width-portrait: clamp(2rem, 1.333rem + 1.389vw, 3rem); +$logo-width-landscape: calc($logo-width-portrait * 2); + +// Sidebars +$sidebar-width-portrait: calc($logo-width-portrait + clamp(1rem, -4.333rem + 11.111vw, 9rem)); +$sidebar-width-landscape: calc($logo-width-landscape + clamp(1rem, -4.333rem + 11.111vw, 9rem)); +$sidebar-padding-y: clamp(0.75rem, 0.25rem + 1.042vw, 1.5rem); + +// Main +$main-padding-y-portrait: calc(($sidebar-padding-y * 2) + ($logo-width-portrait * 2)); +$main-padding-y-landscape: calc(($sidebar-padding-y * 2) + ($logo-width-landscape / 2)); diff --git a/assets/css/base/_base.scss b/assets/css/base/_base.scss new file mode 100644 index 0000000..f9a2fea --- /dev/null +++ b/assets/css/base/_base.scss @@ -0,0 +1,96 @@ +@use "../abstracts" as *; + +// -------------------------------------------------- +// BASE STYLE +// -------------------------------------------------- + +body { + color: $black; + background-color: $white; + font-family: $default-font-family; + font-size: $default-font-size; + line-height: $default-line-height; +} + +a { + color: $black; + text-decoration: underline; + + &:focus-visible { + outline: 1px dashed $black; + outline-offset: 2px; + } +} + +strong { + font-weight: 700; +} + +em { + font-style: italic; +} + +// -------------------------------------------------- +// BASE LAYOUT +// -------------------------------------------------- + +body { + display: flex; + flex-direction: column; + align-items: center; + + #app { + position: relative; + display: grid; + grid-template-columns: $sidebar-width-portrait minmax(min-content, $content-width-m) $sidebar-width-portrait; + justify-content: space-evenly; + justify-items: center; + width: 100%; + max-width: $content-width-xl; + + @media (orientation: landscape) { + grid-template-columns: $sidebar-width-landscape minmax(min-content, $content-width-m) $sidebar-width-landscape; + } + + header { + grid-column: 1; + display: flex; + flex-direction: column; + align-items: center; + position: sticky; + top: 0; + width: 100%; + height: 100vh; // Fallback in case dvh unit is not supported + height: 100dvh; + padding: $sidebar-padding-y 0; + + .header__logo { + display: block; + position: relative; + width: $logo-width-portrait; + aspect-ratio: calc(1 / 2); + background-image: url("/public/images/signature-portrait.svg"); + background-size: contain; + background-position: center; + + @media (orientation: landscape) { + width: $logo-width-landscape; + aspect-ratio: calc(2 / 1); + background-image: url("/public/images/signature-landscape.svg"); + } + } + } + + main { + grid-column: 2; + display: flex; + flex-direction: column; + align-items: center; + padding: $main-padding-y-portrait 0; + + @media (orientation: landscape) { + padding: $main-padding-y-landscape 0; + } + } + } +} diff --git a/assets/css/base/_index.scss b/assets/css/base/_index.scss new file mode 100644 index 0000000..788976a --- /dev/null +++ b/assets/css/base/_index.scss @@ -0,0 +1,6 @@ +// -------------------------------------------------- +// BASE (INDEX) +// -------------------------------------------------- + +@forward "minireset"; +@forward "base"; diff --git a/assets/css/utils/minireset.css b/assets/css/base/_minireset.css similarity index 78% rename from assets/css/utils/minireset.css rename to assets/css/base/_minireset.css index 2023d39..26ec2a6 100644 --- a/assets/css/utils/minireset.css +++ b/assets/css/base/_minireset.css @@ -1,6 +1,6 @@ -/* ---------------------------------------------------------------------------- +/* -------------------------------------------------- MINIRESET V0.0.6 ----------------------------------------------------------------------------- */ +-------------------------------------------------- */ html, body, diff --git a/assets/css/main.scss b/assets/css/main.scss index 93ed18a..dba57a9 100644 --- a/assets/css/main.scss +++ b/assets/css/main.scss @@ -1,178 +1,7 @@ -@use 'utils/minireset'; -@use '@splidejs/splide/dist/css/splide-core.min'; -@use 'partials/variables' as *; -@use 'partials/animations'; - -// ---------------------------------------------------------------------------- -// GENERALITIES -// ---------------------------------------------------------------------------- - -// Fonts and colors - -body { - font-family: var(--text-font-family); - font-size: var(--text-font-size); - line-height: var(--text-line-height); - color: var(--black); -} - -strong { - font-weight: var(--bold-font-weight); -} - -em { - font-style: italic; -} - -// Link style - -a { - color: var(--black); - text-decoration: none; - transition: text-decoration 200ms ease-in-out; - - &:hover, - &:focus, - &:active { - text-decoration: underline; - } - - &:focus-visible { - outline: 1px dashed var(--black); - outline-offset: 2px; - animation: expand-outline 200ms ease-in-out; - } -} - -// General layout - -body { - min-height: 100vh; - overflow-x: hidden; -} - -// ---------------------------------------------------------------------------- +// -------------------------------------------------- // MAIN -// ---------------------------------------------------------------------------- +// -------------------------------------------------- -// Home section - -.home-section { - position: relative; - z-index: 1; - width: 100%; - min-height: 100vh; - padding: var(--sidebar-padding); - display: flex; - flex-direction: column; - justify-content: space-between; - align-items: flex-start; -} - -.navigation { - max-width: 100%; - - &__item { - - + .navigation__item { - margin: 0.3rem 0 0 0; - } - } - - &__link { - display: block; - max-width: fit-content; - overflow-x: hidden; - white-space: nowrap; - text-overflow: ellipsis; - font-weight: var(--bold-font-weight); - transition: color 200ms ease-in-out; - - &--white { - color: var(--white); - - &:focus-visible { - outline: 1px dashed var(--white); - } - } - } -} - -.social { - width: 100%; - display: flex; - justify-content: center; - align-items: center; - margin: 0.8rem 0 0 0; - - &__link { - width: var(--icon-size); - height: var(--icon-size); - display: flex; - justify-content: center; - align-items: center; - - + .social__link { - margin: 0 0 0 0.8rem; - } - - svg { - width: 100%; - height: 100%; - } - - &--white { - - &:focus-visible { - outline: 1px dashed var(--white); - } - } - } -} - -@media screen and (min-width: $tablet-media-query) { - - .navigation { - - &__item { - - + .navigation__item { - margin: 0.2rem 0 0 0; - } - } - } - - .social { - justify-content: flex-start; - } -} - -@media screen and (min-width: $desktop-media-query) { - - .navigation { - - &__item { - - + .navigation__item { - margin: 0.1rem 0 0 0; - } - } - } -} - -// Slider section - -.slider-section { - position: fixed; - top: 0; - left: 0; -} - -.splide { - - &__image { - width: 100%; - height: 100%; - object-fit: cover; - } -} +@use "@splidejs/splide/dist/css/splide-core.min"; +@use "base" as *; +@use "pages" as *; diff --git a/assets/css/pages/_cv.scss b/assets/css/pages/_cv.scss new file mode 100644 index 0000000..c0473bd --- /dev/null +++ b/assets/css/pages/_cv.scss @@ -0,0 +1,39 @@ +@use "../abstracts" as *; + +// -------------------------------------------------- +// CV STYLE +// -------------------------------------------------- + +.cv { + + .cv__title { + @include visually-hidden; + } + + .cv__content { + display: grid; + grid-template-columns: auto 1fr; + gap: 0.5rem; + max-width: $content-width-s; + + > h2 { + grid-column: 1 / span 2; + margin: 0.5rem 0; + } + + > h3 { + grid-column: 1 / span 1; + } + + > p { + grid-column: 1 / span 2; + } + + > ul { + grid-column: 2 / span 1; + display: flex; + flex-direction: column; + gap: 0.25rem; + } + } +} diff --git a/assets/css/pages/_home.scss b/assets/css/pages/_home.scss new file mode 100644 index 0000000..a56980f --- /dev/null +++ b/assets/css/pages/_home.scss @@ -0,0 +1,60 @@ +@use "../abstracts" as *; + +// -------------------------------------------------- +// HOME STYLE +// -------------------------------------------------- + +.home { + + .home__title { + @include visually-hidden; + } + + .home__gallery { + display: flex; + flex-direction: column; + align-items: center; + gap: 2rem; + + .home__gallery-item { + display: table; + + .home__carousel { + + &:where(.splide) { + cursor: pointer; + + .splide__track { + + .splide__list { + position: relative; + + .splide__slide { + transform: none !important; + + &:not(.is-active) { + position: absolute; + inset: 0; + } + + .splide__image { + max-height: 80vh; + } + } + } + } + } + } + + .home__main-image { + max-height: 80vh; + } + + .home__image-caption { + display: table-caption; + caption-side: bottom; + margin: 0.25rem 0 0; + } + } + } +} diff --git a/assets/css/pages/_index.scss b/assets/css/pages/_index.scss new file mode 100644 index 0000000..d3dce69 --- /dev/null +++ b/assets/css/pages/_index.scss @@ -0,0 +1,6 @@ +// -------------------------------------------------- +// PAGES (INDEX) +// -------------------------------------------------- + +@forward "cv"; +@forward "home"; diff --git a/assets/css/panel.scss b/assets/css/panel.scss index 093f3c7..5bb0311 100644 --- a/assets/css/panel.scss +++ b/assets/css/panel.scss @@ -1,28 +1,28 @@ -// ---------------------------------------------------------------------------- +// -------------------------------------------------- // KIRBY PANEL CUSTOMIZATION -// ---------------------------------------------------------------------------- +// -------------------------------------------------- -// Textarea headline buttons +// List item layout +.k-item[data-layout=list] { + --item-height: 3rem; +} -.k-textarea-field { +// Image gallery structure field +.k-field-name-image_gallery { - .k-toolbar { + table { - .k-dropdown { - - .k-button:nth-of-type(2), - .k-button:nth-of-type(3) { - display: none; - } + tbody { + --bubble-size: 7.5em; + --table-row-height: 100%; } } } -// Visual image crop field properties +// CV writer field +.k-field-name-cv { -.kirby-imagecrop-field { - - .k-column:nth-of-type(2) { - display: none; + .k-text { + min-height: 7.5rem; } } diff --git a/assets/css/partials/animations.scss b/assets/css/partials/animations.scss deleted file mode 100644 index 5a7fe5a..0000000 --- a/assets/css/partials/animations.scss +++ /dev/null @@ -1,12 +0,0 @@ -// ---------------------------------------------------------------------------- -// ANIMATIONS -// ---------------------------------------------------------------------------- - -@keyframes expand-outline { - 0% { - outline-offset: 0; - } - 100% { - outline-offset: 2px; - } -} diff --git a/assets/css/partials/variables.scss b/assets/css/partials/variables.scss deleted file mode 100644 index eace197..0000000 --- a/assets/css/partials/variables.scss +++ /dev/null @@ -1,59 +0,0 @@ -// ---------------------------------------------------------------------------- -// VARIABLES -// ---------------------------------------------------------------------------- - -:root { - - // Fonts - - --text-font-family: Helvetica, Arial, sans-serif; - --regular-font-weight: 400; - --medium-font-weight: 500; - --semi-bold-font-weight: 600; - --bold-font-weight: 700; - --text-font-size: 1rem; - --text-line-height: 1.1; - - // Dimensions - - --icon-size: 3rem; - --sidebar-padding: 0.8rem; - - // Colors - - --black: #000; - --white: #fff; -} - -// Media queries - -$tablet-media-query: 48rem; -$desktop-media-query: 62rem; - -@media screen and (min-width: $tablet-media-query) { - - :root { - - // Fonts - - --text-font-size: 0.95rem; - - // Dimensions - - --icon-size: 2.8rem; - } -} - -@media screen and (min-width: $desktop-media-query) { - - :root { - - // Fonts - - --text-font-size: 0.9rem; - - // Dimensions - - --icon-size: 2.6rem; - } -} diff --git a/assets/js/app.js b/assets/js/app.js index f9f548e..75e1f41 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -5,165 +5,42 @@ import create from 'swiped-events'; // DATA // ---------------------------------------------------------------------------- -// UTILS - -const body = document.body; - -// SLIDER - -const homeSection = document.querySelector('.home-section'); -const navLinks = document.querySelectorAll('.navigation__link'); -const socialLinks = document.querySelectorAll('.social__link'); -const sliderSection = document.querySelector('.slider-section'); -let slider; -const slides = document.querySelectorAll('.splide__slide'); -let cursorOrientation; -let cursorColor; +// const body = document.body; +const homeCarousels = document.querySelectorAll('.home__carousel'); // ---------------------------------------------------------------------------- // LOGIC // ---------------------------------------------------------------------------- -// UTILS - -// Enable CSS :active pseudo-class in Safari Mobile -function enableActivePseudoClass() { - document.addEventListener("touchstart", function() {},false); -} - // Convert rem to pixels by getting font-size CSS property -function convertRemToPixels(rem) { - let fontSize = parseFloat(window.getComputedStyle(body).getPropertyValue('font-size')); - return rem * fontSize; -} +// function convertRemToPixels(rem) { +// let fontSize = parseFloat(window.getComputedStyle(body).getPropertyValue('font-size')); +// return rem * fontSize; +// } -// SLIDER - -function setUpSlider() { - if (sliderSection) { - slider = new Splide('.splide', { - type: 'fade', - rewind: true, - rewindByDrag: true, - speed: 400, - width: '100vw', - height: '100vh', - arrows: false, - pagination: false, - easing: 'ease-in-out', - drag: true - }); - } -} - -function mountSlider() { - if (slider) { - slider.mount(); - } -} - -function changeSlideOnClick() { - if (homeSection && slider) { - homeSection.addEventListener('click', function(e) { - if (!e.target.closest('.navigation__link') && !e.target.closest('.social__link')) { - if (e.clientX >= window.innerWidth / 2) { - slider.go('>'); - } else { - slider.go('<'); - } - } - }); - } -} - -function changeSlideOnSwipe() { - if (homeSection && slider) { - homeSection.addEventListener('swiped', function(e) { - if (e.detail.dir === 'left') { +// Set up multiple Splide carousels +function setUpCarousels() { + if (homeCarousels.length) { + homeCarousels.forEach((item) => { + // Create Splide instance + let slider = new Splide(item, { + arrows: false, + drag: true, + easing: 'ease-in-out', + pagination: false, + rewind: true, + rewindByDrag: true, + role: 'undefined', + speed: 0, + type: 'fade', + width: 'auto', + }); + // Mount slider + slider.mount(); + // Change slide on click + slider.on('click', () => { slider.go('>'); - } else if (e.detail.dir === 'right') { - slider.go('<'); - } - }); - } -} - -function turnSidebarLinksToWhite() { - if (navLinks) { - for (let i = 0; i < navLinks.length; i++) { - navLinks[i].classList.add('navigation__link--white'); - } - } - if (socialLinks) { - for (let i = 0; i < socialLinks.length; i++) { - socialLinks[i].classList.add('social__link--white'); - } - } -} - -function turnSidebarLinksToBlack() { - if (navLinks) { - for (let i = 0; i < navLinks.length; i++) { - navLinks[i].classList.remove('navigation__link--white'); - } - } - if (socialLinks) { - for (let i = 0; i < socialLinks.length; i++) { - socialLinks[i].classList.remove('social__link--white'); - } - } -} - -function editColorTheme(slide) { - if (slide) { - if (slide.getAttribute('data-text-color') === 'white') { - turnSidebarLinksToWhite(); - cursorColor = 'light'; - } else if (slide.getAttribute('data-text-color') === 'black') { - turnSidebarLinksToBlack(); - cursorColor = 'dark'; - } - if (body && cursorOrientation && cursorColor) { - if (cursorOrientation === 'previous') { - body.style.cursor = `url('/images/cursor-${cursorOrientation}-${cursorColor}.svg') 0 12, auto`; - } else if (cursorOrientation === 'next') { - body.style.cursor = `url('/images/cursor-${cursorOrientation}-${cursorColor}.svg') 24 12, auto`; - } - } - } -} - -function editColorThemeOnSliderMounted() { - if (slider && slides) { - slider.on('mounted', function() { - editColorTheme(slides[0]); - }) - } -} - -function editColorThemeOnSlideActive() { - if (slider) { - slider.on('active', function(e) { - editColorTheme(e.slide); - }); - } -} - -function setCursorOnMove() { - if (body) { - body.addEventListener('mousemove', function(e) { - if (e.clientX >= window.innerWidth / 2) { - cursorOrientation = 'next'; - } else { - cursorOrientation = 'previous'; - } - if (cursorOrientation && cursorColor) { - if (cursorOrientation === 'previous') { - body.style.cursor = `url('/images/cursor-${cursorOrientation}-${cursorColor}.svg') 0 12, auto`; - } else if (cursorOrientation === 'next') { - body.style.cursor = `url('/images/cursor-${cursorOrientation}-${cursorColor}.svg') 24 12, auto`; - } - } + }); }); } } @@ -172,16 +49,4 @@ function setCursorOnMove() { // PROGRAM // ---------------------------------------------------------------------------- -// UTILS - -enableActivePseudoClass(); - -// SLIDER - -setUpSlider(); -editColorThemeOnSliderMounted(); -mountSlider(); -changeSlideOnClick(); -changeSlideOnSwipe(); -editColorThemeOnSlideActive(); -setCursorOnMove(); +setUpCarousels(); diff --git a/composer.json b/composer.json index 47dde13..67aded7 100644 --- a/composer.json +++ b/composer.json @@ -12,9 +12,8 @@ ], "require": { "php": ">=8.1.0 <8.4.0", - "getkirby/cms": "^3.10.0", + "getkirby/cms": "^4.0", "kirbyzone/sitemapper": "^1.2.1", - "mullema/k3-image-clip": "^3.2", "wearejust/kirby-twig": "^5.0" }, "scripts": { diff --git a/composer.lock b/composer.lock index 3793cbd..41e45db 100644 --- a/composer.lock +++ b/composer.lock @@ -4,20 +4,79 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "cbae6775c72f7f216b3cadb214a0fee3", + "content-hash": "f17093a48c983686f90617c12b92d15c", "packages": [ { - "name": "claviska/simpleimage", - "version": "4.2.0", + "name": "christian-riesen/base32", + "version": "1.6.0", "source": { "type": "git", - "url": "https://github.com/claviska/SimpleImage.git", - "reference": "dfbe53c01dae8467468ef2b817c09b786a7839d2" + "url": "https://github.com/ChristianRiesen/base32.git", + "reference": "2e82dab3baa008e24a505649b0d583c31d31e894" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/claviska/SimpleImage/zipball/dfbe53c01dae8467468ef2b817c09b786a7839d2", - "reference": "dfbe53c01dae8467468ef2b817c09b786a7839d2", + "url": "https://api.github.com/repos/ChristianRiesen/base32/zipball/2e82dab3baa008e24a505649b0d583c31d31e894", + "reference": "2e82dab3baa008e24a505649b0d583c31d31e894", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.17", + "phpstan/phpstan": "^0.12", + "phpunit/phpunit": "^8.5.13 || ^9.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Base32\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Riesen", + "email": "chris.riesen@gmail.com", + "homepage": "http://christianriesen.com", + "role": "Developer" + } + ], + "description": "Base32 encoder/decoder according to RFC 4648", + "homepage": "https://github.com/ChristianRiesen/base32", + "keywords": [ + "base32", + "decode", + "encode", + "rfc4648" + ], + "support": { + "issues": "https://github.com/ChristianRiesen/base32/issues", + "source": "https://github.com/ChristianRiesen/base32/tree/1.6.0" + }, + "time": "2021-02-26T10:19:33+00:00" + }, + { + "name": "claviska/simpleimage", + "version": "4.2.1", + "source": { + "type": "git", + "url": "https://github.com/claviska/SimpleImage.git", + "reference": "ec6d5021e5a7153a2520d64c59b86b6f3c4157c5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/claviska/SimpleImage/zipball/ec6d5021e5a7153a2520d64c59b86b6f3c4157c5", + "reference": "ec6d5021e5a7153a2520d64c59b86b6f3c4157c5", "shasum": "" }, "require": { @@ -49,7 +108,7 @@ "description": "A PHP class that makes working with images as simple as possible.", "support": { "issues": "https://github.com/claviska/SimpleImage/issues", - "source": "https://github.com/claviska/SimpleImage/tree/4.2.0" + "source": "https://github.com/claviska/SimpleImage/tree/4.2.1" }, "funding": [ { @@ -57,28 +116,28 @@ "type": "github" } ], - "time": "2024-04-15T16:07:16+00:00" + "time": "2024-11-22T13:25:03+00:00" }, { "name": "composer/semver", - "version": "3.4.0", + "version": "3.4.3", "source": { "type": "git", "url": "https://github.com/composer/semver.git", - "reference": "35e8d0af4486141bc745f23a29cc2091eb624a32" + "reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/35e8d0af4486141bc745f23a29cc2091eb624a32", - "reference": "35e8d0af4486141bc745f23a29cc2091eb624a32", + "url": "https://api.github.com/repos/composer/semver/zipball/4313d26ada5e0c4edfbd1dc481a92ff7bff91f12", + "reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12", "shasum": "" }, "require": { "php": "^5.3.2 || ^7.0 || ^8.0" }, "require-dev": { - "phpstan/phpstan": "^1.4", - "symfony/phpunit-bridge": "^4.2 || ^5" + "phpstan/phpstan": "^1.11", + "symfony/phpunit-bridge": "^3 || ^7" }, "type": "library", "extra": { @@ -122,7 +181,7 @@ "support": { "irc": "ircs://irc.libera.chat:6697/composer", "issues": "https://github.com/composer/semver/issues", - "source": "https://github.com/composer/semver/tree/3.4.0" + "source": "https://github.com/composer/semver/tree/3.4.3" }, "funding": [ { @@ -138,30 +197,30 @@ "type": "tidelift" } ], - "time": "2023-08-31T09:50:34+00:00" + "time": "2024-09-19T14:15:21+00:00" }, { "name": "filp/whoops", - "version": "2.15.4", + "version": "2.18.0", "source": { "type": "git", "url": "https://github.com/filp/whoops.git", - "reference": "a139776fa3f5985a50b509f2a02ff0f709d2a546" + "reference": "a7de6c3c6c3c022f5cfc337f8ede6a14460cf77e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filp/whoops/zipball/a139776fa3f5985a50b509f2a02ff0f709d2a546", - "reference": "a139776fa3f5985a50b509f2a02ff0f709d2a546", + "url": "https://api.github.com/repos/filp/whoops/zipball/a7de6c3c6c3c022f5cfc337f8ede6a14460cf77e", + "reference": "a7de6c3c6c3c022f5cfc337f8ede6a14460cf77e", "shasum": "" }, "require": { - "php": "^5.5.9 || ^7.0 || ^8.0", + "php": "^7.1 || ^8.0", "psr/log": "^1.0.1 || ^2.0 || ^3.0" }, "require-dev": { - "mockery/mockery": "^0.9 || ^1.0", - "phpunit/phpunit": "^4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.8 || ^9.3.3", - "symfony/var-dumper": "^2.6 || ^3.0 || ^4.0 || ^5.0" + "mockery/mockery": "^1.0", + "phpunit/phpunit": "^7.5.20 || ^8.5.8 || ^9.3.3", + "symfony/var-dumper": "^4.0 || ^5.0" }, "suggest": { "symfony/var-dumper": "Pretty print complex values better with var-dumper available", @@ -201,7 +260,7 @@ ], "support": { "issues": "https://github.com/filp/whoops/issues", - "source": "https://github.com/filp/whoops/tree/2.15.4" + "source": "https://github.com/filp/whoops/tree/2.18.0" }, "funding": [ { @@ -209,25 +268,26 @@ "type": "github" } ], - "time": "2023-11-03T12:00:00+00:00" + "time": "2025-03-15T12:00:00+00:00" }, { "name": "getkirby/cms", - "version": "3.10.1.1", + "version": "4.7.0", "source": { "type": "git", "url": "https://github.com/getkirby/kirby.git", - "reference": "1953eb4979f6d05ce7779c547bf761517024cccb" + "reference": "938fe98951cace6c77aab744779bf4e0799ad705" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/getkirby/kirby/zipball/1953eb4979f6d05ce7779c547bf761517024cccb", - "reference": "1953eb4979f6d05ce7779c547bf761517024cccb", + "url": "https://api.github.com/repos/getkirby/kirby/zipball/938fe98951cace6c77aab744779bf4e0799ad705", + "reference": "938fe98951cace6c77aab744779bf4e0799ad705", "shasum": "" }, "require": { - "claviska/simpleimage": "4.2.0", - "composer/semver": "3.4.0", + "christian-riesen/base32": "1.6.0", + "claviska/simpleimage": "4.2.1", + "composer/semver": "3.4.3", "ext-ctype": "*", "ext-curl": "*", "ext-dom": "*", @@ -239,15 +299,15 @@ "ext-mbstring": "*", "ext-openssl": "*", "ext-simplexml": "*", - "filp/whoops": "2.15.4", + "filp/whoops": "2.18.0", "getkirby/composer-installer": "^1.2.1", - "laminas/laminas-escaper": "2.13.0", + "laminas/laminas-escaper": "2.16.0", "michelf/php-smartypants": "1.8.1", - "php": "~8.1.0 || ~8.2.0 || ~8.3.0", - "phpmailer/phpmailer": "6.9.1", - "symfony/polyfill-intl-idn": "1.30.0", - "symfony/polyfill-mbstring": "1.30.0", - "symfony/yaml": "6.4.8" + "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0", + "phpmailer/phpmailer": "6.9.3", + "symfony/polyfill-intl-idn": "1.31.0", + "symfony/polyfill-mbstring": "1.31.0", + "symfony/yaml": "6.4.18" }, "replace": { "symfony/polyfill-php72": "*" @@ -292,7 +352,7 @@ "homepage": "https://getkirby.com" } ], - "description": "The Kirby 3 core", + "description": "The Kirby core", "homepage": "https://getkirby.com", "keywords": [ "cms", @@ -311,7 +371,7 @@ "type": "custom" } ], - "time": "2024-08-29T08:36:26+00:00" + "time": "2025-03-25T11:15:09+00:00" }, { "name": "getkirby/composer-installer", @@ -399,33 +459,32 @@ }, { "name": "laminas/laminas-escaper", - "version": "2.13.0", + "version": "2.16.0", "source": { "type": "git", "url": "https://github.com/laminas/laminas-escaper.git", - "reference": "af459883f4018d0f8a0c69c7a209daef3bf973ba" + "reference": "9cf1f5317ca65b4fd5c6a3c2855e24a187b288c8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-escaper/zipball/af459883f4018d0f8a0c69c7a209daef3bf973ba", - "reference": "af459883f4018d0f8a0c69c7a209daef3bf973ba", + "url": "https://api.github.com/repos/laminas/laminas-escaper/zipball/9cf1f5317ca65b4fd5c6a3c2855e24a187b288c8", + "reference": "9cf1f5317ca65b4fd5c6a3c2855e24a187b288c8", "shasum": "" }, "require": { "ext-ctype": "*", "ext-mbstring": "*", - "php": "~8.1.0 || ~8.2.0 || ~8.3.0" + "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0" }, "conflict": { "zendframework/zend-escaper": "*" }, "require-dev": { - "infection/infection": "^0.27.0", - "laminas/laminas-coding-standard": "~2.5.0", - "maglnet/composer-require-checker": "^3.8.0", - "phpunit/phpunit": "^9.6.7", - "psalm/plugin-phpunit": "^0.18.4", - "vimeo/psalm": "^5.9" + "infection/infection": "^0.29.8", + "laminas/laminas-coding-standard": "~3.0.1", + "phpunit/phpunit": "^10.5.45", + "psalm/plugin-phpunit": "^0.19.2", + "vimeo/psalm": "^6.6.2" }, "type": "library", "autoload": { @@ -457,7 +516,7 @@ "type": "community_bridge" } ], - "time": "2023-10-10T08:35:13+00:00" + "time": "2025-02-17T12:40:19+00:00" }, { "name": "league/color-extractor", @@ -574,60 +633,18 @@ }, "time": "2016-12-13T01:01:17+00:00" }, - { - "name": "mullema/k3-image-clip", - "version": "3.2.0", - "source": { - "type": "git", - "url": "https://github.com/mullema/k3-image-clip.git", - "reference": "66e5c2147fd6736f48878aaed6eef9ffe08cdd21" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/mullema/k3-image-clip/zipball/66e5c2147fd6736f48878aaed6eef9ffe08cdd21", - "reference": "66e5c2147fd6736f48878aaed6eef9ffe08cdd21", - "shasum": "" - }, - "require": { - "getkirby/composer-installer": "^1.2" - }, - "conflict": { - "getkirby/cms": "<3.6" - }, - "type": "kirby-plugin", - "extra": { - "installer-name": "k3-image-clip" - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Matthias Müller", - "email": "moeli@moeli.com", - "homepage": "https://getkirby.com/plugins/mullema" - } - ], - "description": "Visual image clip for Kirby 3", - "support": { - "issues": "https://github.com/mullema/k3-image-clip/issues", - "source": "https://github.com/mullema/k3-image-clip/tree/3.2.0" - }, - "time": "2022-11-05T10:49:35+00:00" - }, { "name": "phpmailer/phpmailer", - "version": "v6.9.1", + "version": "v6.9.3", "source": { "type": "git", "url": "https://github.com/PHPMailer/PHPMailer.git", - "reference": "039de174cd9c17a8389754d3b877a2ed22743e18" + "reference": "2f5c94fe7493efc213f643c23b1b1c249d40f47e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/039de174cd9c17a8389754d3b877a2ed22743e18", - "reference": "039de174cd9c17a8389754d3b877a2ed22743e18", + "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/2f5c94fe7493efc213f643c23b1b1c249d40f47e", + "reference": "2f5c94fe7493efc213f643c23b1b1c249d40f47e", "shasum": "" }, "require": { @@ -687,7 +704,7 @@ "description": "PHPMailer is a full-featured email creation and transfer class for PHP", "support": { "issues": "https://github.com/PHPMailer/PHPMailer/issues", - "source": "https://github.com/PHPMailer/PHPMailer/tree/v6.9.1" + "source": "https://github.com/PHPMailer/PHPMailer/tree/v6.9.3" }, "funding": [ { @@ -695,7 +712,7 @@ "type": "github" } ], - "time": "2023-11-25T22:23:28+00:00" + "time": "2024-11-24T18:04:13+00:00" }, { "name": "psr/log", @@ -895,22 +912,21 @@ }, { "name": "symfony/polyfill-intl-idn", - "version": "v1.30.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-idn.git", - "reference": "a6e83bdeb3c84391d1dfe16f42e40727ce524a5c" + "reference": "c36586dcf89a12315939e00ec9b4474adcb1d773" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/a6e83bdeb3c84391d1dfe16f42e40727ce524a5c", - "reference": "a6e83bdeb3c84391d1dfe16f42e40727ce524a5c", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/c36586dcf89a12315939e00ec9b4474adcb1d773", + "reference": "c36586dcf89a12315939e00ec9b4474adcb1d773", "shasum": "" }, "require": { - "php": ">=7.1", - "symfony/polyfill-intl-normalizer": "^1.10", - "symfony/polyfill-php72": "^1.10" + "php": ">=7.2", + "symfony/polyfill-intl-normalizer": "^1.10" }, "suggest": { "ext-intl": "For best performance" @@ -959,7 +975,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.31.0" }, "funding": [ { @@ -975,7 +991,7 @@ "type": "tidelift" } ], - "time": "2024-05-31T15:07:36+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-intl-normalizer", @@ -1060,20 +1076,20 @@ }, { "name": "symfony/polyfill-mbstring", - "version": "v1.30.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c" + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fd22ab50000ef01661e2a31d850ebaa297f8e03c", - "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341", + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "provide": { "ext-mbstring": "*" @@ -1120,83 +1136,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.30.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-06-19T12:30:46+00:00" - }, - { - "name": "symfony/polyfill-php81", - "version": "v1.31.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php81.git", - "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", - "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", - "shasum": "" - }, - "require": { - "php": ">=7.2" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Php81\\": "" - }, - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php81/tree/v1.31.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.0" }, "funding": [ { @@ -1216,16 +1156,16 @@ }, { "name": "symfony/yaml", - "version": "v6.4.8", + "version": "v6.4.18", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "52903de178d542850f6f341ba92995d3d63e60c9" + "reference": "bf598c9d9bb4a22f495a4e26e4c4fce2f8ecefc5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/52903de178d542850f6f341ba92995d3d63e60c9", - "reference": "52903de178d542850f6f341ba92995d3d63e60c9", + "url": "https://api.github.com/repos/symfony/yaml/zipball/bf598c9d9bb4a22f495a4e26e4c4fce2f8ecefc5", + "reference": "bf598c9d9bb4a22f495a4e26e4c4fce2f8ecefc5", "shasum": "" }, "require": { @@ -1268,7 +1208,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v6.4.8" + "source": "https://github.com/symfony/yaml/tree/v6.4.18" }, "funding": [ { @@ -1284,28 +1224,27 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:49:08+00:00" + "time": "2025-01-07T09:44:41+00:00" }, { "name": "twig/twig", - "version": "v3.18.0", + "version": "v3.20.0", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "acffa88cc2b40dbe42eaf3a5025d6c0d4600cc50" + "reference": "3468920399451a384bef53cf7996965f7cd40183" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/acffa88cc2b40dbe42eaf3a5025d6c0d4600cc50", - "reference": "acffa88cc2b40dbe42eaf3a5025d6c0d4600cc50", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/3468920399451a384bef53cf7996965f7cd40183", + "reference": "3468920399451a384bef53cf7996965f7cd40183", "shasum": "" }, "require": { - "php": ">=8.0.2", + "php": ">=8.1.0", "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-ctype": "^1.8", - "symfony/polyfill-mbstring": "^1.3", - "symfony/polyfill-php81": "^1.29" + "symfony/polyfill-mbstring": "^1.3" }, "require-dev": { "phpstan/phpstan": "^2.0", @@ -1352,7 +1291,7 @@ ], "support": { "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v3.18.0" + "source": "https://github.com/twigphp/Twig/tree/v3.20.0" }, "funding": [ { @@ -1364,7 +1303,7 @@ "type": "tidelift" } ], - "time": "2024-12-29T10:51:50+00:00" + "time": "2025-02-13T08:34:43+00:00" }, { "name": "wearejust/kirby-twig", diff --git a/kirby/.editorconfig b/kirby/.editorconfig index 76df047..10fd327 100644 --- a/kirby/.editorconfig +++ b/kirby/.editorconfig @@ -17,6 +17,10 @@ trim_trailing_whitespace = true indent_size = 4 insert_final_newline = true +[*.vue.php] +indent_size = 2 +insert_final_newline = false + [*.yml] indent_style = space diff --git a/kirby/CONTRIBUTING.md b/kirby/CONTRIBUTING.md index 5aadd3c..a4c299a 100644 --- a/kirby/CONTRIBUTING.md +++ b/kirby/CONTRIBUTING.md @@ -6,15 +6,17 @@ To keep track of different states of our code (current release, bugfixes, features) we use branches: -| Branch | Used for | PRs allowed? | -| ----------- | ------------------------------------------------------------------------ | --------------------------- | -| `main` | Latest released version | - | -| `develop` | Working branch for next release, e.g. `3.7.x` | ✅ | -| `fix/*` | Temporary branches for single patch | - | -| `feature/*` | Temporary branches for single feature | - | -| `release/*` | Pre-releases in testing before they are merged into `main` when released | only during release testing | +| Branch | Used for | PRs allowed? | +| --------------- | ------------------------------------------------------------------------ | --------------------------- | +| `main` | Latest released version | ❌ | +| `develop-patch` | Working branch for next patch release, e.g. `4.0.x` | ✅ | +| `develop-minor` | Working branch for next minor release, e.g. `4.x.0` | ✅ | +| `v5/develop` | Working branch for next major release, e.g. `5.0.0` | ✅ | +| `fix/*` | Temporary branches for single bugfix | - | +| `feature/*` | Temporary branches for single feature | - | +| `release/*` | Pre-releases in testing before they are merged into `main` when released | only during release testing | -We will review all pull requests (PRs) to `develop` and merge them if accepted, once an appropriate version is upcoming. Please understand that this might not be the immediate next release and might take some time. +We will review all pull requests (PRs) to `develop-patch`, `develop-minor` and `v5/develop` and merge them if accepted, once an appropriate version is upcoming. Please understand that this might not be the immediate next release and might take some time. ## How you can contribute @@ -26,25 +28,25 @@ When you find a bug, the first step to fixing it is to help us understand and re For bug fixes, please create a new branch following the name scheme: `fix/issue_number-bug-x`, e.g. `fix/234-this-nasty-bug`. Limit bug fix PRs to a single bug. **Do not mix multiple bug fixes in a single PR.** This will make it easier for us to review the fix and merge it. -- Always send bug fix PRs against the `develop` branch––not `main`. +- Always send bug fix PRs against the `develop-patch` branch––not `main`. - Add a helpful description of what the PR does if it is not 100% self-explanatory. - Every bug fix should include a [unit test](#tests) to avoid future regressions. Let us know if you need help with that. - Make sure your code [style](#style) matches ours and includes [comments/in-code documentation](#documentation). -- Make sure your branch is up to date with the latest state on the `develop` branch. [Rebase](https://help.github.com/articles/about-pull-request-merges/) changes before you send the PR. +- Make sure your branch is up to date with the latest state on the `develop-patch` branch. [Rebase](https://help.github.com/articles/about-pull-request-merges/) changes before you send the PR. - Please *don't* commit updated dist files in the `panel/dist` folder to avoid merge conflicts. We only build the dist files on release. Your branch should only contain changes to the source files. ### Features For features create a new branch following the name scheme: `feature/issue_number-feature-x`, e.g. `feature/123-awesome-function`. Our [feedback platform](https://feedback.getkirby.com) can be a good source of highly requested features. Maybe your feature idea already exists and you can get valuable feedback from other Kirby users. Focus on a single feature per PR. Don't mix features! -- Always send feature PRs against the `develop` branch––not `main`. +- Always send feature PRs against the `develop-minor` branch––not `main`. - Add a helpful description of what the PR does. - New features should include [unit tests](#tests). Let us know if you need help with that. - Make your code [style](#style) matches ours and includes [comments/in-code documentation](#documentation). -- Make sure your branch is up to date with the latest state on the `develop` branch. [Rebase](https://help.github.com/articles/about-pull-request-merges/) changes before you send the PR. +- Make sure your branch is up to date with the latest state on the `develop-minor` branch. [Rebase](https://help.github.com/articles/about-pull-request-merges/) changes before you send the PR. - Please *don't* commit updated dist files in the `panel/dist` folder to avoid merge conflicts. We only build the dist files on release. Your branch should only contain changes to the source files. -We try to bundle features in our major releases, e.g. `3.x`. That is why we might only review and, if accepted, merge your PR once an appropriate release is upcoming. Please understand that we cannot merge all feature ideas or that it might take a while. Check out the [roadmap](https://roadmap.getkirby.com) to see upcoming releases. +We try to bundle features in our major releases, e.g. `5.0`. That is why we might only review and, if accepted, merge your PR once an appropriate release is upcoming. Please understand that we cannot merge all feature ideas or that it might take a while. Check out the [roadmap](https://roadmap.getkirby.com) to see upcoming releases. ### Translations diff --git a/kirby/LICENSE.md b/kirby/LICENSE.md index 8d109fe..27b4e75 100644 --- a/kirby/LICENSE.md +++ b/kirby/LICENSE.md @@ -1,7 +1,7 @@ # Kirby License Agreement -Published: March 21, 2023 -Source: https://getkirby.com/license/2023-03-21 +Published: March 18, 2025 +Source: https://getkirby.com/license/2025-03-18 ## About this Agreement @@ -11,84 +11,131 @@ This End User License Agreement (the **"Agreement"**) is fundamental to the rela If you do not agree to this Agreement, please do not download, install or use Kirby. Installation or use of Kirby signifies that you have read, understood, and agreed to be bound by this Agreement. +## Summary + +This section summarizes the most important conditions of this Agreement to give you a quick overview: + +- With your purchase you obtain a license. A license allows you to use Kirby according to this Agreement. +- Each project (defined by its URL) needs its own license. You need to purchase the right license for your project and/or client. You can find our license variants on . +- In some explicitly listed cases, you can use Kirby without having to purchase a license. In these cases, this Agreement grants you the license directly. There are also cases where you can request a free or discounted license from us. +- Each license includes any Kirby version that gets released within three years from the date when you first activated your license. We also provide free security updates for older versions that may protect your project beyond three years. +- After those three years, you can continue to use Kirby for your project with any of these versions as long as you want. +- To use any newer version released after this time, you will need to upgrade your license. +- Upgrading your license extends the timeframe for an additional three years during which you can use new releases. You can perform the upgrade at any time. +- You have the right to transfer or reassign a license to another person or project if needed. +- There are some restrictions for use of Kirby that you can find below. + +For the full license details, please read the Agreement in full. Only the following sections are legally binding. + ## Definitions Before we get started with the conditions of the Agreement, let's define the terms that will be used throughout it: -- When we refer to **"You"**, we mean the licensee. Before purchasing Kirby, that's the individual or company that has downloaded and/or installed Kirby for a Development Installation, Private Installation or Extension Demo. When used for a Public Site, the licensee is the individual or company that has purchased the Kirby license. If you work on a client project and have purchased the Kirby license for your client, you (and _not_ the client) are the licensee. -- When we refer to **"We"**/**"Us"**/**"Our"**, we mean the licensor, the Content Folder GmbH & Co. KG. You can find Our company and contact information on Our [contact page](https://getkirby.com/contact). +- When we refer to **"You"**, we mean the licensee. Before purchasing Kirby, that's the individual or company that has downloaded and/or installed Kirby for a Development Installation or Private Installation. When used for a Public Site, the licensee is the individual or company that has purchased the Kirby license or received a free license from Us on request. If you work on a client project and have purchased the Kirby license for your client, you (and _not_ the client) are the licensee. +- When we refer to **"We"**/**"Us"**/**"Our"**, we mean the licensor, the Content Folder GmbH & Co. KG. You can find Our company and contact information on Our [contact page](https://getkirby.com/contact). +- **"Client"** refers to the individual or company that is primarily responsible for and benefits from the Website, unless they are the licensee. You might create or work on the Website on behalf of the Client, either directly or through other intermediaries (e.g. as a freelancer for an agency that works on a client website). - A **"Website"** is a single Kirby project that is defined by its domain name and root directory (e.g. `https://sub.example.com` or `https://example.com/example/`). Each (sub)domain and root directory is a separate Website, even if the projects are related in any way. Exception: If You use the cross-domain multi-language feature with the same `content` folder, these domains count as the same Website. You may use Kirby as a headless backend or as a static site generator. In these cases the Website is defined by the domain and root directory of the user- or visitor-facing frontend(s). - A **"Development Installation"** is a Website that is installed purely for the purposes of development and client preview. It must only be accessible by a restricted number of users (like on a personal computer, on a server in a network with restricted access or when protecting a staging website with a password that only a restricted number of users know). - A **"Private Installation"** is a Website that is installed purely for personal use. It must only be accessible by You and Your family. -- An **"Extension Demo"** is a Website with the single purpose to showcase a free or commercial Kirby theme or Kirby plugin, as long as that Website only contains demo content. If the showcased extension is a Kirby theme, the demo content must be exactly as shipped with the theme. Demos for Kirby plugins may _not_ contain any additional content that is not needed to showcase the plugin in use. -- A **"Public Site"** is a Website that is _neither_ a Development Installation, a Private Installation nor an Extension Demo. -- An **"Update"** is defined as a Kirby release which adds smaller new features, minor functionality enhancements or bug fixes. This class of release is identified by the change of the revision to the right of the first decimal point, e.g. 3.1 to 3.2, 3.X.1 to 3.X.2 or 3.X.X.1 to 3.X.X.2. -- An **"Upgrade"** is a major Kirby release which incorporates major new features or enhancements that increase the core functionality of Kirby to a larger extent. This class of release is identified by the change of the revision to the left of the first decimal point, e.g. 3.X to 4.0. +- A **"Public Site"** is a Website that is _neither_ a Development Installation nor a Private Installation. +- A **"Minor Release"** is a stable Kirby release which adds smaller new features, minor functionality enhancements or bug fixes. This class of release is identified by the change of the revision to the right of the first decimal point, e.g. 4.1 to 4.2, 4.X.1 to 4.X.2. +- A **"Major Release"** is a stable Kirby release which incorporates major new features or enhancements that increase or change the core functionality of Kirby to a larger extent. It may also deprecate existing parts of the Source Code or change them in a breaking way. This class of release is identified by the change of the revision to the left of the first decimal point, e.g. 4.X to 5.0. +- A **"Major Generation"** is defined as all releases that share the revision to the left of the first decimal point, e.g. 4.0.0, 4.0.X, 4.X.0 and 4.X.X. - The **"Source Code"** is defined as the contents of all files that are provided with Kirby and that make Kirby work. This includes (but is not limited to) all PHP, JavaScript, JSON, HTML and CSS files as well as all related image and other media files. +- The **"MIT-licensed Source Code Parts"** are defined as the parts of the Source Code for which a file header or code comment inside the same source file explicitly states the applicability of the MIT license. +- The **"Activation Date"** determines the included updates. It is defined like this: + - For a newly purchased or granted license, it is the date when the license was first activated for use with a Public Site. + - When You upgrade an already activated license, it is the date on which the upgrade was performed in Our [license hub](https://hub.getkirby.com). If the license is still within the Included Updates Period, the Activation Date of the upgrade license will be set to the end of the Included Updates Period of the existing license. + - When You upgrade a license that had _not_ been activated before, the upgrade license adopts the unactivated state of the existing license. The Activation Date is set on first activation for use with a Public Site. +- The **"Included Updates Period"** is the time span of three (3) years after the Activation Date. +- Licensees (You), Clients and Websites are **"Qualified"** if they satisfy the purchase requirements from the ["Order Process" section](#order-process) of this Agreement. Every time you see one of these capitalized terms in the following text, it has the meaning that has been explained above. ## Usage for a Public Site -Installing Kirby on or using it for a Public Site requires a [paid license](https://getkirby.com/buy). +Installing Kirby on or using it for a Public Site requires a [paid license](https://getkirby.com/buy). Once a paid license is needed, the license must be immediately activated to the Public Site’s domain name and root directory via our [license hub](https://hub.getkirby.com) or the activation feature in the Kirby Panel. As Kirby is software and software is intangible, We don't sell it as such. Instead, this Agreement grants a license for each purchase to install and use a single instance of Kirby on a **specific Website**. Additional Kirby licenses must be purchased in order to install and use Kirby on **additional Websites**. -The license is **non-exclusive** (meaning that You are not the only one who We will issue a license) and **generally non-transferable** (meaning that the one who purchases the license is the licensee). +The license is **non-exclusive** (meaning that You are not the only one to whom We will issue a license) and **generally non-transferable** (meaning that the one who purchases the license is the licensee). -On request, We will **transfer** a license to anyone who is also allowed to buy Kirby licenses by law and this Agreement. The new licensee will take over all rights and obligations of this Agreement from You at the moment We confirm the license transfer. +On request, We will **transfer** a license to anyone who would be allowed and Qualified to purchase the license by law and this Agreement. The new licensee will take over all rights and obligations of this Agreement from You at the moment We confirm the license transfer. -We will also **reassign** a license to another Website domain and root directory of Your choice, provided that You confirm that the previous Website is no longer in operation and will not be operated with the same license in the future. +We will also **reassign** a license to another Qualified Website domain and root directory, if You confirm that the previous Website is no longer in operation and will not be operated with the same license in the future. -If you need to transfer your Kirby license to another individual or company (for example to your client or a new agency) or reassign it to a different project, please get in touch directly at . +If the new licensee, Website or Client in a transfer or reassignment is not Qualified for the existing license, You or the new licensee need to **upgrade the license to the qualifying terms and conditions** before the transfer or reassignment can be performed. -A license is valid for all Updates of the same major Kirby release. We reserve the right to charge an **upgrade fee for Upgrade releases**. Whether a release is an Update or Upgrade is at Our sole discretion. +> [!NOTE] +> If you need to transfer your Kirby license to another individual or company (for example to your client or a new agency) or reassign it to a different project, please get in touch directly at . + +A license is valid for all Major Releases that We publish before the end of the Included Updates Period. It is also valid for all releases in those Major Generations independent of their release date. Whether a release is a Minor Release or Major Release is at Our sole discretion. + +The use of releases in Major Generations that We publish after the Included Updates Period requires a **paid license upgrade**. An upgrade license replaces the existing license. ## Order Process Our order process is conducted by Our online reseller [Paddle.com](https://paddle.com). Paddle.com is the Merchant of Record for all Our orders. Paddle provides all customer service inquiries and handles returns. +When purchasing a license, You are **responsible to choose the right license** based on You and the Website project. Different license variants can come with certain requirements towards You and/or the Website project. We publish all such requirements on in a way that makes them clearly visible before the purchase. With Your purchase, You confirm that You and the Website project qualify for the selected license variant. + +If the Website is created for a Client, You need to make sure that the **Client qualifies for the selected license**. + +If You purchase licenses **in advance**, You need to ensure to only use the license(s) for projects that satisfy the requirements for the selected license variant(s). + +We **reserve the right to verify** at any time after the purchase whether You, the Website and (if applicable) the Client are Qualified. Changes to the situation of You, the Website or the Client as well as changes to the published information on Our "Buy" page after the purchase or after the assignment to a Client do _not_ take effect on an existing license unless You upgrade the license or We transfer or reassign the license on Your request. + ## Free Licenses Kirby can be used **for free in the following cases**. -Please note that the restrictions and all other clauses of this Agreement also apply to free licenses. You may especially _not_ alter or circumvent the licensing features. +> [!NOTE] +> Please note that the restrictions and all other clauses of this Agreement also apply to free licenses. You may especially _not_ alter or circumvent the licensing features. ### Usage for a Development Installation We believe that it should be possible to test and evaluate software before having to purchase a license. Also, We understand that a web project first needs to be built in a protected environment before it can be published. -Therefore, installing and using Kirby on a personal computer (like a desktop PC, notebook or tablet) or server for a Development Installation is **free** for as long as You need. +Therefore, installing and using Kirby on a personal computer (like a desktop PC, notebook or tablet) or server for a Development Installation is **free** for as long as You need. If You have already purchased a license, You do *not* need to activate it to the development domain(s) of the project. -The usage of Kirby in production (with the intention to handle production data or content) is _never_ considered a Development Installation, even in internal apps or systems. +> [!NOTE] +> The usage of Kirby in production (with the intention to handle production data or content) is _never_ considered a Development Installation, even in internal apps or systems. ### Usage for a Private Installation You may also install and use Kirby for **free** in Private Installations as long as they are not accessible by anyone except You and Your family. -Our [definition](#definitions) of a Private Installation allows the following use cases: +> [!NOTE] +> Our [definition](#definitions) of a Private Installation allows the following use cases: +> +> - Private sites for personal use, for example: +> - Apps for You personally (like a personal diary) +> - Apps for You as a freelancer (like a bookkeeping, invoicing or project management app) +> - Apps for Your family (like a private photo gallery) +> - Experimental local Kirby setups for Your personal use (for example to try out Kirby features) +> +> However, the following use cases are _not_ covered and need a **[paid license](#usage-for-a-public-site)**: +> +> - Intranets for companies, authorities or organizations, no matter if on a local or public server +> - (Internal) apps for teams or entire companies, authorities or organizations +> - Websites that are accessible by the public, even for personal/non-commercial purposes +> - Use of Kirby as a local CMS for a static or headless site without a license for the frontend domain(s) -- Private sites for personal use, for example: - - Apps for You personally (like a personal diary) - - Apps for You as a freelancer (like a bookkeeping, invoicing or project management app) - - Apps for Your family (like a private photo gallery) -- Experimental local Kirby setups for Your personal use (for example to try out Kirby features) +### Free Licenses on Request -However, the following use cases are _not_ covered and need a **[paid license](#usage-for-a-public-site)**: +We provide free or discounted licenses for specific purposes: -- Intranets for companies, authorities or organizations, no matter if on a local or public server -- (Internal) apps for teams or entire companies, authorities or organizations -- Websites that are accessible by the public, even for personal/non-commercial purposes -- Use of Kirby as a local CMS for a static or headless site without a license for the frontend domain(s) +- students, +- selected educational projects, social and environmental organizations, charities and non-profits with insufficient funding and +- demo sites showcasing a Kirby extension (plugin or theme). -### Usage for an Extension Demo +Unlike licenses for Development Installations or Private Installations, these free or discounted licenses are not granted by this Agreement directly. You need to request them from Us via email to . Any discounts or free licenses are at Our sole discretion. -Extension Demos are not real Websites. We want to encourage you to build and showcase your themes and plugins. +Should We grant a free or discounted license under the terms of this section, You will receive a license code for use with a Public Site. The [section "Usage for a Public Site"](#usage-for-a-public-site) as well as all other terms of this Agreement apply with the following modifications: -Therefore, You may **operate Extension Demos without purchasing a license**. - -Please note that this does _not_ apply to store fronts or other types of sites used to promote free or commercial themes or plugins. If such a site is built with Kirby as well, it is a Public Site and needs a **[paid license](#usage-for-a-public-site)**. +- We will only transfer or reassign free or discounted licenses if the new licensee or project qualifies for the free or discounted license at Our sole discretion. +- You may upgrade free or discounted licenses to future Major Generations like paid licenses. We will grant free or discounted upgrades under the terms of this section. ## Restrictions @@ -105,7 +152,7 @@ You may make **copies of Kirby** in any machine readable form solely for the **f - when working on code contributions to Kirby or - as a backup. -You may _not_ reproduce Kirby or its Source Code, in whole or in part, for **any other purpose**. +You may _not_ reproduce Kirby or its Source Code, in whole or in part, for **any other purpose**, except if granted below. ### Modification of the Source Code @@ -113,10 +160,12 @@ You may **alter, modify or extend the Source Code** for Your own use or with the However You may _not_: -- **alter or circumvent the licensing features**, including (but not limited to) the license validation and payment prompts or +- **alter or circumvent the licensing features**, including (but not limited to) the license validation and payment prompts, +- **remove or alter any proprietary notices** on Kirby or - **resell, redistribute or transfer** the modified or derivative version. -Please note that We **can't provide technical support** for modified or derivative versions of the Source Code. +> [!NOTE] +> Please note that We **can't provide technical support** for modified or derivative versions of the Source Code. ### Your Relationship to Third Parties @@ -129,13 +178,45 @@ The following cases are exempted from this restriction: - You may make Kirby available to customers via a Software-as-a-Service (SaaS) offering, provided You ensure that each Website has a valid Kirby license purchased either by You or Your customer. If multiple customers share a Website, each customer needs at least one license. Your offering _must not_ appear to be provided or officially endorsed by Us. - You may make a Kirby installation available to employees or partners of You or Your Website client. You may also disclose and distribute Kirby’s Source Code to Your client together with the source code of the Website You created for them. - You may disclose the Source Code to individuals or companies that are involved in the development or operation of Your Website (e.g. agencies, design or development freelancers, hosting providers or administrators). +- You may disclose, distribute and make available extracted parts of the Source Code according to the conditions of the following section "Extraction of Source Code parts". -E.g. the following cases are explicitly **_not_ allowed**: +> [!NOTE] +> E.g. the following cases are explicitly **_not_ allowed**: +> +> - Selling, licensing or distributing a new product based on Kirby that modifies or hides Kirby’s identity as a Content Management System (CMS) +> - Forking Kirby and selling the modified version ([see above](#modification-of-the-source-code)) +> - Buying licenses in bulk and reselling them in your own shop +> - Bundling or including Kirby’s full Source Code in the publication and/or distribution of a Website’s source code or a (free or paid) theme or plugin (please use Git submodules or Composer or provide a link to Our repository or website instead) -- Selling, licensing or distributing a new product based on Kirby that modifies or hides Kirby’s identity as a Content Management System (CMS) -- Forking Kirby and selling the modified version ([see above](#restrictions__modification-of-the-source-code)) -- Buying licenses in bulk and reselling them in your own shop -- Bundling or including Kirby’s Source Code in the publication and/or distribution of a Website’s source code or a (free or paid) theme or plugin (please use Git submodules or Composer or provide a link to Our repository or website instead) +### Extraction of Source Code parts + +We provide the Source Code as a complete unit for use according to this Agreement. + +Reuse of code parts in other programs or projects is _only_ permitted in the cases stated in the following sections. You may _not_ extract the Source Code or parts of the Source Code in any other way or for any other purpose. + +#### Extraction of MIT-licensed Source Code Parts + +Permission is hereby granted, free of charge, to any person obtaining a copy of MIT-licensed Source Code Parts, to deal in the MIT-licensed Source Code Parts without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the MIT-licensed Source Code Parts, and to permit persons to whom the MIT-licensed Source Code Parts is furnished to do so, subject to the following conditions: + +The copyright notice to Bastian Allgeier and this permission notice shall be included in all copies or substantial portions of the MIT-licensed Source Code Parts. + +THE MIT-LICENSED SOURCE CODE PARTS ARE PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MIT-LICENSED SOURCE CODE PARTS OR THE USE OR OTHER DEALINGS IN THE MIT-LICENSED SOURCE CODE PARTS. + +#### Extraction for Extensions + +You may extract, use, disclose and distribute copies or adapted copies of individual parts of the Source Code (such as component templates), including those not licensed under the terms of the MIT license but excluding those covered by third-party licenses, if both of the following conditions are met: + +- You use the code parts in or disclose/distribute them as part of an extension (plugin or theme) that You solely intend to be used with Kirby. +- Every copied or adapted code part carries a clear note that references these conditions as well as Our copyright and this Agreement. You may use the following example as a template: + ``` + /** + * The following code was copied or adapted from the source code of + * Kirby CMS and may not be used outside of licensed Kirby projects. + * + * @copyright Bastian Allgeier + * @license https://getkirby.com/license + */ + ``` ### Disallowed Uses @@ -157,24 +238,27 @@ The following uses of Kirby are _not_ covered by this Agreement and will result - a safe environment or action to curtail the use of fossil fuels or to prevent climate change or - democratic processes. -### Other Restrictions - -You may also _not_: - -- **extract parts of the Source Code** for use in other programs or projects (unless the code file in question is explicitly licensed under the terms of the MIT license) or -- **remove or alter any proprietary notices** on Kirby. - ## Technical Support Technical support is **provided as described on Our website** at . **No representations or guarantees** are made regarding the response time in which support questions are answered, however We will do Our best to respond quickly. +For each Major Generation, We aim to provide **security support for three (3) years** after the Major Release. Security support means that We will provide free security updates for the supported releases, which will include fixes for security vulnerabilities according to the following rules: + - We published a security advisory on within the respective security support period. We will publish vulnerabilities on this page as soon as they are known to Us and an official fix for any supported release is available. + - The latest release of the supported Major Generation is affected by the vulnerability. + +With each vulnerability, We aim to publish the security advisory and security updates for all supported Major Generations at the same time. + +> [!NOTE] +> You can find up-to-date information on our currently supported versions in our [public security policy](https://getkirby.com/security). + We reserve the right to **limit technical support for free licenses**. ## Refund Policy We offer a **14-day**, money back refund policy if Kirby didn't work out for Your project. -If you need a refund, please get in touch directly at . +> [!NOTE] +> If you need a refund, please get in touch directly at . ## No Warranty @@ -204,6 +288,8 @@ YOU EXPRESSLY UNDERSTAND AND AGREE THAT **WE SHALL NOT BE LIABLE** FOR ANY DIREC Bastian Allgeier **owns all rights**, title and interest to Kirby (including all intellectual property rights) and **reserves all rights to Kirby** that are not expressly granted in this Agreement. +In the event that Kirby will no longer be actively maintained, Bastian Allgeier will provide the Source Code under the terms of a free and open source software (FOSS) license as far as legally and contractually possible. + ## Applicable Law & Place of Jurisdiction 1. For all disputes arising out of or in connection with this Agreement, the courts competent for Neckargemünd, Germany, shall have exclusive jurisdiction. However, We shall have the choice to file lawsuits against You before the courts competent for Your place of business. @@ -219,4 +305,4 @@ Should any provision of this Agreement be or become invalid, void or unenforceab Due to Kirby's flexibility, you may have special use cases or requirements that don't fit this Agreement. -If that's the case or if you have any questions, feel free to get in touch: . We are happy to think outside the box and find custom license solutions for your creative application of Kirby. +If that's the case or if you have any questions, feel free to [get in touch](mailto:support@getkirby.com). We are happy to think outside the box and find custom license solutions for your creative application of Kirby. diff --git a/kirby/README.md b/kirby/README.md index 15bb31b..284fd6d 100644 --- a/kirby/README.md +++ b/kirby/README.md @@ -1,12 +1,12 @@ -[](https://getkirby.com) +[](https://getkirby.com) [![Release](https://img.shields.io/github/v/release/getkirby/kirby)](https://github.com/getkirby/kirby/releases/latest) [![CI Status](https://img.shields.io/github/actions/workflow/status/getkirby/kirby/ci.yml?branch=main&label=CI)](https://github.com/getkirby/kirby/actions?query=workflow%3ACI+branch%3Amain) [![Coverage Status](https://img.shields.io/codecov/c/gh/getkirby/kirby?token=ROZ2RVA0OF)](https://codecov.io/gh/getkirby/kirby) [![Downloads](https://img.shields.io/packagist/dt/getkirby/cms?color=red)](https://github.com/getkirby/kirby/releases/latest) -**Kirby: the CMS that adapts to any project, loved by developers and editors alike.** -With Kirby, you build your own ideal interface. Combine forms, galleries, articles, spreadsheets and more into an amazing editing experience. You can learn more about Kirby at [getkirby.com](https://getkirby.com). +**Kirby: the CMS that adapts to any project, loved by developers and editors alike.** +With Kirby, you build your own ideal interface. Combine forms, galleries, articles, spreadsheets and more into an amazing editing experience. You can learn more about Kirby at [getkirby.com](https://getkirby.com). This is Kirby's core application folder. Get started with one of the following repositories instead: @@ -15,21 +15,23 @@ This is Kirby's core application folder. Get started with one of the following r -### Try Kirby for free +### Try Kirby for free + Kirby is not free software. However, you can try Kirby and the Starterkit on your local machine or on a test server as long as you need to make sure it is the right tool for your next project. … and when you’re convinced, [buy your license](https://getkirby.com/buy). ### Contribute -**Found a bug?** +**Found a bug?** Please post all bugs as individual reports in our [issue tracker](https://github.com/getkirby/kirby/issues). -**Suggest a feature** +**Suggest a feature** If you have ideas for a feature or enhancement for Kirby, please use our [feedback platform](https://feedback.getkirby.com). -**Translations, bug fixes, code contributions ...** +**Translations, bug fixes, code contributions ...** Read about how to contribute to the development in our [contributing guide](/CONTRIBUTING.md). ## What's Kirby? + - **[getkirby.com](https://getkirby.com)** – Get to know the CMS. - **[Try it](https://getkirby.com/try)** – Take a test ride with our online demo. Or download one of our kits to get started. - **[Documentation](https://getkirby.com/docs/guide)** – Read the official guide, reference and cookbook recipes. @@ -39,9 +41,9 @@ Read about how to contribute to the development in our [contributing guide](/CON - **[Discord](https://chat.getkirby.com)** – Hang out and meet the community. - **[YouTube](https://youtube.com/kirbyCasts)** - Watch the latest video tutorials visually with Bastian. - **[Mastodon](https://mastodon.social/@getkirby)** – Spread the word. -- **[Instagram](https://www.instagram.com/getkirby/)** – Share your creations: #madewithkirby. +- **[Bluesky](https://bsky.app/profile/getkirby.com)** – Tell a friend. --- -© 2009-2023 Bastian Allgeier +© 2009 Bastian Allgeier [getkirby.com](https://getkirby.com) · [License agreement](https://getkirby.com/license) diff --git a/kirby/SECURITY.md b/kirby/SECURITY.md index ae42a38..ffc6ad0 100644 --- a/kirby/SECURITY.md +++ b/kirby/SECURITY.md @@ -1,3 +1,27 @@ # Security Policy -Please see the [Security Policy on the Kirby website](https://getkirby.com/security) for a list of the currently supported Kirby versions and of past security incidents as well as for information on how to report security vulnerabilities in the Kirby core or in the Panel. +## Supported versions and past security incidents + +You can find up-to-date information on the security status of each version on . + +## Security of your Kirby site + +We have a detailed [security guide](https://getkirby.com/docs/guide/security) with information on how to keep your Kirby installation secure. + +## Reporting a vulnerability + +If you have spotted a vulnerability in Kirby's core or the Panel, please make sure to let us know immediately. We appreciate your efforts to responsibly disclose your findings, and will make every effort to acknowledge your contributions. + +You can always contact us directly at ****. +If you want to encrypt your message, our GPG key is [6E6B 057A F491 FFAD 363F 6F49 9101 10FA A459 E120](https://getkirby.com/pgp.asc). + +You can also use the [security advisory form on GitHub](https://github.com/getkirby/kirby/security/advisories/new) to securely and privately report a vulnerability to us. + +We will send you a response as soon as possible and will keep you informed on our progress towards a fix and announcement. + +> [!IMPORTANT] +> Please do not write to us publicly, e.g. in the forum, on Discord or in a GitHub issue. A public report can give attackers valuable time to exploit the issue before it is fixed. +> +> By letting us know directly and coordinating the disclosure with us, you can help to protect other Kirby users from such attacks. +> +> Also please do *not* request a CVE ID from organizations like MITRE. The responsible CVE Numbering Authority (CNA) for Kirby is GitHub. We can and will request a CVE ID for each confirmed vulnerability and will provide it to you in advance of the coordinated release. diff --git a/kirby/bootstrap.php b/kirby/bootstrap.php index 9a419de..9500125 100644 --- a/kirby/bootstrap.php +++ b/kirby/bootstrap.php @@ -6,7 +6,7 @@ */ if ( version_compare(PHP_VERSION, '8.1.0', '>=') === false || - version_compare(PHP_VERSION, '8.4.0', '<') === false + version_compare(PHP_VERSION, '8.5.0', '<') === false ) { die(include __DIR__ . '/views/php.php'); } diff --git a/kirby/cacert.pem b/kirby/cacert.pem index 86d6cd8..584af3c 100644 --- a/kirby/cacert.pem +++ b/kirby/cacert.pem @@ -1,7 +1,9 @@ ## ## Bundle of CA Root Certificates ## -## Certificate data from Mozilla as of: Tue Jul 2 03:12:04 2024 GMT +## Certificate data from Mozilla as of: Tue Feb 25 04:12:03 2025 GMT +## +## Find updated versions here: https://curl.se/docs/caextract.html ## ## This is a bundle of X.509 certificates of public Certificate Authorities ## (CA). These were automatically extracted from Mozilla's root certificates @@ -14,7 +16,7 @@ ## Just configure this file as the SSLCACertificateFile. ## ## Conversion done with mk-ca-bundle.pl version 1.29. -## SHA256: 456ff095dde6dd73354c5c28c73d9c06f53b61a803963414cb91a1d92945cdd3 +## SHA256: 620fd89c02acb0019f1899dab7907db5d20735904f5a9a0d3a8771a5857ac482 ## @@ -369,37 +371,6 @@ NU0LbbqhPcCT4H8js1WtciVORvnSFu+wZMEBnunKoGqYDs/YYPIvSbjkQuE4NRb0yG5P94FW6Lqj viOvrv1vA+ACOzB2+httQc8Bsem4yWb02ybzOqR08kkkW8mw0FfB+j564ZfJ -----END CERTIFICATE----- -SwissSign Silver CA - G2 -======================== ------BEGIN CERTIFICATE----- -MIIFvTCCA6WgAwIBAgIITxvUL1S7L0swDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCQ0gxFTAT -BgNVBAoTDFN3aXNzU2lnbiBBRzEhMB8GA1UEAxMYU3dpc3NTaWduIFNpbHZlciBDQSAtIEcyMB4X -DTA2MTAyNTA4MzI0NloXDTM2MTAyNTA4MzI0NlowRzELMAkGA1UEBhMCQ0gxFTATBgNVBAoTDFN3 -aXNzU2lnbiBBRzEhMB8GA1UEAxMYU3dpc3NTaWduIFNpbHZlciBDQSAtIEcyMIICIjANBgkqhkiG -9w0BAQEFAAOCAg8AMIICCgKCAgEAxPGHf9N4Mfc4yfjDmUO8x/e8N+dOcbpLj6VzHVxumK4DV644 -N0MvFz0fyM5oEMF4rhkDKxD6LHmD9ui5aLlV8gREpzn5/ASLHvGiTSf5YXu6t+WiE7brYT7QbNHm -+/pe7R20nqA1W6GSy/BJkv6FCgU+5tkL4k+73JU3/JHpMjUi0R86TieFnbAVlDLaYQ1HTWBCrpJH -6INaUFjpiou5XaHc3ZlKHzZnu0jkg7Y360g6rw9njxcH6ATK72oxh9TAtvmUcXtnZLi2kUpCe2Uu -MGoM9ZDulebyzYLs2aFK7PayS+VFheZteJMELpyCbTapxDFkH4aDCyr0NQp4yVXPQbBH6TCfmb5h -qAaEuSh6XzjZG6k4sIN/c8HDO0gqgg8hm7jMqDXDhBuDsz6+pJVpATqJAHgE2cn0mRmrVn5bi4Y5 -FZGkECwJMoBgs5PAKrYYC51+jUnyEEp/+dVGLxmSo5mnJqy7jDzmDrxHB9xzUfFwZC8I+bRHHTBs -ROopN4WSaGa8gzj+ezku01DwH/teYLappvonQfGbGHLy9YR0SslnxFSuSGTfjNFusB3hB48IHpmc -celM2KX3RxIfdNFRnobzwqIjQAtz20um53MGjMGg6cFZrEb65i/4z3GcRm25xBWNOHkDRUjvxF3X -CO6HOSKGsg0PWEP3calILv3q1h8CAwEAAaOBrDCBqTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/ -BAUwAwEB/zAdBgNVHQ4EFgQUF6DNweRBtjpbO8tFnb0cwpj6hlgwHwYDVR0jBBgwFoAUF6DNweRB -tjpbO8tFnb0cwpj6hlgwRgYDVR0gBD8wPTA7BglghXQBWQEDAQEwLjAsBggrBgEFBQcCARYgaHR0 -cDovL3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIBAHPGgeAn0i0P -4JUw4ppBf1AsX19iYamGamkYDHRJ1l2E6kFSGG9YrVBWIGrGvShpWJHckRE1qTodvBqlYJ7YH39F -kWnZfrt4csEGDyrOj4VwYaygzQu4OSlWhDJOhrs9xCrZ1x9y7v5RoSJBsXECYxqCsGKrXlcSH9/L -3XWgwF15kIwb4FDm3jH+mHtwX6WQ2K34ArZv02DdQEsixT2tOnqfGhpHkXkzuoLcMmkDlm4fS/Bx -/uNncqCxv1yL5PqZIseEuRuNI5c/7SXgz2W79WEE790eslpBIlqhn10s6FvJbakMDHiqYMZWjwFa -DGi8aRl5xB9+lwW/xekkUV7U1UtT7dkjWjYDZaPBA61BMPNGG4WQr2W11bHkFlt4dR2Xem1ZqSqP -e97Dh4kQmUlzeMg9vVE1dCrV8X5pGyq7O70luJpaPXJhkGaH7gzWTdQRdAtq/gsD/KNVV4n+Ssuu -WxcFyPKNIzFTONItaj+CuY0IavdeQXRuwxF+B6wpYJE/OMpXEA29MC/HpeZBoNquBYeaoKRlbEwJ -DIm6uNO5wJOKMPqN5ZprFQFOZ6raYlY+hAhm0sQ2fac+EPyI4NSA5QC9qvNOBqN6avlicuMJT+ub -DgEj8Z+7fNzcbBGXJbLytGMU0gYqZ4yD9c7qB9iaah7s5Aq7KkzrCWA5zspi2C5u ------END CERTIFICATE----- - SecureTrust CA ============== -----BEGIN CERTIFICATE----- @@ -582,27 +553,6 @@ NwUASZQDhETnv0Mxz3WLJdH0pmT1kvarBes96aULNmLazAZfNou2XjG4Kvte9nHfRCaexOYNkbQu dZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E= -----END CERTIFICATE----- -SecureSign RootCA11 -=================== ------BEGIN CERTIFICATE----- -MIIDbTCCAlWgAwIBAgIBATANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQGEwJKUDErMCkGA1UEChMi -SmFwYW4gQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcywgSW5jLjEcMBoGA1UEAxMTU2VjdXJlU2lnbiBS -b290Q0ExMTAeFw0wOTA0MDgwNDU2NDdaFw0yOTA0MDgwNDU2NDdaMFgxCzAJBgNVBAYTAkpQMSsw -KQYDVQQKEyJKYXBhbiBDZXJ0aWZpY2F0aW9uIFNlcnZpY2VzLCBJbmMuMRwwGgYDVQQDExNTZWN1 -cmVTaWduIFJvb3RDQTExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA/XeqpRyQBTvL -TJszi1oURaTnkBbR31fSIRCkF/3frNYfp+TbfPfs37gD2pRY/V1yfIw/XwFndBWW4wI8h9uuywGO -wvNmxoVF9ALGOrVisq/6nL+k5tSAMJjzDbaTj6nU2DbysPyKyiyhFTOVMdrAG/LuYpmGYz+/3ZMq -g6h2uRMft85OQoWPIucuGvKVCbIFtUROd6EgvanyTgp9UK31BQ1FT0Zx/Sg+U/sE2C3XZR1KG/rP -O7AxmjVuyIsG0wCR8pQIZUyxNAYAeoni8McDWc/V1uinMrPmmECGxc0nEovMe863ETxiYAcjPitA -bpSACW22s293bzUIUPsCh8U+iQIDAQABo0IwQDAdBgNVHQ4EFgQUW/hNT7KlhtQ60vFjmqC+CfZX -t94wDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAKCh -OBZmLqdWHyGcBvod7bkixTgm2E5P7KN/ed5GIaGHd48HCJqypMWvDzKYC3xmKbabfSVSSUOrTC4r -bnpwrxYO4wJs+0LmGJ1F2FXI6Dvd5+H0LgscNFxsWEr7jIhQX5Ucv+2rIrVls4W6ng+4reV6G4pQ -Oh29Dbx7VFALuUKvVaAYga1lme++5Jy/xIWrQbJUb9wlze144o4MjQlJ3WN7WmmWAiGovVJZ6X01 -y8hSyn+B/tlr0/cR7SXf+Of5pPpyl4RTDaXQMhhRdlkUbA/r7F+AjHVDg8OFmP9Mni0N5HeDk061 -lgeLKBObjBmNQSdJQO7e5iNEOdyhIta6A/I= ------END CERTIFICATE----- - Microsec e-Szigno Root CA 2009 ============================== -----BEGIN CERTIFICATE----- @@ -2317,40 +2267,6 @@ hcErulWuBurQB7Lcq9CClnXO0lD+mefPL5/ndtFhKvshuzHQqp9HpLIiyhY6UFfEW0NnxWViA0kB dBb9HxEGmpv0 -----END CERTIFICATE----- -Entrust Root Certification Authority - G4 -========================================= ------BEGIN CERTIFICATE----- -MIIGSzCCBDOgAwIBAgIRANm1Q3+vqTkPAAAAAFVlrVgwDQYJKoZIhvcNAQELBQAwgb4xCzAJBgNV -BAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1c3Qu -bmV0L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxNSBFbnRydXN0LCBJbmMuIC0gZm9yIGF1 -dGhvcml6ZWQgdXNlIG9ubHkxMjAwBgNVBAMTKUVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1 -dGhvcml0eSAtIEc0MB4XDTE1MDUyNzExMTExNloXDTM3MTIyNzExNDExNlowgb4xCzAJBgNVBAYT -AlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1c3QubmV0 -L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxNSBFbnRydXN0LCBJbmMuIC0gZm9yIGF1dGhv -cml6ZWQgdXNlIG9ubHkxMjAwBgNVBAMTKUVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhv -cml0eSAtIEc0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAsewsQu7i0TD/pZJH4i3D -umSXbcr3DbVZwbPLqGgZ2K+EbTBwXX7zLtJTmeH+H17ZSK9dE43b/2MzTdMAArzE+NEGCJR5WIoV -3imz/f3ET+iq4qA7ec2/a0My3dl0ELn39GjUu9CH1apLiipvKgS1sqbHoHrmSKvS0VnM1n4j5pds -8ELl3FFLFUHtSUrJ3hCX1nbB76W1NhSXNdh4IjVS70O92yfbYVaCNNzLiGAMC1rlLAHGVK/XqsEQ -e9IFWrhAnoanw5CGAlZSCXqc0ieCU0plUmr1POeo8pyvi73TDtTUXm6Hnmo9RR3RXRv06QqsYJn7 -ibT/mCzPfB3pAqoEmh643IhuJbNsZvc8kPNXwbMv9W3y+8qh+CmdRouzavbmZwe+LGcKKh9asj5X -xNMhIWNlUpEbsZmOeX7m640A2Vqq6nPopIICR5b+W45UYaPrL0swsIsjdXJ8ITzI9vF01Bx7owVV -7rtNOzK+mndmnqxpkCIHH2E6lr7lmk/MBTwoWdPBDFSoWWG9yHJM6Nyfh3+9nEg2XpWjDrk4JFX8 -dWbrAuMINClKxuMrLzOg2qOGpRKX/YAr2hRC45K9PvJdXmd0LhyIRyk0X+IyqJwlN4y6mACXi0mW -Hv0liqzc2thddG5msP9E36EYxr5ILzeUePiVSj9/E15dWf10hkNjc0kCAwEAAaNCMEAwDwYDVR0T -AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJ84xFYjwznooHFs6FRM5Og6sb9n -MA0GCSqGSIb3DQEBCwUAA4ICAQAS5UKme4sPDORGpbZgQIeMJX6tuGguW8ZAdjwD+MlZ9POrYs4Q -jbRaZIxowLByQzTSGwv2LFPSypBLhmb8qoMi9IsabyZIrHZ3CL/FmFz0Jomee8O5ZDIBf9PD3Vht -7LGrhFV0d4QEJ1JrhkzO3bll/9bGXp+aEJlLdWr+aumXIOTkdnrG0CSqkM0gkLpHZPt/B7NTeLUK -YvJzQ85BK4FqLoUWlFPUa19yIqtRLULVAJyZv967lDtX/Zr1hstWO1uIAeV8KEsD+UmDfLJ/fOPt -jqF/YFOOVZ1QNBIPt5d7bIdKROf1beyAN/BYGW5KaHbwH5Lk6rWS02FREAutp9lfx1/cH6NcjKF+ -m7ee01ZvZl4HliDtC3T7Zk6LERXpgUl+b7DUUH8i119lAg2m9IUe2K4GS0qn0jFmwvjO5QimpAKW -RGhXxNUzzxkvFMSUHHuk2fCfDrGA4tGeEWSpiBE6doLlYsKA2KSD7ZPvfC+QsDJMlhVoSFLUmQjA -JOgc47OlIQ6SwJAfzyBfyjs4x7dtOvPmRLgOMWuIjnDrnBdSqEGULoe256YSxXXfW8AKbnuk5F6G -+TaU33fD6Q3AOfF5u0aOq0NZJ7cguyPpVkAh7DE9ZapD8j3fcEThuk0mEDuYn/PIjhs4ViFqUZPT -kcpG2om3PVODLAgfi49T3f+sHw== ------END CERTIFICATE----- - Microsoft ECC Root Certificate Authority 2017 ============================================= -----BEGIN CERTIFICATE----- @@ -2600,6 +2516,36 @@ vLtoURMMA/cVi4RguYv/Uo7njLwcAjA8+RHUjE7AwWHCFUyqqx0LMV87HOIAl0Qx5v5zli/altP+ CAezNIm8BZ/3Hobui3A= -----END CERTIFICATE----- +GLOBALTRUST 2020 +================ +-----BEGIN CERTIFICATE----- +MIIFgjCCA2qgAwIBAgILWku9WvtPilv6ZeUwDQYJKoZIhvcNAQELBQAwTTELMAkGA1UEBhMCQVQx +IzAhBgNVBAoTGmUtY29tbWVyY2UgbW9uaXRvcmluZyBHbWJIMRkwFwYDVQQDExBHTE9CQUxUUlVT +VCAyMDIwMB4XDTIwMDIxMDAwMDAwMFoXDTQwMDYxMDAwMDAwMFowTTELMAkGA1UEBhMCQVQxIzAh +BgNVBAoTGmUtY29tbWVyY2UgbW9uaXRvcmluZyBHbWJIMRkwFwYDVQQDExBHTE9CQUxUUlVTVCAy +MDIwMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAri5WrRsc7/aVj6B3GyvTY4+ETUWi +D59bRatZe1E0+eyLinjF3WuvvcTfk0Uev5E4C64OFudBc/jbu9G4UeDLgztzOG53ig9ZYybNpyrO +VPu44sB8R85gfD+yc/LAGbaKkoc1DZAoouQVBGM+uq/ufF7MpotQsjj3QWPKzv9pj2gOlTblzLmM +CcpL3TGQlsjMH/1WljTbjhzqLL6FLmPdqqmV0/0plRPwyJiT2S0WR5ARg6I6IqIoV6Lr/sCMKKCm +fecqQjuCgGOlYx8ZzHyyZqjC0203b+J+BlHZRYQfEs4kUmSFC0iAToexIiIwquuuvuAC4EDosEKA +A1GqtH6qRNdDYfOiaxaJSaSjpCuKAsR49GiKweR6NrFvG5Ybd0mN1MkGco/PU+PcF4UgStyYJ9OR +JitHHmkHr96i5OTUawuzXnzUJIBHKWk7buis/UDr2O1xcSvy6Fgd60GXIsUf1DnQJ4+H4xj04KlG +DfV0OoIu0G4skaMxXDtG6nsEEFZegB31pWXogvziB4xiRfUg3kZwhqG8k9MedKZssCz3AwyIDMvU +clOGvGBG85hqwvG/Q/lwIHfKN0F5VVJjjVsSn8VoxIidrPIwq7ejMZdnrY8XD2zHc+0klGvIg5rQ +mjdJBKuxFshsSUktq6HQjJLyQUp5ISXbY9e2nKd+Qmn7OmMCAwEAAaNjMGEwDwYDVR0TAQH/BAUw +AwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFNwuH9FhN3nkq9XVsxJxaD1qaJwiMB8GA1Ud +IwQYMBaAFNwuH9FhN3nkq9XVsxJxaD1qaJwiMA0GCSqGSIb3DQEBCwUAA4ICAQCR8EICaEDuw2jA +VC/f7GLDw56KoDEoqoOOpFaWEhCGVrqXctJUMHytGdUdaG/7FELYjQ7ztdGl4wJCXtzoRlgHNQIw +4Lx0SsFDKv/bGtCwr2zD/cuz9X9tAy5ZVp0tLTWMstZDFyySCstd6IwPS3BD0IL/qMy/pJTAvoe9 +iuOTe8aPmxadJ2W8esVCgmxcB9CpwYhgROmYhRZf+I/KARDOJcP5YBugxZfD0yyIMaK9MOzQ0MAS +8cE54+X1+NZK3TTN+2/BT+MAi1bikvcoskJ3ciNnxz8RFbLEAwW+uxF7Cr+obuf/WEPPm2eggAe2 +HcqtbepBEX4tdJP7wry+UUTF72glJ4DjyKDUEuzZpTcdN3y0kcra1LGWge9oXHYQSa9+pTeAsRxS +vTOBTI/53WXZFM2KJVj04sWDpQmQ1GwUY7VA3+vA/MRYfg0UFodUJ25W5HCEuGwyEn6CMUO+1918 +oa2u1qsgEu8KwxCMSZY13At1XrFP1U80DhEgB3VDRemjEdqso5nCtnkn4rnvyOL2NSl6dPrFf4IF +YqYK6miyeUcGbvJXqBUzxvd4Sj1Ce2t+/vdG6tHrju+IaFvowdlxfv1k7/9nR4hYJS8+hge9+6jl +gqispdNpQ80xiEmEU5LAsTkbOYMBMMTyqfrQA71yN2BWHzZ8vTmR9W0Nv3vXkg== +-----END CERTIFICATE----- + ANF Secure Server Root CA ========================= -----BEGIN CERTIFICATE----- @@ -3138,36 +3084,6 @@ AwMDaAAwZQIxALGOWiDDshliTd6wT99u0nCK8Z9+aozmut6Dacpps6kFtZaSF4fC0urQe87YQVt8 rgIwRt7qy12a7DLCZRawTDBcMPPaTnOGBtjOiQRINzf43TNRnXCve1XYAS59BWQOhriR -----END CERTIFICATE----- -Security Communication RootCA3 -============================== ------BEGIN CERTIFICATE----- -MIIFfzCCA2egAwIBAgIJAOF8N0D9G/5nMA0GCSqGSIb3DQEBDAUAMF0xCzAJBgNVBAYTAkpQMSUw -IwYDVQQKExxTRUNPTSBUcnVzdCBTeXN0ZW1zIENPLixMVEQuMScwJQYDVQQDEx5TZWN1cml0eSBD -b21tdW5pY2F0aW9uIFJvb3RDQTMwHhcNMTYwNjE2MDYxNzE2WhcNMzgwMTE4MDYxNzE2WjBdMQsw -CQYDVQQGEwJKUDElMCMGA1UEChMcU0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UE -AxMeU2VjdXJpdHkgQ29tbXVuaWNhdGlvbiBSb290Q0EzMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A -MIICCgKCAgEA48lySfcw3gl8qUCBWNO0Ot26YQ+TUG5pPDXC7ltzkBtnTCHsXzW7OT4rCmDvu20r -hvtxosis5FaU+cmvsXLUIKx00rgVrVH+hXShuRD+BYD5UpOzQD11EKzAlrenfna84xtSGc4RHwsE -NPXY9Wk8d/Nk9A2qhd7gCVAEF5aEt8iKvE1y/By7z/MGTfmfZPd+pmaGNXHIEYBMwXFAWB6+oHP2 -/D5Q4eAvJj1+XCO1eXDe+uDRpdYMQXF79+qMHIjH7Iv10S9VlkZ8WjtYO/u62C21Jdp6Ts9EriGm -npjKIG58u4iFW/vAEGK78vknR+/RiTlDxN/e4UG/VHMgly1s2vPUB6PmudhvrvyMGS7TZ2crldtY -XLVqAvO4g160a75BflcJdURQVc1aEWEhCmHCqYj9E7wtiS/NYeCVvsq1e+F7NGcLH7YMx3weGVPK -p7FKFSBWFHA9K4IsD50VHUeAR/94mQ4xr28+j+2GaR57GIgUssL8gjMunEst+3A7caoreyYn8xrC -3PsXuKHqy6C0rtOUfnrQq8PsOC0RLoi/1D+tEjtCrI8Cbn3M0V9hvqG8OmpI6iZVIhZdXw3/JzOf -GAN0iltSIEdrRU0id4xVJ/CvHozJgyJUt5rQT9nO/NkuHJYosQLTA70lUhw0Zk8jq/R3gpYd0Vcw -CBEF/VfR2ccCAwEAAaNCMEAwHQYDVR0OBBYEFGQUfPxYchamCik0FW8qy7z8r6irMA4GA1UdDwEB -/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBDAUAA4ICAQDcAiMI4u8hOscNtybS -YpOnpSNyByCCYN8Y11StaSWSntkUz5m5UoHPrmyKO1o5yGwBQ8IibQLwYs1OY0PAFNr0Y/Dq9HHu -Tofjcan0yVflLl8cebsjqodEV+m9NU1Bu0soo5iyG9kLFwfl9+qd9XbXv8S2gVj/yP9kaWJ5rW4O -H3/uHWnlt3Jxs/6lATWUVCvAUm2PVcTJ0rjLyjQIUYWg9by0F1jqClx6vWPGOi//lkkZhOpn2ASx -YfQAW0q3nHE3GYV5v4GwxxMOdnE+OoAGrgYWp421wsTL/0ClXI2lyTrtcoHKXJg80jQDdwj98ClZ -XSEIx2C/pHF7uNkegr4Jr2VvKKu/S7XuPghHJ6APbw+LP6yVGPO5DtxnVW5inkYO0QR4ynKudtml -+LLfiAlhi+8kTtFZP1rUPcmTPCtk9YENFpb3ksP+MW/oKjJ0DvRMmEoYDjBU1cXrvMUVnuiZIesn -KwkK2/HmcBhWuwzkvvnoEKQTkrgc4NtnHVMDpCKn3F2SEDzq//wbEBrD2NCcnWXL0CsnMQMeNuE9 -dnUM/0Umud1RvCPHX9jYhxBAEg09ODfnRDwYwFMJZI//1ZqmfHAuc1Uh6N//g7kdPjIe1qZ9LPFm -6Vwdp6POXiUyK+OVrCoHzrQoeIY8LaadTdJ0MN1kURXbg4NR16/9M51NZg== ------END CERTIFICATE----- - Security Communication ECC RootCA1 ================================== -----BEGIN CERTIFICATE----- @@ -3566,3 +3482,161 @@ Y1w8ndYn81LsF7Kpryz3dvgwHQYDVR0OBBYEFJPhQ2NcPJ3WJ/NS7Beyqa8s93b4MA4GA1UdDwEB cFBTApFwhVmpHqTm6iMxoAACMQD94vizrxa5HnPEluPBMBnYfubDl94cT7iJLzPrSA8Z94dGXSaQ pYXFuXqUPoeovQA= -----END CERTIFICATE----- + +TWCA CYBER Root CA +================== +-----BEGIN CERTIFICATE----- +MIIFjTCCA3WgAwIBAgIQQAE0jMIAAAAAAAAAATzyxjANBgkqhkiG9w0BAQwFADBQMQswCQYDVQQG +EwJUVzESMBAGA1UEChMJVEFJV0FOLUNBMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJUV0NB +IENZQkVSIFJvb3QgQ0EwHhcNMjIxMTIyMDY1NDI5WhcNNDcxMTIyMTU1OTU5WjBQMQswCQYDVQQG +EwJUVzESMBAGA1UEChMJVEFJV0FOLUNBMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJUV0NB +IENZQkVSIFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDG+Moe2Qkgfh1s +Ts6P40czRJzHyWmqOlt47nDSkvgEs1JSHWdyKKHfi12VCv7qze33Kc7wb3+szT3vsxxFavcokPFh +V8UMxKNQXd7UtcsZyoC5dc4pztKFIuwCY8xEMCDa6pFbVuYdHNWdZsc/34bKS1PE2Y2yHer43CdT +o0fhYcx9tbD47nORxc5zb87uEB8aBs/pJ2DFTxnk684iJkXXYJndzk834H/nY62wuFm40AZoNWDT +Nq5xQwTxaWV4fPMf88oon1oglWa0zbfuj3ikRRjpJi+NmykosaS3Om251Bw4ckVYsV7r8Cibt4LK +/c/WMw+f+5eesRycnupfXtuq3VTpMCEobY5583WSjCb+3MX2w7DfRFlDo7YDKPYIMKoNM+HvnKkH +IuNZW0CP2oi3aQiotyMuRAlZN1vH4xfyIutuOVLF3lSnmMlLIJXcRolftBL5hSmO68gnFSDAS9TM +fAxsNAwmmyYxpjyn9tnQS6Jk/zuZQXLB4HCX8SS7K8R0IrGsayIyJNN4KsDAoS/xUgXJP+92ZuJF +2A09rZXIx4kmyA+upwMu+8Ff+iDhcK2wZSA3M2Cw1a/XDBzCkHDXShi8fgGwsOsVHkQGzaRP6AzR +wyAQ4VRlnrZR0Bp2a0JaWHY06rc3Ga4udfmW5cFZ95RXKSWNOkyrTZpB0F8mAwIDAQABo2MwYTAO +BgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBSdhWEUfMFib5do5E83 +QOGt4A1WNzAdBgNVHQ4EFgQUnYVhFHzBYm+XaORPN0DhreANVjcwDQYJKoZIhvcNAQEMBQADggIB +AGSPesRiDrWIzLjHhg6hShbNcAu3p4ULs3a2D6f/CIsLJc+o1IN1KriWiLb73y0ttGlTITVX1olN +c79pj3CjYcya2x6a4CD4bLubIp1dhDGaLIrdaqHXKGnK/nZVekZn68xDiBaiA9a5F/gZbG0jAn/x +X9AKKSM70aoK7akXJlQKTcKlTfjF/biBzysseKNnTKkHmvPfXvt89YnNdJdhEGoHK4Fa0o635yDR +IG4kqIQnoVesqlVYL9zZyvpoBJ7tRCT5dEA7IzOrg1oYJkK2bVS1FmAwbLGg+LhBoF1JSdJlBTrq +/p1hvIbZv97Tujqxf36SNI7JAG7cmL3c7IAFrQI932XtCwP39xaEBDG6k5TY8hL4iuO/Qq+n1M0R +FxbIQh0UqEL20kCGoE8jypZFVmAGzbdVAaYBlGX+bgUJurSkquLvWL69J1bY73NxW0Qz8ppy6rBe +Pm6pUlvscG21h483XjyMnM7k8M4MZ0HMzvaAq07MTFb1wWFZk7Q+ptq4NxKfKjLji7gh7MMrZQzv +It6IKTtM1/r+t+FHvpw+PoP7UV31aPcuIYXcv/Fa4nzXxeSDwWrruoBa3lwtcHb4yOWHh8qgnaHl +IhInD0Q9HWzq1MKLL295q39QpsQZp6F6t5b5wR9iWqJDB0BeJsas7a5wFsWqynKKTbDPAYsDP27X +-----END CERTIFICATE----- + +SecureSign Root CA12 +==================== +-----BEGIN CERTIFICATE----- +MIIDcjCCAlqgAwIBAgIUZvnHwa/swlG07VOX5uaCwysckBYwDQYJKoZIhvcNAQELBQAwUTELMAkG +A1UEBhMCSlAxIzAhBgNVBAoTGkN5YmVydHJ1c3QgSmFwYW4gQ28uLCBMdGQuMR0wGwYDVQQDExRT +ZWN1cmVTaWduIFJvb3QgQ0ExMjAeFw0yMDA0MDgwNTM2NDZaFw00MDA0MDgwNTM2NDZaMFExCzAJ +BgNVBAYTAkpQMSMwIQYDVQQKExpDeWJlcnRydXN0IEphcGFuIENvLiwgTHRkLjEdMBsGA1UEAxMU +U2VjdXJlU2lnbiBSb290IENBMTIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6OcE3 +emhFKxS06+QT61d1I02PJC0W6K6OyX2kVzsqdiUzg2zqMoqUm048luT9Ub+ZyZN+v/mtp7JIKwcc +J/VMvHASd6SFVLX9kHrko+RRWAPNEHl57muTH2SOa2SroxPjcf59q5zdJ1M3s6oYwlkm7Fsf0uZl +fO+TvdhYXAvA42VvPMfKWeP+bl+sg779XSVOKik71gurFzJ4pOE+lEa+Ym6b3kaosRbnhW70CEBF +EaCeVESE99g2zvVQR9wsMJvuwPWW0v4JhscGWa5Pro4RmHvzC1KqYiaqId+OJTN5lxZJjfU+1Uef +NzFJM3IFTQy2VYzxV4+Kh9GtxRESOaCtAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0P +AQH/BAQDAgEGMB0GA1UdDgQWBBRXNPN0zwRL1SXm8UC2LEzZLemgrTANBgkqhkiG9w0BAQsFAAOC +AQEAPrvbFxbS8hQBICw4g0utvsqFepq2m2um4fylOqyttCg6r9cBg0krY6LdmmQOmFxv3Y67ilQi +LUoT865AQ9tPkbeGGuwAtEGBpE/6aouIs3YIcipJQMPTw4WJmBClnW8Zt7vPemVV2zfrPIpyMpce +mik+rY3moxtt9XUa5rBouVui7mlHJzWhhpmA8zNL4WukJsPvdFlseqJkth5Ew1DgDzk9qTPxpfPS +vWKErI4cqc1avTc7bgoitPQV55FYxTpE05Uo2cBl6XLK0A+9H7MV2anjpEcJnuDLN/v9vZfVvhga +aaI5gdka9at/yOPiZwud9AzqVN/Ssq+xIvEg37xEHA== +-----END CERTIFICATE----- + +SecureSign Root CA14 +==================== +-----BEGIN CERTIFICATE----- +MIIFcjCCA1qgAwIBAgIUZNtaDCBO6Ncpd8hQJ6JaJ90t8sswDQYJKoZIhvcNAQEMBQAwUTELMAkG +A1UEBhMCSlAxIzAhBgNVBAoTGkN5YmVydHJ1c3QgSmFwYW4gQ28uLCBMdGQuMR0wGwYDVQQDExRT +ZWN1cmVTaWduIFJvb3QgQ0ExNDAeFw0yMDA0MDgwNzA2MTlaFw00NTA0MDgwNzA2MTlaMFExCzAJ +BgNVBAYTAkpQMSMwIQYDVQQKExpDeWJlcnRydXN0IEphcGFuIENvLiwgTHRkLjEdMBsGA1UEAxMU +U2VjdXJlU2lnbiBSb290IENBMTQwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDF0nqh +1oq/FjHQmNE6lPxauG4iwWL3pwon71D2LrGeaBLwbCRjOfHw3xDG3rdSINVSW0KZnvOgvlIfX8xn +bacuUKLBl422+JX1sLrcneC+y9/3OPJH9aaakpUqYllQC6KxNedlsmGy6pJxaeQp8E+BgQQ8sqVb +1MWoWWd7VRxJq3qdwudzTe/NCcLEVxLbAQ4jeQkHO6Lo/IrPj8BGJJw4J+CDnRugv3gVEOuGTgpa +/d/aLIJ+7sr2KeH6caH3iGicnPCNvg9JkdjqOvn90Ghx2+m1K06Ckm9mH+Dw3EzsytHqunQG+bOE +kJTRX45zGRBdAuVwpcAQ0BB8b8VYSbSwbprafZX1zNoCr7gsfXmPvkPx+SgojQlD+Ajda8iLLCSx +jVIHvXiby8posqTdDEx5YMaZ0ZPxMBoH064iwurO8YQJzOAUbn8/ftKChazcqRZOhaBgy/ac18iz +ju3Gm5h1DVXoX+WViwKkrkMpKBGk5hIwAUt1ax5mnXkvpXYvHUC0bcl9eQjs0Wq2XSqypWa9a4X0 +dFbD9ed1Uigspf9mR6XU/v6eVL9lfgHWMI+lNpyiUBzuOIABSMbHdPTGrMNASRZhdCyvjG817XsY +AFs2PJxQDcqSMxDxJklt33UkN4Ii1+iW/RVLApY+B3KVfqs9TC7XyvDf4Fg/LS8EmjijAQIDAQAB +o0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUBpOjCl4oaTeq +YR3r6/wtbyPk86AwDQYJKoZIhvcNAQEMBQADggIBAJaAcgkGfpzMkwQWu6A6jZJOtxEaCnFxEM0E +rX+lRVAQZk5KQaID2RFPeje5S+LGjzJmdSX7684/AykmjbgWHfYfM25I5uj4V7Ibed87hwriZLoA +ymzvftAj63iP/2SbNDefNWWipAA9EiOWWF3KY4fGoweITedpdopTzfFP7ELyk+OZpDc8h7hi2/Ds +Hzc/N19DzFGdtfCXwreFamgLRB7lUe6TzktuhsHSDCRZNhqfLJGP4xjblJUK7ZGqDpncllPjYYPG +FrojutzdfhrGe0K22VoF3Jpf1d+42kd92jjbrDnVHmtsKheMYc2xbXIBw8MgAGJoFjHVdqqGuw6q +nsb58Nn4DSEC5MUoFlkRudlpcyqSeLiSV5sI8jrlL5WwWLdrIBRtFO8KvH7YVdiI2i/6GaX7i+B/ +OfVyK4XELKzvGUWSTLNhB9xNH27SgRNcmvMSZ4PPmz+Ln52kuaiWA3rF7iDeM9ovnhp6dB7h7sxa +OgTdsxoEqBRjrLdHEoOabPXm6RUVkRqEGQ6UROcSjiVbgGcZ3GOTEAtlLor6CZpO2oYofaphNdgO +pygau1LgePhsumywbrmHXumZNTfxPWQrqaA0k89jL9WB365jJ6UeTo3cKXhZ+PmhIIynJkBugnLN +eLLIjzwec+fBH7/PzqUqm9tEZDKgu39cJRNItX+S +-----END CERTIFICATE----- + +SecureSign Root CA15 +==================== +-----BEGIN CERTIFICATE----- +MIICIzCCAamgAwIBAgIUFhXHw9hJp75pDIqI7fBw+d23PocwCgYIKoZIzj0EAwMwUTELMAkGA1UE +BhMCSlAxIzAhBgNVBAoTGkN5YmVydHJ1c3QgSmFwYW4gQ28uLCBMdGQuMR0wGwYDVQQDExRTZWN1 +cmVTaWduIFJvb3QgQ0ExNTAeFw0yMDA0MDgwODMyNTZaFw00NTA0MDgwODMyNTZaMFExCzAJBgNV +BAYTAkpQMSMwIQYDVQQKExpDeWJlcnRydXN0IEphcGFuIENvLiwgTHRkLjEdMBsGA1UEAxMUU2Vj +dXJlU2lnbiBSb290IENBMTUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQLUHSNZDKZmbPSYAi4Io5G +dCx4wCtELW1fHcmuS1Iggz24FG1Th2CeX2yF2wYUleDHKP+dX+Sq8bOLbe1PL0vJSpSRZHX+AezB +2Ot6lHhWGENfa4HL9rzatAy2KZMIaY+jQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD +AgEGMB0GA1UdDgQWBBTrQciu/NWeUUj1vYv0hyCTQSvT9DAKBggqhkjOPQQDAwNoADBlAjEA2S6J +fl5OpBEHvVnCB96rMjhTKkZEBhd6zlHp4P9mLQlO4E/0BdGF9jVg3PVys0Z9AjBEmEYagoUeYWmJ +SwdLZrWeqrqgHkHZAXQ6bkU6iYAZezKYVWOr62Nuk22rGwlgMU4= +-----END CERTIFICATE----- + +D-TRUST BR Root CA 2 2023 +========================= +-----BEGIN CERTIFICATE----- +MIIFqTCCA5GgAwIBAgIQczswBEhb2U14LnNLyaHcZjANBgkqhkiG9w0BAQ0FADBIMQswCQYDVQQG +EwJERTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMSIwIAYDVQQDExlELVRSVVNUIEJSIFJvb3QgQ0Eg +MiAyMDIzMB4XDTIzMDUwOTA4NTYzMVoXDTM4MDUwOTA4NTYzMFowSDELMAkGA1UEBhMCREUxFTAT +BgNVBAoTDEQtVHJ1c3QgR21iSDEiMCAGA1UEAxMZRC1UUlVTVCBCUiBSb290IENBIDIgMjAyMzCC +AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK7/CVmRgApKaOYkP7in5Mg6CjoWzckjYaCT +cfKri3OPoGdlYNJUa2NRb0kz4HIHE304zQaSBylSa053bATTlfrdTIzZXcFhfUvnKLNEgXtRr90z +sWh81k5M/itoucpmacTsXld/9w3HnDY25QdgrMBM6ghs7wZ8T1soegj8k12b9py0i4a6Ibn08OhZ +WiihNIQaJZG2tY/vsvmA+vk9PBFy2OMvhnbFeSzBqZCTRphny4NqoFAjpzv2gTng7fC5v2Xx2Mt6 +++9zA84A9H3X4F07ZrjcjrqDy4d2A/wl2ecjbwb9Z/Pg/4S8R7+1FhhGaRTMBffb00msa8yr5LUL +QyReS2tNZ9/WtT5PeB+UcSTq3nD88ZP+npNa5JRal1QMNXtfbO4AHyTsA7oC9Xb0n9Sa7YUsOCIv +x9gvdhFP/Wxc6PWOJ4d/GUohR5AdeY0cW/jPSoXk7bNbjb7EZChdQcRurDhaTyN0dKkSw/bSuREV +MweR2Ds3OmMwBtHFIjYoYiMQ4EbMl6zWK11kJNXuHA7e+whadSr2Y23OC0K+0bpwHJwh5Q8xaRfX +/Aq03u2AnMuStIv13lmiWAmlY0cL4UEyNEHZmrHZqLAbWt4NDfTisl01gLmB1IRpkQLLddCNxbU9 +CZEJjxShFHR5PtbJFR2kWVki3PaKRT08EtY+XTIvAgMBAAGjgY4wgYswDwYDVR0TAQH/BAUwAwEB +/zAdBgNVHQ4EFgQUZ5Dw1t61GNVGKX5cq/ieCLxklRAwDgYDVR0PAQH/BAQDAgEGMEkGA1UdHwRC +MEAwPqA8oDqGOGh0dHA6Ly9jcmwuZC10cnVzdC5uZXQvY3JsL2QtdHJ1c3RfYnJfcm9vdF9jYV8y +XzIwMjMuY3JsMA0GCSqGSIb3DQEBDQUAA4ICAQA097N3U9swFrktpSHxQCF16+tIFoE9c+CeJyrr +d6kTpGoKWloUMz1oH4Guaf2Mn2VsNELZLdB/eBaxOqwjMa1ef67nriv6uvw8l5VAk1/DLQOj7aRv +U9f6QA4w9QAgLABMjDu0ox+2v5Eyq6+SmNMW5tTRVFxDWy6u71cqqLRvpO8NVhTaIasgdp4D/Ca4 +nj8+AybmTNudX0KEPUUDAxxZiMrcLmEkWqTqJwtzEr5SswrPMhfiHocaFpVIbVrg0M8JkiZmkdij +YQ6qgYF/6FKC0ULn4B0Y+qSFNueG4A3rvNTJ1jxD8V1Jbn6Bm2m1iWKPiFLY1/4nwSPFyysCu7Ff +/vtDhQNGvl3GyiEm/9cCnnRK3PgTFbGBVzbLZVzRHTF36SXDw7IyN9XxmAnkbWOACKsGkoHU6XCP +pz+y7YaMgmo1yEJagtFSGkUPFaUA8JR7ZSdXOUPPfH/mvTWze/EZTN46ls/pdu4D58JDUjxqgejB +WoC9EV2Ta/vH5mQ/u2kc6d0li690yVRAysuTEwrt+2aSEcr1wPrYg1UDfNPFIkZ1cGt5SAYqgpq/ +5usWDiJFAbzdNpQ0qTUmiteXue4Icr80knCDgKs4qllo3UCkGJCy89UDyibK79XH4I9TjvAA46jt +n/mtd+ArY0+ew+43u3gJhJ65bvspmZDogNOfJA== +-----END CERTIFICATE----- + +D-TRUST EV Root CA 2 2023 +========================= +-----BEGIN CERTIFICATE----- +MIIFqTCCA5GgAwIBAgIQaSYJfoBLTKCnjHhiU19abzANBgkqhkiG9w0BAQ0FADBIMQswCQYDVQQG +EwJERTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMSIwIAYDVQQDExlELVRSVVNUIEVWIFJvb3QgQ0Eg +MiAyMDIzMB4XDTIzMDUwOTA5MTAzM1oXDTM4MDUwOTA5MTAzMlowSDELMAkGA1UEBhMCREUxFTAT +BgNVBAoTDEQtVHJ1c3QgR21iSDEiMCAGA1UEAxMZRC1UUlVTVCBFViBSb290IENBIDIgMjAyMzCC +AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANiOo4mAC7JXUtypU0w3uX9jFxPvp1sjW2l1 +sJkKF8GLxNuo4MwxusLyzV3pt/gdr2rElYfXR8mV2IIEUD2BCP/kPbOx1sWy/YgJ25yE7CUXFId/ +MHibaljJtnMoPDT3mfd/06b4HEV8rSyMlD/YZxBTfiLNTiVR8CUkNRFeEMbsh2aJgWi6zCudR3Mf +vc2RpHJqnKIbGKBv7FD0fUDCqDDPvXPIEysQEx6Lmqg6lHPTGGkKSv/BAQP/eX+1SH977ugpbzZM +lWGG2Pmic4ruri+W7mjNPU0oQvlFKzIbRlUWaqZLKfm7lVa/Rh3sHZMdwGWyH6FDrlaeoLGPaxK3 +YG14C8qKXO0elg6DpkiVjTujIcSuWMYAsoS0I6SWhjW42J7YrDRJmGOVxcttSEfi8i4YHtAxq910 +7PncjLgcjmgjutDzUNzPZY9zOjLHfP7KgiJPvo5iR2blzYfi6NUPGJ/lBHJLRjwQ8kTCZFZxTnXo +nMkmdMV9WdEKWw9t/p51HBjGGjp82A0EzM23RWV6sY+4roRIPrN6TagD4uJ+ARZZaBhDM7DS3LAa +QzXupdqpRlyuhoFBAUp0JuyfBr/CBTdkdXgpaP3F9ev+R/nkhbDhezGdpn9yo7nELC7MmVcOIQxF +AZRl62UJxmMiCzNJkkg8/M3OsD6Onov4/knFNXJHAgMBAAGjgY4wgYswDwYDVR0TAQH/BAUwAwEB +/zAdBgNVHQ4EFgQUqvyREBuHkV8Wub9PS5FeAByxMoAwDgYDVR0PAQH/BAQDAgEGMEkGA1UdHwRC +MEAwPqA8oDqGOGh0dHA6Ly9jcmwuZC10cnVzdC5uZXQvY3JsL2QtdHJ1c3RfZXZfcm9vdF9jYV8y +XzIwMjMuY3JsMA0GCSqGSIb3DQEBDQUAA4ICAQCTy6UfmRHsmg1fLBWTxj++EI14QvBukEdHjqOS +Mo1wj/Zbjb6JzkcBahsgIIlbyIIQbODnmaprxiqgYzWRaoUlrRc4pZt+UPJ26oUFKidBK7GB0aL2 +QHWpDsvxVUjY7NHss+jOFKE17MJeNRqrphYBBo7q3C+jisosketSjl8MmxfPy3MHGcRqwnNU73xD +UmPBEcrCRbH0O1P1aa4846XerOhUt7KR/aypH/KH5BfGSah82ApB9PI+53c0BFLd6IHyTS9URZ0V +4U/M5d40VxDJI3IXcI1QcB9WbMy5/zpaT2N6w25lBx2Eof+pDGOJbbJAiDnXH3dotfyc1dZnaVuo +dNv8ifYbMvekJKZ2t0dT741Jj6m2g1qllpBFYfXeA08mD6iL8AOWsKwV0HFaanuU5nCT2vFp4LJi +TZ6P/4mdm13NRemUAiKN4DV/6PEEeXFsVIP4M7kFMhtYVRFP0OUnR3Hs7dpn1mKmS00PaaLJvOwi +S5THaJQXfuKOKD62xur1NGyfN4gHONuGcfrNlUhDbqNPgofXNJhuS5N5YHVpD/Aa1VP6IQzCP+k/ +HxiMkl14p3ZnGbuy6n/pcAlWVqOwDAstNl7F6cTVg8uGF5csbBNvh1qvSaYd2804BC5f4ko1Di1L ++KIkBI3Y4WNeApI02phhXBxvWHZks/wCuPWdCg== +-----END CERTIFICATE----- diff --git a/kirby/composer.json b/kirby/composer.json index 9eb7970..65379ce 100644 --- a/kirby/composer.json +++ b/kirby/composer.json @@ -1,9 +1,9 @@ { "name": "getkirby/cms", - "description": "The Kirby 3 core", + "description": "The Kirby core", "license": "proprietary", "type": "kirby-cms", - "version": "3.10.1.1", + "version": "4.7.0", "keywords": [ "kirby", "cms", @@ -24,7 +24,7 @@ "source": "https://github.com/getkirby/kirby" }, "require": { - "php": "~8.1.0 || ~8.2.0 || ~8.3.0", + "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0", "ext-SimpleXML": "*", "ext-ctype": "*", "ext-curl": "*", @@ -36,16 +36,17 @@ "ext-libxml": "*", "ext-mbstring": "*", "ext-openssl": "*", - "claviska/simpleimage": "4.2.0", - "composer/semver": "3.4.0", - "filp/whoops": "2.15.4", + "christian-riesen/base32": "1.6.0", + "claviska/simpleimage": "4.2.1", + "composer/semver": "3.4.3", + "filp/whoops": "2.18.0", "getkirby/composer-installer": "^1.2.1", - "laminas/laminas-escaper": "2.13.0", + "laminas/laminas-escaper": "2.16.0", "michelf/php-smartypants": "1.8.1", - "phpmailer/phpmailer": "6.9.1", - "symfony/polyfill-intl-idn": "1.30.0", - "symfony/polyfill-mbstring": "1.30.0", - "symfony/yaml": "6.4.8" + "phpmailer/phpmailer": "6.9.3", + "symfony/polyfill-intl-idn": "1.31.0", + "symfony/polyfill-mbstring": "1.31.0", + "symfony/yaml": "6.4.18" }, "replace": { "symfony/polyfill-php72": "*" @@ -98,6 +99,8 @@ "analyze:composer": "composer validate --strict --no-check-version --no-check-all", "analyze:phpmd": "phpmd . ansi phpmd.xml.dist --exclude 'dependencies/*,tests/*,vendor/*'", "analyze:psalm": "psalm", + "bench": "phpbench run --report=aggregate --ref baseline", + "bench:baseline": "phpbench run --report=aggregate --tag baseline", "build": "./scripts/build", "ci": [ "@fix", @@ -105,8 +108,8 @@ "@test" ], "fix": "php-cs-fixer fix", - "test": "phpunit --stderr", - "test:coverage": "phpunit --stderr --coverage-html=tests/coverage", + "test": "phpunit", + "test:coverage": "XDEBUG_MODE=coverage phpunit --coverage-html=tests/coverage", "zip": "composer archive --format=zip --file=dist" } } diff --git a/kirby/composer.lock b/kirby/composer.lock index 69df2cd..aa048b9 100644 --- a/kirby/composer.lock +++ b/kirby/composer.lock @@ -4,20 +4,79 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "422424ea128b83257e5cc3506bcbe984", + "content-hash": "fa52461c55dc4e276cd5853b57c95c60", "packages": [ { - "name": "claviska/simpleimage", - "version": "4.2.0", + "name": "christian-riesen/base32", + "version": "1.6.0", "source": { "type": "git", - "url": "https://github.com/claviska/SimpleImage.git", - "reference": "dfbe53c01dae8467468ef2b817c09b786a7839d2" + "url": "https://github.com/ChristianRiesen/base32.git", + "reference": "2e82dab3baa008e24a505649b0d583c31d31e894" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/claviska/SimpleImage/zipball/dfbe53c01dae8467468ef2b817c09b786a7839d2", - "reference": "dfbe53c01dae8467468ef2b817c09b786a7839d2", + "url": "https://api.github.com/repos/ChristianRiesen/base32/zipball/2e82dab3baa008e24a505649b0d583c31d31e894", + "reference": "2e82dab3baa008e24a505649b0d583c31d31e894", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.17", + "phpstan/phpstan": "^0.12", + "phpunit/phpunit": "^8.5.13 || ^9.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Base32\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Riesen", + "email": "chris.riesen@gmail.com", + "homepage": "http://christianriesen.com", + "role": "Developer" + } + ], + "description": "Base32 encoder/decoder according to RFC 4648", + "homepage": "https://github.com/ChristianRiesen/base32", + "keywords": [ + "base32", + "decode", + "encode", + "rfc4648" + ], + "support": { + "issues": "https://github.com/ChristianRiesen/base32/issues", + "source": "https://github.com/ChristianRiesen/base32/tree/1.6.0" + }, + "time": "2021-02-26T10:19:33+00:00" + }, + { + "name": "claviska/simpleimage", + "version": "4.2.1", + "source": { + "type": "git", + "url": "https://github.com/claviska/SimpleImage.git", + "reference": "ec6d5021e5a7153a2520d64c59b86b6f3c4157c5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/claviska/SimpleImage/zipball/ec6d5021e5a7153a2520d64c59b86b6f3c4157c5", + "reference": "ec6d5021e5a7153a2520d64c59b86b6f3c4157c5", "shasum": "" }, "require": { @@ -49,7 +108,7 @@ "description": "A PHP class that makes working with images as simple as possible.", "support": { "issues": "https://github.com/claviska/SimpleImage/issues", - "source": "https://github.com/claviska/SimpleImage/tree/4.2.0" + "source": "https://github.com/claviska/SimpleImage/tree/4.2.1" }, "funding": [ { @@ -57,28 +116,28 @@ "type": "github" } ], - "time": "2024-04-15T16:07:16+00:00" + "time": "2024-11-22T13:25:03+00:00" }, { "name": "composer/semver", - "version": "3.4.0", + "version": "3.4.3", "source": { "type": "git", "url": "https://github.com/composer/semver.git", - "reference": "35e8d0af4486141bc745f23a29cc2091eb624a32" + "reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/35e8d0af4486141bc745f23a29cc2091eb624a32", - "reference": "35e8d0af4486141bc745f23a29cc2091eb624a32", + "url": "https://api.github.com/repos/composer/semver/zipball/4313d26ada5e0c4edfbd1dc481a92ff7bff91f12", + "reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12", "shasum": "" }, "require": { "php": "^5.3.2 || ^7.0 || ^8.0" }, "require-dev": { - "phpstan/phpstan": "^1.4", - "symfony/phpunit-bridge": "^4.2 || ^5" + "phpstan/phpstan": "^1.11", + "symfony/phpunit-bridge": "^3 || ^7" }, "type": "library", "extra": { @@ -122,7 +181,7 @@ "support": { "irc": "ircs://irc.libera.chat:6697/composer", "issues": "https://github.com/composer/semver/issues", - "source": "https://github.com/composer/semver/tree/3.4.0" + "source": "https://github.com/composer/semver/tree/3.4.3" }, "funding": [ { @@ -138,30 +197,30 @@ "type": "tidelift" } ], - "time": "2023-08-31T09:50:34+00:00" + "time": "2024-09-19T14:15:21+00:00" }, { "name": "filp/whoops", - "version": "2.15.4", + "version": "2.18.0", "source": { "type": "git", "url": "https://github.com/filp/whoops.git", - "reference": "a139776fa3f5985a50b509f2a02ff0f709d2a546" + "reference": "a7de6c3c6c3c022f5cfc337f8ede6a14460cf77e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filp/whoops/zipball/a139776fa3f5985a50b509f2a02ff0f709d2a546", - "reference": "a139776fa3f5985a50b509f2a02ff0f709d2a546", + "url": "https://api.github.com/repos/filp/whoops/zipball/a7de6c3c6c3c022f5cfc337f8ede6a14460cf77e", + "reference": "a7de6c3c6c3c022f5cfc337f8ede6a14460cf77e", "shasum": "" }, "require": { - "php": "^5.5.9 || ^7.0 || ^8.0", + "php": "^7.1 || ^8.0", "psr/log": "^1.0.1 || ^2.0 || ^3.0" }, "require-dev": { - "mockery/mockery": "^0.9 || ^1.0", - "phpunit/phpunit": "^4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.8 || ^9.3.3", - "symfony/var-dumper": "^2.6 || ^3.0 || ^4.0 || ^5.0" + "mockery/mockery": "^1.0", + "phpunit/phpunit": "^7.5.20 || ^8.5.8 || ^9.3.3", + "symfony/var-dumper": "^4.0 || ^5.0" }, "suggest": { "symfony/var-dumper": "Pretty print complex values better with var-dumper available", @@ -201,7 +260,7 @@ ], "support": { "issues": "https://github.com/filp/whoops/issues", - "source": "https://github.com/filp/whoops/tree/2.15.4" + "source": "https://github.com/filp/whoops/tree/2.18.0" }, "funding": [ { @@ -209,7 +268,7 @@ "type": "github" } ], - "time": "2023-11-03T12:00:00+00:00" + "time": "2025-03-15T12:00:00+00:00" }, { "name": "getkirby/composer-installer", @@ -260,33 +319,32 @@ }, { "name": "laminas/laminas-escaper", - "version": "2.13.0", + "version": "2.16.0", "source": { "type": "git", "url": "https://github.com/laminas/laminas-escaper.git", - "reference": "af459883f4018d0f8a0c69c7a209daef3bf973ba" + "reference": "9cf1f5317ca65b4fd5c6a3c2855e24a187b288c8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-escaper/zipball/af459883f4018d0f8a0c69c7a209daef3bf973ba", - "reference": "af459883f4018d0f8a0c69c7a209daef3bf973ba", + "url": "https://api.github.com/repos/laminas/laminas-escaper/zipball/9cf1f5317ca65b4fd5c6a3c2855e24a187b288c8", + "reference": "9cf1f5317ca65b4fd5c6a3c2855e24a187b288c8", "shasum": "" }, "require": { "ext-ctype": "*", "ext-mbstring": "*", - "php": "~8.1.0 || ~8.2.0 || ~8.3.0" + "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0" }, "conflict": { "zendframework/zend-escaper": "*" }, "require-dev": { - "infection/infection": "^0.27.0", - "laminas/laminas-coding-standard": "~2.5.0", - "maglnet/composer-require-checker": "^3.8.0", - "phpunit/phpunit": "^9.6.7", - "psalm/plugin-phpunit": "^0.18.4", - "vimeo/psalm": "^5.9" + "infection/infection": "^0.29.8", + "laminas/laminas-coding-standard": "~3.0.1", + "phpunit/phpunit": "^10.5.45", + "psalm/plugin-phpunit": "^0.19.2", + "vimeo/psalm": "^6.6.2" }, "type": "library", "autoload": { @@ -318,7 +376,7 @@ "type": "community_bridge" } ], - "time": "2023-10-10T08:35:13+00:00" + "time": "2025-02-17T12:40:19+00:00" }, { "name": "league/color-extractor", @@ -437,16 +495,16 @@ }, { "name": "phpmailer/phpmailer", - "version": "v6.9.1", + "version": "v6.9.3", "source": { "type": "git", "url": "https://github.com/PHPMailer/PHPMailer.git", - "reference": "039de174cd9c17a8389754d3b877a2ed22743e18" + "reference": "2f5c94fe7493efc213f643c23b1b1c249d40f47e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/039de174cd9c17a8389754d3b877a2ed22743e18", - "reference": "039de174cd9c17a8389754d3b877a2ed22743e18", + "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/2f5c94fe7493efc213f643c23b1b1c249d40f47e", + "reference": "2f5c94fe7493efc213f643c23b1b1c249d40f47e", "shasum": "" }, "require": { @@ -506,7 +564,7 @@ "description": "PHPMailer is a full-featured email creation and transfer class for PHP", "support": { "issues": "https://github.com/PHPMailer/PHPMailer/issues", - "source": "https://github.com/PHPMailer/PHPMailer/tree/v6.9.1" + "source": "https://github.com/PHPMailer/PHPMailer/tree/v6.9.3" }, "funding": [ { @@ -514,20 +572,20 @@ "type": "github" } ], - "time": "2023-11-25T22:23:28+00:00" + "time": "2024-11-24T18:04:13+00:00" }, { "name": "psr/log", - "version": "3.0.1", + "version": "3.0.2", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", - "reference": "79dff0b268932c640297f5208d6298f71855c03e" + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/79dff0b268932c640297f5208d6298f71855c03e", - "reference": "79dff0b268932c640297f5208d6298f71855c03e", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", "shasum": "" }, "require": { @@ -562,22 +620,22 @@ "psr-3" ], "support": { - "source": "https://github.com/php-fig/log/tree/3.0.1" + "source": "https://github.com/php-fig/log/tree/3.0.2" }, - "time": "2024-08-21T13:31:24+00:00" + "time": "2024-09-11T13:17:53+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "v3.5.0", + "version": "v3.5.1", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1" + "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", - "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", + "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", "shasum": "" }, "require": { @@ -585,12 +643,12 @@ }, "type": "library", "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, "branch-alias": { "dev-main": "3.5-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" } }, "autoload": { @@ -615,7 +673,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.0" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.1" }, "funding": [ { @@ -631,24 +689,24 @@ "type": "tidelift" } ], - "time": "2024-04-18T09:32:20+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.30.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "0424dff1c58f028c451efff2045f5d92410bd540" + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/0424dff1c58f028c451efff2045f5d92410bd540", - "reference": "0424dff1c58f028c451efff2045f5d92410bd540", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "provide": { "ext-ctype": "*" @@ -659,8 +717,8 @@ "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -694,7 +752,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.31.0" }, "funding": [ { @@ -710,26 +768,25 @@ "type": "tidelift" } ], - "time": "2024-05-31T15:07:36+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-intl-idn", - "version": "v1.30.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-idn.git", - "reference": "a6e83bdeb3c84391d1dfe16f42e40727ce524a5c" + "reference": "c36586dcf89a12315939e00ec9b4474adcb1d773" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/a6e83bdeb3c84391d1dfe16f42e40727ce524a5c", - "reference": "a6e83bdeb3c84391d1dfe16f42e40727ce524a5c", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/c36586dcf89a12315939e00ec9b4474adcb1d773", + "reference": "c36586dcf89a12315939e00ec9b4474adcb1d773", "shasum": "" }, "require": { - "php": ">=7.1", - "symfony/polyfill-intl-normalizer": "^1.10", - "symfony/polyfill-php72": "^1.10" + "php": ">=7.2", + "symfony/polyfill-intl-normalizer": "^1.10" }, "suggest": { "ext-intl": "For best performance" @@ -737,8 +794,8 @@ "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -778,7 +835,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.31.0" }, "funding": [ { @@ -794,24 +851,24 @@ "type": "tidelift" } ], - "time": "2024-05-31T15:07:36+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.30.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "a95281b0be0d9ab48050ebd988b967875cdb9fdb" + "reference": "3833d7255cc303546435cb650316bff708a1c75c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/a95281b0be0d9ab48050ebd988b967875cdb9fdb", - "reference": "a95281b0be0d9ab48050ebd988b967875cdb9fdb", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", + "reference": "3833d7255cc303546435cb650316bff708a1c75c", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "suggest": { "ext-intl": "For best performance" @@ -819,8 +876,8 @@ "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -859,7 +916,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.31.0" }, "funding": [ { @@ -875,24 +932,24 @@ "type": "tidelift" } ], - "time": "2024-05-31T15:07:36+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.30.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c" + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fd22ab50000ef01661e2a31d850ebaa297f8e03c", - "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341", + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "provide": { "ext-mbstring": "*" @@ -903,8 +960,8 @@ "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -939,7 +996,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.0" }, "funding": [ { @@ -955,20 +1012,20 @@ "type": "tidelift" } ], - "time": "2024-06-19T12:30:46+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/yaml", - "version": "v6.4.8", + "version": "v6.4.18", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "52903de178d542850f6f341ba92995d3d63e60c9" + "reference": "bf598c9d9bb4a22f495a4e26e4c4fce2f8ecefc5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/52903de178d542850f6f341ba92995d3d63e60c9", - "reference": "52903de178d542850f6f341ba92995d3d63e60c9", + "url": "https://api.github.com/repos/symfony/yaml/zipball/bf598c9d9bb4a22f495a4e26e4c4fce2f8ecefc5", + "reference": "bf598c9d9bb4a22f495a4e26e4c4fce2f8ecefc5", "shasum": "" }, "require": { @@ -1011,7 +1068,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v6.4.8" + "source": "https://github.com/symfony/yaml/tree/v6.4.18" }, "funding": [ { @@ -1027,17 +1084,17 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:49:08+00:00" + "time": "2025-01-07T09:44:41+00:00" } ], "packages-dev": [], "aliases": [], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": {}, "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": "~8.1.0 || ~8.2.0 || ~8.3.0", + "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0", "ext-simplexml": "*", "ext-ctype": "*", "ext-curl": "*", @@ -1050,7 +1107,7 @@ "ext-mbstring": "*", "ext-openssl": "*" }, - "platform-dev": [], + "platform-dev": {}, "platform-overrides": { "php": "8.1.0" }, diff --git a/kirby/config/aliases.php b/kirby/config/aliases.php index cadd7d0..206d3b4 100644 --- a/kirby/config/aliases.php +++ b/kirby/config/aliases.php @@ -3,7 +3,6 @@ return [ // cms classes 'collection' => 'Kirby\Cms\Collection', - 'field' => 'Kirby\Cms\Field', 'file' => 'Kirby\Cms\File', 'files' => 'Kirby\Cms\Files', 'find' => 'Kirby\Cms\Find', @@ -24,6 +23,9 @@ return [ 'users' => 'Kirby\Cms\Users', 'visitor' => 'Kirby\Cms\Visitor', + // content classes + 'field' => 'Kirby\Content\Field', + // data handler 'data' => 'Kirby\Data\Data', 'json' => 'Kirby\Data\Json', @@ -69,17 +71,25 @@ return [ 'v' => 'Kirby\Toolkit\V', 'xml' => 'Kirby\Toolkit\Xml', - // TODO: remove in 4.0.0 - 'kirby\cms\asset' => 'Kirby\Filesystem\Asset', - 'kirby\cms\dir' => 'Kirby\Filesystem\Dir', - 'kirby\cms\filename' => 'Kirby\Filesystem\Filename', - 'kirby\cms\filefoundation' => 'Kirby\Filesystem\IsFile', - 'kirby\cms\form' => 'Kirby\Form\Form', - 'kirby\cms\kirbytag' => 'Kirby\Text\KirbyTag', - 'kirby\cms\kirbytags' => 'Kirby\Text\KirbyTags', - 'kirby\cms\template' => 'Kirby\Template\Template', - 'kirby\toolkit\dir' => 'Kirby\Filesystem\Dir', - 'kirby\toolkit\f' => 'Kirby\Filesystem\F', - 'kirby\toolkit\file' => 'Kirby\Filesystem\File', - 'kirby\toolkit\mime' => 'Kirby\Filesystem\Mime', + // Deprecated aliases: + // Any of these might be removed at any point in the future + 'kirby\cms\asset' => 'Kirby\Filesystem\Asset', + 'kirby\cms\content' => 'Kirby\Content\Content', + 'kirby\cms\contenttranslation' => 'Kirby\Content\ContentTranslation', + 'kirby\cms\dir' => 'Kirby\Filesystem\Dir', + 'kirby\cms\filename' => 'Kirby\Filesystem\Filename', + 'kirby\cms\filefoundation' => 'Kirby\Filesystem\IsFile', + 'kirby\cms\field' => 'Kirby\Content\Field', + 'kirby\cms\form' => 'Kirby\Form\Form', + 'kirby\cms\kirbytag' => 'Kirby\Text\KirbyTag', + 'kirby\cms\kirbytags' => 'Kirby\Text\KirbyTags', + 'kirby\cms\template' => 'Kirby\Template\Template', + 'kirby\form\options' => 'Kirby\Option\Options', + 'kirby\form\optionsapi' => 'Kirby\Option\OptionsApi', + 'kirby\form\optionsquery' => 'Kirby\Option\OptionsQuery', + 'kirby\toolkit\dir' => 'Kirby\Filesystem\Dir', + 'kirby\toolkit\f' => 'Kirby\Filesystem\F', + 'kirby\toolkit\file' => 'Kirby\Filesystem\File', + 'kirby\toolkit\mime' => 'Kirby\Filesystem\Mime', + 'kirby\toolkit\query' => 'Kirby\Query\Query', ]; diff --git a/kirby/config/api/authentication.php b/kirby/config/api/authentication.php index 4758599..15f3bbd 100644 --- a/kirby/config/api/authentication.php +++ b/kirby/config/api/authentication.php @@ -1,6 +1,6 @@ kirby()->auth(); @@ -11,17 +11,17 @@ return function () { $auth->type($allowImpersonation) === 'session' && $auth->csrf() === false ) { - throw new PermissionException('Unauthenticated'); + throw new AuthException('Unauthenticated'); } // get user from session or basic auth if ($user = $auth->user(null, $allowImpersonation)) { if ($user->role()->permissions()->for('access', 'panel') === false) { - throw new PermissionException(['key' => 'access.panel']); + throw new AuthException(['key' => 'access.panel']); } return $user; } - throw new PermissionException('Unauthenticated'); + throw new AuthException('Unauthenticated'); }; diff --git a/kirby/config/api/collections.php b/kirby/config/api/collections.php index 3a34927..97b2180 100644 --- a/kirby/config/api/collections.php +++ b/kirby/config/api/collections.php @@ -3,6 +3,14 @@ /** * Api Collection Definitions */ + +use Kirby\Cms\Files; +use Kirby\Cms\Languages; +use Kirby\Cms\Pages; +use Kirby\Cms\Roles; +use Kirby\Cms\Translations; +use Kirby\Cms\Users; + return [ /** @@ -10,7 +18,7 @@ return [ */ 'children' => [ 'model' => 'page', - 'type' => 'Kirby\Cms\Pages', + 'type' => Pages::class, 'view' => 'compact' ], @@ -19,7 +27,7 @@ return [ */ 'files' => [ 'model' => 'file', - 'type' => 'Kirby\Cms\Files' + 'type' => Files::class, ], /** @@ -27,7 +35,7 @@ return [ */ 'languages' => [ 'model' => 'language', - 'type' => 'Kirby\Cms\Languages' + 'type' => Languages::class, ], /** @@ -35,7 +43,7 @@ return [ */ 'pages' => [ 'model' => 'page', - 'type' => 'Kirby\Cms\Pages', + 'type' => Pages::class, 'view' => 'compact' ], @@ -44,7 +52,7 @@ return [ */ 'roles' => [ 'model' => 'role', - 'type' => 'Kirby\Cms\Roles', + 'type' => Roles::class, 'view' => 'compact' ], @@ -53,7 +61,7 @@ return [ */ 'translations' => [ 'model' => 'translation', - 'type' => 'Kirby\Cms\Translations', + 'type' => Translations::class, 'view' => 'compact' ], @@ -63,7 +71,7 @@ return [ 'users' => [ 'default' => fn () => $this->users(), 'model' => 'user', - 'type' => 'Kirby\Cms\Users', + 'type' => Users::class, 'view' => 'compact' ] diff --git a/kirby/config/api/models.php b/kirby/config/api/models.php index 075442f..9e6fc18 100644 --- a/kirby/config/api/models.php +++ b/kirby/config/api/models.php @@ -8,6 +8,7 @@ return [ 'FileBlueprint' => include __DIR__ . '/models/FileBlueprint.php', 'FileVersion' => include __DIR__ . '/models/FileVersion.php', 'Language' => include __DIR__ . '/models/Language.php', + 'License' => include __DIR__ . '/models/License.php', 'Page' => include __DIR__ . '/models/Page.php', 'PageBlueprint' => include __DIR__ . '/models/PageBlueprint.php', 'Role' => include __DIR__ . '/models/Role.php', diff --git a/kirby/config/api/models/File.php b/kirby/config/api/models/File.php index 4e9a3a0..b70919a 100644 --- a/kirby/config/api/models/File.php +++ b/kirby/config/api/models/File.php @@ -59,7 +59,7 @@ return [ 'url' => fn (File $file) => $file->url(), 'uuid' => fn (File $file) => $file->uuid()?->toString() ], - 'type' => 'Kirby\Cms\File', + 'type' => File::class, 'views' => [ 'default' => [ 'content', diff --git a/kirby/config/api/models/FileBlueprint.php b/kirby/config/api/models/FileBlueprint.php index e781e45..f255c14 100644 --- a/kirby/config/api/models/FileBlueprint.php +++ b/kirby/config/api/models/FileBlueprint.php @@ -12,7 +12,6 @@ return [ 'tabs' => fn (FileBlueprint $blueprint) => $blueprint->tabs(), 'title' => fn (FileBlueprint $blueprint) => $blueprint->title(), ], - 'type' => 'Kirby\Cms\FileBlueprint', - 'views' => [ - ], + 'type' => FileBlueprint::class, + 'views' => [], ]; diff --git a/kirby/config/api/models/FileVersion.php b/kirby/config/api/models/FileVersion.php index df2aac0..063e10b 100644 --- a/kirby/config/api/models/FileVersion.php +++ b/kirby/config/api/models/FileVersion.php @@ -20,7 +20,7 @@ return [ 'type' => fn (FileVersion $file) => $file->type(), 'url' => fn (FileVersion $file) => $file->url(), ], - 'type' => 'Kirby\Cms\FileVersion', + 'type' => FileVersion::class, 'views' => [ 'default' => [ 'dimensions', diff --git a/kirby/config/api/models/Language.php b/kirby/config/api/models/Language.php index 362d6f5..fcebad1 100644 --- a/kirby/config/api/models/Language.php +++ b/kirby/config/api/models/Language.php @@ -15,7 +15,7 @@ return [ 'rules' => fn (Language $language) => $language->rules(), 'url' => fn (Language $language) => $language->url(), ], - 'type' => 'Kirby\Cms\Language', + 'type' => Language::class, 'views' => [ 'default' => [ 'code', diff --git a/kirby/config/api/models/License.php b/kirby/config/api/models/License.php new file mode 100644 index 0000000..3a56556 --- /dev/null +++ b/kirby/config/api/models/License.php @@ -0,0 +1,17 @@ + [ + 'status' => fn (License $license) => $license->status()->value(), + 'code' => function (License $license) { + return $this->kirby()->user()->isAdmin() ? $license->code() : $license->code(true); + }, + 'type' => fn (License $license) => $license->type()->label(), + ], + 'type' => License::class, +]; diff --git a/kirby/config/api/models/Page.php b/kirby/config/api/models/Page.php index 8941c00..4ff6dbc 100644 --- a/kirby/config/api/models/Page.php +++ b/kirby/config/api/models/Page.php @@ -40,7 +40,7 @@ return [ 'url' => fn (Page $page) => $page->url(), 'uuid' => fn (Page $page) => $page->uuid()?->toString() ], - 'type' => 'Kirby\Cms\Page', + 'type' => Page::class, 'views' => [ 'compact' => [ 'id', diff --git a/kirby/config/api/models/PageBlueprint.php b/kirby/config/api/models/PageBlueprint.php index dc240ed..e993b91 100644 --- a/kirby/config/api/models/PageBlueprint.php +++ b/kirby/config/api/models/PageBlueprint.php @@ -15,7 +15,6 @@ return [ 'tabs' => fn (PageBlueprint $blueprint) => $blueprint->tabs(), 'title' => fn (PageBlueprint $blueprint) => $blueprint->title(), ], - 'type' => 'Kirby\Cms\PageBlueprint', - 'views' => [ - ], + 'type' => PageBlueprint::class, + 'views' => [], ]; diff --git a/kirby/config/api/models/Role.php b/kirby/config/api/models/Role.php index e1d27c3..3c2b468 100644 --- a/kirby/config/api/models/Role.php +++ b/kirby/config/api/models/Role.php @@ -12,7 +12,7 @@ return [ 'permissions' => fn (Role $role) => $role->permissions()->toArray(), 'title' => fn (Role $role) => $role->title(), ], - 'type' => 'Kirby\Cms\Role', + 'type' => Role::class, 'views' => [ 'compact' => [ 'description', diff --git a/kirby/config/api/models/Site.php b/kirby/config/api/models/Site.php index c312ef4..81cab62 100644 --- a/kirby/config/api/models/Site.php +++ b/kirby/config/api/models/Site.php @@ -19,7 +19,7 @@ return [ 'title' => fn (Site $site) => $site->title()->value(), 'url' => fn (Site $site) => $site->url(), ], - 'type' => 'Kirby\Cms\Site', + 'type' => Site::class, 'views' => [ 'compact' => [ 'title', diff --git a/kirby/config/api/models/SiteBlueprint.php b/kirby/config/api/models/SiteBlueprint.php index 7841841..69aad5b 100644 --- a/kirby/config/api/models/SiteBlueprint.php +++ b/kirby/config/api/models/SiteBlueprint.php @@ -12,6 +12,6 @@ return [ 'tabs' => fn (SiteBlueprint $blueprint) => $blueprint->tabs(), 'title' => fn (SiteBlueprint $blueprint) => $blueprint->title(), ], - 'type' => 'Kirby\Cms\SiteBlueprint', + 'type' => SiteBlueprint::class, 'views' => [], ]; diff --git a/kirby/config/api/models/System.php b/kirby/config/api/models/System.php index 725d3ed..4627226 100644 --- a/kirby/config/api/models/System.php +++ b/kirby/config/api/models/System.php @@ -49,7 +49,7 @@ return [ return null; } ], - 'type' => 'Kirby\Cms\System', + 'type' => System::class, 'views' => [ 'login' => [ 'authStatus', diff --git a/kirby/config/api/models/Translation.php b/kirby/config/api/models/Translation.php index faf51e7..94f9c02 100644 --- a/kirby/config/api/models/Translation.php +++ b/kirby/config/api/models/Translation.php @@ -13,7 +13,7 @@ return [ 'id' => fn (Translation $translation) => $translation->id(), 'name' => fn (Translation $translation) => $translation->name(), ], - 'type' => 'Kirby\Cms\Translation', + 'type' => Translation::class, 'views' => [ 'compact' => [ 'direction', diff --git a/kirby/config/api/models/User.php b/kirby/config/api/models/User.php index 89e20dd..0da32e9 100644 --- a/kirby/config/api/models/User.php +++ b/kirby/config/api/models/User.php @@ -27,7 +27,7 @@ return [ 'username' => fn (User $user) => $user->username(), 'uuid' => fn (User $user) => $user->uuid()?->toString() ], - 'type' => 'Kirby\Cms\User', + 'type' => User::class, 'views' => [ 'default' => [ 'avatar', diff --git a/kirby/config/api/models/UserBlueprint.php b/kirby/config/api/models/UserBlueprint.php index 099a177..2fa83e9 100644 --- a/kirby/config/api/models/UserBlueprint.php +++ b/kirby/config/api/models/UserBlueprint.php @@ -12,7 +12,6 @@ return [ 'tabs' => fn (UserBlueprint $blueprint) => $blueprint->tabs(), 'title' => fn (UserBlueprint $blueprint) => $blueprint->title(), ], - 'type' => 'Kirby\Cms\UserBlueprint', - 'views' => [ - ], + 'type' => UserBlueprint::class, + 'views' => [], ]; diff --git a/kirby/config/api/routes.php b/kirby/config/api/routes.php index 749fbad..aca9e79 100644 --- a/kirby/config/api/routes.php +++ b/kirby/config/api/routes.php @@ -19,7 +19,10 @@ return function ($kirby) { // only add the language routes if the // multi language setup is activated if ($kirby->option('languages', false) !== false) { - $routes = array_merge($routes, include __DIR__ . '/routes/languages.php'); + $routes = array_merge( + $routes, + include __DIR__ . '/routes/languages.php' + ); } return $routes; diff --git a/kirby/config/api/routes/files.php b/kirby/config/api/routes/files.php index 78d46da..23e1729 100644 --- a/kirby/config/api/routes/files.php +++ b/kirby/config/api/routes/files.php @@ -1,63 +1,70 @@ $pattern . '/files/(:any)/sections/(:any)', - 'method' => 'GET', - 'action' => function (string $path, string $filename, string $sectionName) { - return $this->file($path, $filename)->blueprint()->section($sectionName)?->toResponse(); - } - ], - [ - 'pattern' => $pattern . '/files/(:any)/fields/(:any)/(:all?)', + 'pattern' => $filePattern . '/fields/(:any)/(:all?)', 'method' => 'ALL', - 'action' => function (string $parent, string $filename, string $fieldName, string $path = null) { + 'action' => function (string $parent, string $filename, string $fieldName, string|null $path = null) { if ($file = $this->file($parent, $filename)) { return $this->fieldApi($file, $fieldName, $path); } } ], [ - 'pattern' => $pattern . '/files', + 'pattern' => $filePattern . '/sections/(:any)', 'method' => 'GET', - 'action' => function (string $path) { - return $this->parent($path)->files()->sorted(); + 'action' => function (string $path, string $filename, string $sectionName) { + return $this->file($path, $filename)->blueprint()->section($sectionName)?->toResponse(); } ], [ - 'pattern' => $pattern . '/files', + 'pattern' => $filePattern . '/sections/(:any)/(:all?)', + 'method' => 'ALL', + 'action' => function (string $parent, string $filename, string $sectionName, string|null $path = null) { + if ($file = $this->file($parent, $filename)) { + return $this->sectionApi($file, $sectionName, $path); + } + } + ], + [ + 'pattern' => $parentPattern, + 'method' => 'GET', + 'action' => function (string $path) { + return $this->files($path)->sorted(); + } + ], + [ + 'pattern' => $parentPattern, 'method' => 'POST', 'action' => function (string $path) { // move_uploaded_file() not working with unit test // @codeCoverageIgnoreStart return $this->upload(function ($source, $filename) use ($path) { - $props = [ + // move the source file from the temp dir + return $this->parent($path)->createFile([ 'content' => [ 'sort' => $this->requestBody('sort') ], 'source' => $source, 'template' => $this->requestBody('template'), 'filename' => $filename - ]; - - // move the source file from the temp dir - return $this->parent($path)->createFile($props, true); + ], true); }); // @codeCoverageIgnoreEnd } ], [ - 'pattern' => $pattern . '/files/search', + 'pattern' => $parentPattern . '/search', 'method' => 'GET|POST', 'action' => function (string $path) { - $files = $this->parent($path)->files(); + $files = $this->files($path); if ($this->requestMethod() === 'GET') { return $files->search($this->requestQuery('q')); @@ -67,24 +74,24 @@ return [ } ], [ - 'pattern' => $pattern . '/files/sort', + 'pattern' => $parentPattern . '/sort', 'method' => 'PATCH', 'action' => function (string $path) { - return $this->parent($path)->files()->changeSort( + return $this->files($path)->changeSort( $this->requestBody('files'), $this->requestBody('index') ); } ], [ - 'pattern' => $pattern . '/files/(:any)', + 'pattern' => $filePattern, 'method' => 'GET', 'action' => function (string $path, string $filename) { return $this->file($path, $filename); } ], [ - 'pattern' => $pattern . '/files/(:any)', + 'pattern' => $filePattern, 'method' => 'PATCH', 'action' => function (string $path, string $filename) { return $this->file($path, $filename)->update( @@ -95,7 +102,7 @@ return [ } ], [ - 'pattern' => $pattern . '/files/(:any)', + 'pattern' => $filePattern, 'method' => 'POST', 'action' => function (string $path, string $filename) { // move the source file from the temp dir @@ -105,28 +112,29 @@ return [ } ], [ - 'pattern' => $pattern . '/files/(:any)', + 'pattern' => $filePattern, 'method' => 'DELETE', 'action' => function (string $path, string $filename) { return $this->file($path, $filename)->delete(); } ], [ - 'pattern' => $pattern . '/files/(:any)/name', + 'pattern' => $filePattern . '/name', 'method' => 'PATCH', 'action' => function (string $path, string $filename) { return $this->file($path, $filename)->changeName($this->requestBody('name')); } ], [ - 'pattern' => 'files/search', + 'pattern' => $parentPattern . '/search', 'method' => 'GET|POST', 'action' => function () { $files = $this ->site() ->index(true) - ->filter('isReadable', true) - ->files(); + ->filter('isListable', true) + ->files() + ->filter('isListable', true); if ($this->requestMethod() === 'GET') { return $files->search($this->requestQuery('q')); diff --git a/kirby/config/api/routes/kql.php b/kirby/config/api/routes/kql.php new file mode 100644 index 0000000..68710ec --- /dev/null +++ b/kirby/config/api/routes/kql.php @@ -0,0 +1,35 @@ + function ($kirby) { + return [ + [ + 'pattern' => 'query', + 'method' => 'POST|GET', + 'auth' => $kirby->option('kql.auth') !== false, + 'action' => function () use ($kirby) { + $kql = '\Kirby\Kql\Kql'; + + if (class_exists($kql) === false) { + return [ + 'code' => 500, + 'status' => 'error', + 'message' => 'KQL plugin is not installed', + ]; + } + + $input = $kirby->request()->get(); + $result = $kql::run($input); + + return [ + 'code' => 200, + 'result' => $result, + 'status' => 'ok', + ]; + } + ] + ]; + } +]; +// @codeCoverageIgnoreEnd diff --git a/kirby/config/api/routes/pages.php b/kirby/config/api/routes/pages.php index 476b1ef..6de9c1a 100644 --- a/kirby/config/api/routes/pages.php +++ b/kirby/config/api/routes/pages.php @@ -5,7 +5,7 @@ * Page Routes */ return [ - + // @codeCoverageIgnoreStart [ 'pattern' => 'pages/(:any)', 'method' => 'GET', @@ -100,6 +100,15 @@ return [ return $this->page($id)->changeTitle($this->requestBody('title')); } ], + [ + 'pattern' => 'pages/(:any)/fields/(:any)/(:all?)', + 'method' => 'ALL', + 'action' => function (string $id, string $fieldName, string|null $path = null) { + if ($page = $this->page($id)) { + return $this->fieldApi($page, $fieldName, $path); + } + } + ], [ 'pattern' => 'pages/(:any)/sections/(:any)', 'method' => 'GET', @@ -108,12 +117,13 @@ return [ } ], [ - 'pattern' => 'pages/(:any)/fields/(:any)/(:all?)', + 'pattern' => 'pages/(:any)/sections/(:any)/(:all?)', 'method' => 'ALL', - 'action' => function (string $id, string $fieldName, string $path = null) { + 'action' => function (string $id, string $sectionName, string|null $path = null) { if ($page = $this->page($id)) { - return $this->fieldApi($page, $fieldName, $path); + return $this->sectionApi($page, $sectionName, $path); } } ], + // @codeCoverageIgnoreEnd ]; diff --git a/kirby/config/api/routes/site.php b/kirby/config/api/routes/site.php index afad5bd..abc2ac5 100644 --- a/kirby/config/api/routes/site.php +++ b/kirby/config/api/routes/site.php @@ -5,7 +5,7 @@ * Site Routes */ return [ - + // @codeCoverageIgnoreStart [ 'pattern' => 'site', 'action' => function () { @@ -75,7 +75,7 @@ return [ $pages = $this ->site() ->index(true) - ->filter('isReadable', true); + ->filter('isListable', true); if ($this->requestMethod() === 'GET') { return $pages->search($this->requestQuery('q')); @@ -84,6 +84,13 @@ return [ return $pages->query($this->requestBody()); } ], + [ + 'pattern' => 'site/fields/(:any)/(:all?)', + 'method' => 'ALL', + 'action' => function (string $fieldName, string|null $path = null) { + return $this->fieldApi($this->site(), $fieldName, $path); + } + ], [ 'pattern' => 'site/sections/(:any)', 'method' => 'GET', @@ -92,11 +99,11 @@ return [ } ], [ - 'pattern' => 'site/fields/(:any)/(:all?)', + 'pattern' => 'site/sections/(:any)/(:all?)', 'method' => 'ALL', - 'action' => function (string $fieldName, string $path = null) { - return $this->fieldApi($this->site(), $fieldName, $path); + 'action' => function (string $sectionName, string|null $path = null) { + return $this->sectionApi($this->site(), $sectionName, $path); } - ] - + ], + // @codeCoverageIgnoreEnd ]; diff --git a/kirby/config/api/routes/system.php b/kirby/config/api/routes/system.php index 103e826..e7e49b4 100644 --- a/kirby/config/api/routes/system.php +++ b/kirby/config/api/routes/system.php @@ -31,6 +31,18 @@ return [ ]; } ], + [ + 'pattern' => 'system/method-test', + 'method' => 'PATCH', + 'action' => function () { + return [ + 'status' => match ($this->kirby()->request()->method()) { + 'PATCH' => 'ok', + default => 'fail' + } + ]; + } + ], [ 'pattern' => 'system/register', 'method' => 'POST', diff --git a/kirby/config/api/routes/users.php b/kirby/config/api/routes/users.php index d5ff3c5..203e2d2 100644 --- a/kirby/config/api/routes/users.php +++ b/kirby/config/api/routes/users.php @@ -8,6 +8,7 @@ use Kirby\Toolkit\Str; * User Routes */ return [ + // @codeCoverageIgnoreStart [ 'pattern' => 'users', 'method' => 'GET', @@ -202,7 +203,19 @@ return [ 'users/(:any)/roles', ], 'action' => function (string $id) { - return $this->user($id)->roles(); + $kirby = $this->kirby(); + $purpose = $kirby->request()->get('purpose'); + return $this->user($id)->roles($purpose); + } + ], + [ + 'pattern' => [ + '(account)/fields/(:any)/(:all?)', + 'users/(:any)/fields/(:any)/(:all?)', + ], + 'method' => 'ALL', + 'action' => function (string $id, string $fieldName, string|null $path = null) { + return $this->fieldApi($this->user($id), $fieldName, $path); } ], [ @@ -219,12 +232,13 @@ return [ ], [ 'pattern' => [ - '(account)/fields/(:any)/(:all?)', - 'users/(:any)/fields/(:any)/(:all?)', + '(account)/sections/(:any)/(:all?)', + 'users/(:any)/sections/(:any)/(:all?)', ], 'method' => 'ALL', - 'action' => function (string $id, string $fieldName, string $path = null) { - return $this->fieldApi($this->user($id), $fieldName, $path); + 'action' => function (string $id, string $sectionName, string|null $path = null) { + return $this->sectionApi($this->user($id), $sectionName, $path); } ], + // @codeCoverageIgnoreEnd ]; diff --git a/kirby/config/areas/account.php b/kirby/config/areas/account.php index 738a839..e138cab 100644 --- a/kirby/config/areas/account.php +++ b/kirby/config/areas/account.php @@ -8,6 +8,7 @@ return function () { 'label' => I18n::translate('view.account'), 'search' => 'users', 'dialogs' => require __DIR__ . '/account/dialogs.php', + 'drawers' => require __DIR__ . '/account/drawers.php', 'dropdowns' => require __DIR__ . '/account/dropdowns.php', 'views' => require __DIR__ . '/account/views.php' ]; diff --git a/kirby/config/areas/account/dialogs.php b/kirby/config/areas/account/dialogs.php index 7c13062..93635ce 100644 --- a/kirby/config/areas/account/dialogs.php +++ b/kirby/config/areas/account/dialogs.php @@ -1,5 +1,7 @@ $dialogs['user.delete']['submit'], ], + // account fields dialogs + 'account.fields' => [ + 'pattern' => '(account)/files/(:any)/fields/(:any)/(:all?)', + 'load' => $dialogs['user.fields']['load'], + 'submit' => $dialogs['user.fields']['submit'] + ], + // change file name 'account.file.changeName' => [ 'pattern' => '(account)/files/(:any)/changeName', @@ -60,6 +69,13 @@ return [ 'submit' => $dialogs['user.file.changeSort']['submit'], ], + // change file template + 'account.file.changeTemplate' => [ + 'pattern' => '(account)/files/(:any)/changeTemplate', + 'load' => $dialogs['user.file.changeTemplate']['load'], + 'submit' => $dialogs['user.file.changeTemplate']['submit'], + ], + // delete 'account.file.delete' => [ 'pattern' => '(account)/files/(:any)/delete', @@ -67,4 +83,24 @@ return [ 'submit' => $dialogs['user.file.delete']['submit'], ], + // account file fields dialogs + 'account.file.fields' => [ + 'pattern' => '(account)/files/(:any)/fields/(:any)/(:all?)', + 'load' => $dialogs['user.file.fields']['load'], + 'submit' => $dialogs['user.file.fields']['submit'] + ], + + // account enable TOTP + 'account.totp.enable' => [ + 'pattern' => '(account)/totp/enable', + 'load' => fn () => (new UserTotpEnableDialog())->load(), + 'submit' => fn () => (new UserTotpEnableDialog())->submit() + ], + + // account disable TOTP + 'account.totp.disable' => [ + 'pattern' => '(account)/totp/disable', + 'load' => $dialogs['user.totp.disable']['load'], + 'submit' => $dialogs['user.totp.disable']['submit'] + ], ]; diff --git a/kirby/config/areas/account/drawers.php b/kirby/config/areas/account/drawers.php new file mode 100644 index 0000000..01bb0b6 --- /dev/null +++ b/kirby/config/areas/account/drawers.php @@ -0,0 +1,19 @@ + [ + 'pattern' => '(account)/files/(:any)/fields/(:any)/(:all?)', + 'load' => $drawers['user.fields']['load'], + 'submit' => $drawers['user.fields']['submit'] + ], + + // account file fields drawers + 'account.file.fields' => [ + 'pattern' => '(account)/files/(:any)/fields/(:any)/(:all?)', + 'load' => $drawers['user.file.fields']['load'], + 'submit' => $drawers['user.file.fields']['submit'] + ], +]; diff --git a/kirby/config/areas/account/views.php b/kirby/config/areas/account/views.php index 264edf9..933e0a4 100644 --- a/kirby/config/areas/account/views.php +++ b/kirby/config/areas/account/views.php @@ -2,6 +2,7 @@ use Kirby\Cms\App; use Kirby\Cms\Find; +use Kirby\Toolkit\I18n; return [ 'account' => [ @@ -19,6 +20,13 @@ return [ ], 'account.password' => [ 'pattern' => 'reset-password', - 'action' => fn () => ['component' => 'k-reset-password-view'] + 'action' => fn () => [ + 'component' => 'k-reset-password-view', + 'breadcrumb' => [ + [ + 'label' => I18n::translate('view.resetPassword') + ] + ] + ] ] ]; diff --git a/kirby/config/areas/fields/dialogs.php b/kirby/config/areas/fields/dialogs.php new file mode 100644 index 0000000..ca14e08 --- /dev/null +++ b/kirby/config/areas/fields/dialogs.php @@ -0,0 +1,61 @@ + [ + 'load' => function ( + string $modelPath, + string $fieldName, + string|null $path = null + ) { + return Field::dialog( + model: Find::parent($modelPath), + fieldName: $fieldName, + path: $path, + method: 'GET' + ); + }, + 'submit' => function ( + string $modelPath, + string $fieldName, + string|null $path = null + ) { + return Field::dialog( + model: Find::parent($modelPath), + fieldName: $fieldName, + path: $path, + method: 'POST' + ); + } + ], + 'file' => [ + 'load' => function ( + string $modelPath, + string $filename, + string $fieldName, + string|null $path = null + ) { + return Field::dialog( + model: Find::file($modelPath, $filename), + fieldName: $fieldName, + path: $path, + method: 'GET' + ); + }, + 'submit' => function ( + string $modelPath, + string $filename, + string $fieldName, + string|null $path = null + ) { + return Field::dialog( + model: Find::file($modelPath, $filename), + fieldName: $fieldName, + path: $path, + method: 'POST' + ); + } + ], +]; diff --git a/kirby/config/areas/fields/drawers.php b/kirby/config/areas/fields/drawers.php new file mode 100644 index 0000000..1280a68 --- /dev/null +++ b/kirby/config/areas/fields/drawers.php @@ -0,0 +1,61 @@ + [ + 'load' => function ( + string $modelPath, + string $fieldName, + string|null $path = null + ) { + return Field::drawer( + model: Find::parent($modelPath), + fieldName: $fieldName, + path: $path, + method: 'GET' + ); + }, + 'submit' => function ( + string $modelPath, + string $fieldName, + string|null $path = null + ) { + return Field::drawer( + model: Find::parent($modelPath), + fieldName: $fieldName, + path: $path, + method: 'POST' + ); + } + ], + 'file' => [ + 'load' => function ( + string $modelPath, + string $filename, + string $fieldName, + string|null $path = null + ) { + return Field::drawer( + model: Find::file($modelPath, $filename), + fieldName: $fieldName, + path: $path, + method: 'GET' + ); + }, + 'submit' => function ( + string $modelPath, + string $filename, + string $fieldName, + string|null $path = null + ) { + return Field::drawer( + model: Find::file($modelPath, $filename), + fieldName: $fieldName, + path: $path, + method: 'POST' + ); + } + ], +]; diff --git a/kirby/config/areas/files/dialogs.php b/kirby/config/areas/files/dialogs.php index 44625eb..8707bd0 100644 --- a/kirby/config/areas/files/dialogs.php +++ b/kirby/config/areas/files/dialogs.php @@ -26,7 +26,7 @@ return [ 'type' => 'slug', 'required' => true, 'icon' => 'title', - 'allow' => '@._-', + 'allow' => 'a-z0-9@._-', 'after' => '.' . $file->extension(), 'preselect' => true ] @@ -40,7 +40,8 @@ return [ }, 'submit' => function (string $path, string $filename) { $file = Find::file($path, $filename); - $renamed = $file->changeName($file->kirby()->request()->get('name')); + $name = $file->kirby()->request()->get('name'); + $renamed = $file->changeName($name); $oldUrl = $file->panel()->url(true); $newUrl = $renamed->panel()->url(true); $response = [ @@ -96,6 +97,44 @@ return [ } ], + 'changeTemplate' => [ + 'load' => function (string $path, string $filename) { + $file = Find::file($path, $filename); + $blueprints = $file->blueprints(); + + return [ + 'component' => 'k-form-dialog', + 'props' => [ + 'fields' => [ + 'warning' => [ + 'type' => 'info', + 'theme' => 'notice', + 'text' => I18n::translate('file.changeTemplate.notice') + ], + 'template' => Field::template($blueprints, [ + 'required' => true + ]) + ], + 'theme' => 'notice', + 'submitButton' => I18n::translate('change'), + 'value' => [ + 'template' => $file->template() + ] + ] + ]; + }, + 'submit' => function (string $path, string $filename) { + $file = Find::file($path, $filename); + $template = $file->kirby()->request()->get('template'); + + $file->changeTemplate($template); + + return [ + 'event' => 'file.changeTemplate', + ]; + } + ], + 'delete' => [ 'load' => function (string $path, string $filename) { $file = Find::file($path, $filename); @@ -129,4 +168,5 @@ return [ ]; } ], + ]; diff --git a/kirby/config/areas/lab.php b/kirby/config/areas/lab.php new file mode 100644 index 0000000..580a9fb --- /dev/null +++ b/kirby/config/areas/lab.php @@ -0,0 +1,11 @@ + 'lab', + 'label' => 'Lab', + 'menu' => false, + 'drawers' => require __DIR__ . '/lab/drawers.php', + 'views' => require __DIR__ . '/lab/views.php' + ]; +}; diff --git a/kirby/config/areas/lab/drawers.php b/kirby/config/areas/lab/drawers.php new file mode 100644 index 0000000..9d16d81 --- /dev/null +++ b/kirby/config/areas/lab/drawers.php @@ -0,0 +1,30 @@ + [ + 'pattern' => 'lab/docs/(:any)', + 'load' => function (string $component) { + if (Docs::installed() === false) { + return [ + 'component' => 'k-text-drawer', + 'props' => [ + 'text' => 'The UI docs are not installed.' + ] + ]; + } + + $docs = new Docs($component); + + return [ + 'component' => 'k-lab-docs-drawer', + 'props' => [ + 'icon' => 'book', + 'title' => $component, + 'docs' => $docs->toArray() + ] + ]; + }, + ], +]; diff --git a/kirby/config/areas/lab/views.php b/kirby/config/areas/lab/views.php new file mode 100644 index 0000000..1f6e879 --- /dev/null +++ b/kirby/config/areas/lab/views.php @@ -0,0 +1,151 @@ + [ + 'pattern' => 'lab', + 'action' => function () { + return [ + 'component' => 'k-lab-index-view', + 'props' => [ + 'categories' => Category::all(), + 'info' => Category::installed() ? null : 'The default Lab examples are not installed.', + 'tab' => 'examples', + ], + ]; + } + ], + 'lab.docs' => [ + 'pattern' => 'lab/docs', + 'action' => function () { + $props = match (Docs::installed()) { + true => [ + 'categories' => [['examples' => Docs::all()]], + 'tab' => 'docs', + ], + false => [ + 'info' => 'The UI docs are not installed.', + 'tab' => 'docs', + ] + }; + + return [ + 'component' => 'k-lab-index-view', + 'title' => 'Docs', + 'breadcrumb' => [ + [ + 'label' => 'Docs', + 'link' => 'lab/docs' + ] + ], + 'props' => $props, + ]; + } + ], + 'lab.doc' => [ + 'pattern' => 'lab/docs/(:any)', + 'action' => function (string $component) { + $crumbs = [ + [ + 'label' => 'Docs', + 'link' => 'lab/docs' + ], + [ + 'label' => $component, + 'link' => 'lab/docs/' . $component + ] + ]; + + if (Docs::installed() === false) { + return [ + 'component' => 'k-lab-index-view', + 'title' => $component, + 'breadcrumb' => $crumbs, + 'props' => [ + 'info' => 'The UI docs are not installed.', + 'tab' => 'docs', + ], + ]; + } + + $docs = new Docs($component); + + return [ + 'component' => 'k-lab-docs-view', + 'title' => $component, + 'breadcrumb' => $crumbs, + 'props' => [ + 'component' => $component, + 'docs' => $docs->toArray(), + 'lab' => $docs->lab() + ] + ]; + } + ], + 'lab.vue' => [ + 'pattern' => [ + 'lab/(:any)/(:any)/index.vue', + 'lab/(:any)/(:any)/(:any)/index.vue' + ], + 'action' => function ( + string $category, + string $id, + string|null $tab = null + ) { + return Category::factory($category)->example($id, $tab)->serve(); + } + ], + 'lab.example' => [ + 'pattern' => 'lab/(:any)/(:any)/(:any?)', + 'action' => function ( + string $category, + string $id, + string|null $tab = null + ) { + $category = Category::factory($category); + $example = $category->example($id, $tab); + $props = $example->props(); + $vue = $example->vue(); + $compiler = App::instance()->option('panel.vue.compiler', true); + + if (Docs::installed() === true && $docs = $props['docs'] ?? null) { + $docs = new Docs($docs); + } + + $github = $docs?->github(); + + if ($source = $props['source'] ?? null) { + $github ??= 'https://github.com/getkirby/kirby/tree/main/' . $source; + } + + return [ + 'component' => 'k-lab-playground-view', + 'breadcrumb' => [ + [ + 'label' => $category->name(), + ], + [ + 'label' => $example->title(), + 'link' => $example->url() + ] + ], + 'props' => [ + 'compiler' => $compiler, + 'docs' => $docs?->name(), + 'examples' => $vue['examples'], + 'file' => $example->module(), + 'github' => $github, + 'props' => $props, + 'styles' => $vue['style'], + 'tab' => $example->tab(), + 'tabs' => array_values($example->tabs()), + 'template' => $vue['template'], + 'title' => $example->title(), + ], + ]; + } + ] +]; diff --git a/kirby/config/areas/languages.php b/kirby/config/areas/languages.php index d464c58..263ffd7 100644 --- a/kirby/config/areas/languages.php +++ b/kirby/config/areas/languages.php @@ -4,7 +4,7 @@ use Kirby\Toolkit\I18n; return function ($kirby) { return [ - 'icon' => 'globe', + 'icon' => 'translate', 'label' => I18n::translate('view.languages'), 'menu' => true, 'dialogs' => require __DIR__ . '/languages/dialogs.php', diff --git a/kirby/config/areas/languages/dialogs.php b/kirby/config/areas/languages/dialogs.php index a38b148..8923f1f 100644 --- a/kirby/config/areas/languages/dialogs.php +++ b/kirby/config/areas/languages/dialogs.php @@ -2,13 +2,15 @@ use Kirby\Cms\App; use Kirby\Cms\Find; -use Kirby\Panel\Field; +use Kirby\Cms\LanguageVariable; +use Kirby\Exception\NotFoundException; use Kirby\Toolkit\A; use Kirby\Toolkit\Escape; use Kirby\Toolkit\I18n; $languageDialogFields = [ 'name' => [ + 'counter' => false, 'label' => I18n::translate('language.name'), 'type' => 'text', 'required' => true, @@ -19,7 +21,7 @@ $languageDialogFields = [ 'type' => 'text', 'required' => true, 'counter' => false, - 'icon' => 'globe', + 'icon' => 'translate', 'width' => '1/2' ], 'direction' => [ @@ -34,11 +36,27 @@ $languageDialogFields = [ 'width' => '1/2' ], 'locale' => [ - 'label' => I18n::translate('language.locale'), - 'type' => 'text', + 'counter' => false, + 'label' => I18n::translate('language.locale'), + 'type' => 'text', ], ]; +$translationDialogFields = [ + 'key' => [ + 'counter' => false, + 'icon' => null, + 'label' => I18n::translate('language.variable.key'), + 'type' => 'text' + ], + 'value' => [ + 'buttons' => false, + 'counter' => false, + 'label' => I18n::translate('language.variable.value'), + 'type' => 'textarea' + ] +]; + return [ // create language @@ -92,8 +110,10 @@ return [ }, 'submit' => function (string $id) { Find::language($id)->delete(); + return [ - 'event' => 'language.delete', + 'event' => 'language.delete', + 'redirect' => 'languages' ]; } ], @@ -152,4 +172,109 @@ return [ ]; } ], + + 'language.translation.create' => [ + 'pattern' => 'languages/(:any)/translations/create', + 'load' => function (string $languageCode) use ($translationDialogFields) { + // find the language to make sure it exists + Find::language($languageCode); + + return [ + 'component' => 'k-form-dialog', + 'props' => [ + 'fields' => $translationDialogFields, + 'size' => 'large', + ], + ]; + }, + 'submit' => function (string $languageCode) { + $request = App::instance()->request(); + $language = Find::language($languageCode); + + $key = $request->get('key', ''); + $value = $request->get('value', ''); + + LanguageVariable::create($key, $value); + + if ($language->isDefault() === false) { + $language->variable($key)->update($value); + } + + return true; + } + ], + 'language.translation.delete' => [ + 'pattern' => 'languages/(:any)/translations/(:any)/delete', + 'load' => function (string $languageCode, string $translationKey) { + $variable = Find::language($languageCode)->variable($translationKey, true); + + if ($variable->exists() === false) { + throw new NotFoundException([ + 'key' => 'language.variable.notFound' + ]); + } + + return [ + 'component' => 'k-remove-dialog', + 'props' => [ + 'text' => I18n::template('language.variable.delete.confirm', [ + 'key' => Escape::html($variable->key()) + ]) + ], + ]; + }, + 'submit' => function (string $languageCode, string $translationKey) { + return Find::language($languageCode)->variable($translationKey, true)->delete(); + } + ], + 'language.translation.update' => [ + 'pattern' => 'languages/(:any)/translations/(:any)/update', + 'load' => function (string $languageCode, string $translationKey) use ($translationDialogFields) { + $variable = Find::language($languageCode)->variable($translationKey, true); + + if ($variable->exists() === false) { + throw new NotFoundException([ + 'key' => 'language.variable.notFound' + ]); + } + + $fields = $translationDialogFields; + $fields['key']['disabled'] = true; + $fields['value']['autofocus'] = true; + + // shows info text when variable is an array + // TODO: 5.0: use entries field instead showing info text + $isVariableArray = is_array($variable->value()) === true; + + if ($isVariableArray === true) { + $fields['value'] = [ + 'label' => I18n::translate('info'), + 'type' => 'info', + 'text' => 'You are using an array variable for this key. Please modify it in the language file in /site/languages', + ]; + } + + return [ + 'component' => 'k-form-dialog', + 'props' => [ + 'cancelButton' => $isVariableArray === false, + 'fields' => $fields, + 'size' => 'large', + 'submitButton' => $isVariableArray === false, + 'value' => [ + 'key' => $variable->key(), + 'value' => $variable->value() + ] + ], + ]; + }, + 'submit' => function (string $languageCode, string $translationKey) { + Find::language($languageCode)->variable($translationKey, true)->update( + App::instance()->request()->get('value', '') + ); + + return true; + } + ] + ]; diff --git a/kirby/config/areas/languages/views.php b/kirby/config/areas/languages/views.php index 0eee9da..ac2d6d4 100644 --- a/kirby/config/areas/languages/views.php +++ b/kirby/config/areas/languages/views.php @@ -1,9 +1,110 @@ [ + 'pattern' => 'languages/(:any)', + 'when' => function (): bool { + return App::instance()->option('languages.variables', true) !== false; + }, + 'action' => function (string $code) { + $kirby = App::instance(); + $language = Find::language($code); + $link = '/languages/' . $language->code(); + $strings = []; + $foundation = $kirby->defaultLanguage()->translations(); + $translations = $language->translations(); + + // TODO: update following line and adapt for update and delete options + // when new `languageVariables.*` permissions available + $canUpdate = $kirby->user()?->role()->permissions()->for('languages', 'update') === true; + + ksort($foundation); + + foreach ($foundation as $key => $value) { + $strings[] = [ + 'key' => $key, + 'value' => $translations[$key] ?? null, + 'options' => [ + [ + 'click' => 'update', + 'disabled' => $canUpdate === false, + 'icon' => 'edit', + 'text' => I18n::translate('edit'), + ], + [ + 'click' => 'delete', + 'disabled' => $canUpdate === false || $language->isDefault() === false, + 'icon' => 'trash', + 'text' => I18n::translate('delete'), + ] + ] + ]; + } + + $next = function () use ($language) { + if ($next = $language->next()) { + return [ + 'link' => '/languages/' . $next->code(), + 'title' => $next->name(), + ]; + } + }; + + $prev = function () use ($language) { + if ($prev = $language->prev()) { + return [ + 'link' => '/languages/' . $prev->code(), + 'title' => $prev->name(), + ]; + } + }; + + return [ + 'component' => 'k-language-view', + 'breadcrumb' => [ + [ + 'label' => $name = $language->name(), + 'link' => $link, + ] + ], + 'props' => [ + 'deletable' => $language->isDeletable(), + 'code' => Escape::html($language->code()), + 'default' => $language->isDefault(), + 'direction' => $language->direction(), + 'id' => $language->code(), + 'info' => [ + [ + 'label' => 'Status', + 'value' => I18n::translate('language.' . ($language->isDefault() ? 'default' : 'secondary')), + ], + [ + 'label' => I18n::translate('language.code'), + 'value' => $language->code(), + ], + [ + 'label' => I18n::translate('language.locale'), + 'value' => $language->locale(LC_ALL) + ], + [ + 'label' => I18n::translate('language.direction'), + 'value' => I18n::translate('language.direction.' . $language->direction()), + ], + ], + 'name' => $name, + 'next' => $next, + 'prev' => $prev, + 'translations' => $strings, + 'url' => $language->url(), + ] + ]; + } + ], 'languages' => [ 'pattern' => 'languages', 'action' => function () { @@ -13,13 +114,15 @@ return [ 'component' => 'k-languages-view', 'props' => [ 'languages' => $kirby->languages()->values(fn ($language) => [ - 'default' => $language->isDefault(), - 'id' => $language->code(), - 'info' => Escape::html($language->code()), - 'text' => Escape::html($language->name()), - ]) + 'deletable' => $language->isDeletable(), + 'default' => $language->isDefault(), + 'id' => $language->code(), + 'info' => Escape::html($language->code()), + 'text' => Escape::html($language->name()), + ]), + 'variables' => $kirby->option('languages.variables', true) ] ]; } - ], + ] ]; diff --git a/kirby/config/areas/search.php b/kirby/config/areas/search.php new file mode 100644 index 0000000..2f7474e --- /dev/null +++ b/kirby/config/areas/search.php @@ -0,0 +1,11 @@ + 'search', + 'label' => I18n::translate('search'), + 'views' => require __DIR__ . '/search/views.php' + ]; +}; diff --git a/kirby/config/areas/search/views.php b/kirby/config/areas/search/views.php new file mode 100644 index 0000000..365e834 --- /dev/null +++ b/kirby/config/areas/search/views.php @@ -0,0 +1,17 @@ + [ + 'pattern' => 'search', + 'action' => function () { + return [ + 'component' => 'k-search-view', + 'props' => [ + 'type' => App::instance()->request()->get('type'), + ] + ]; + } + ], +]; diff --git a/kirby/config/areas/site.php b/kirby/config/areas/site.php index 3f082be..8995d12 100644 --- a/kirby/config/areas/site.php +++ b/kirby/config/areas/site.php @@ -3,15 +3,19 @@ use Kirby\Toolkit\I18n; return function ($kirby) { + $blueprint = $kirby->site()->blueprint(); + return [ 'breadcrumbLabel' => function () use ($kirby) { return $kirby->site()->title()->or(I18n::translate('view.site'))->toString(); }, - 'icon' => 'home', - 'label' => $kirby->site()->blueprint()->title() ?? I18n::translate('view.site'), + 'icon' => $blueprint->icon() ?? 'home', + 'label' => $blueprint->title() ?? I18n::translate('view.site'), 'menu' => true, 'dialogs' => require __DIR__ . '/site/dialogs.php', + 'drawers' => require __DIR__ . '/site/drawers.php', 'dropdowns' => require __DIR__ . '/site/dropdowns.php', + 'requests' => require __DIR__ . '/site/requests.php', 'searches' => require __DIR__ . '/site/searches.php', 'views' => require __DIR__ . '/site/views.php', ]; diff --git a/kirby/config/areas/site/dialogs.php b/kirby/config/areas/site/dialogs.php index 9ec9c7f..1a2cc1d 100644 --- a/kirby/config/areas/site/dialogs.php +++ b/kirby/config/areas/site/dialogs.php @@ -2,14 +2,21 @@ use Kirby\Cms\App; use Kirby\Cms\Find; +use Kirby\Cms\PageRules; +use Kirby\Cms\Url; use Kirby\Exception\Exception; use Kirby\Exception\InvalidArgumentException; use Kirby\Exception\PermissionException; +use Kirby\Panel\ChangesDialog; use Kirby\Panel\Field; +use Kirby\Panel\PageCreateDialog; use Kirby\Panel\Panel; +use Kirby\Toolkit\Escape; use Kirby\Toolkit\I18n; use Kirby\Toolkit\Str; +use Kirby\Uuid\Uuids; +$fields = require __DIR__ . '/../fields/dialogs.php'; $files = require __DIR__ . '/../files/dialogs.php'; return [ @@ -155,10 +162,16 @@ return [ 'component' => 'k-form-dialog', 'props' => [ 'fields' => [ + 'notice' => [ + 'type' => 'info', + 'theme' => 'notice', + 'text' => I18n::translate('page.changeTemplate.notice') + ], 'template' => Field::template($blueprints, [ 'required' => true ]) ], + 'theme' => 'notice', 'submitButton' => I18n::translate('change'), 'value' => [ 'template' => $page->intendedTemplate()->name() @@ -167,9 +180,10 @@ return [ ]; }, 'submit' => function (string $id) { - $request = App::instance()->request(); + $page = Find::page($id); + $template = App::instance()->request()->get('template'); - Find::page($id)->changeTemplate($request->get('template')); + $page->changeTemplate($template); return [ 'event' => 'page.changeTemplate', @@ -181,12 +195,23 @@ return [ 'page.changeTitle' => [ 'pattern' => 'pages/(:any)/changeTitle', 'load' => function (string $id) { - $request = App::instance()->request(); + $kirby = App::instance(); + $request = $kirby->request(); $page = Find::page($id); $permissions = $page->permissions(); $select = $request->get('select', 'title'); + // build the path prefix + $path = match ($kirby->multilang()) { + true => Str::after($kirby->site()->url(), $kirby->url()) . '/', + false => '/' + }; + + if ($parent = $page->parent()) { + $path .= $parent->uri() . '/'; + } + return [ 'component' => 'k-form-dialog', 'props' => [ @@ -199,7 +224,7 @@ return [ 'slug' => Field::slug([ 'required' => true, 'preselect' => $select === 'slug', - 'path' => $page->parent() ? '/' . $page->parent()->uri() . '/' : '/', + 'path' => $path, 'disabled' => $permissions->can('changeSlug') === false, 'wizard' => [ 'text' => I18n::translate('page.changeSlug.fromTitle'), @@ -224,17 +249,8 @@ return [ $slug = trim($request->get('slug', '')); // basic input validation before we move on - if (Str::length($title) === 0) { - throw new InvalidArgumentException([ - 'key' => 'page.changeTitle.empty' - ]); - } - - if (Str::length($slug) === 0) { - throw new InvalidArgumentException([ - 'key' => 'page.slug.invalid' - ]); - } + PageRules::validateTitleLength($title); + PageRules::validateSlugLength($slug); // nothing changed if ($page->title()->value() === $title && $page->slug() === $slug) { @@ -277,93 +293,30 @@ return [ 'page.create' => [ 'pattern' => 'pages/create', 'load' => function () { - $kirby = App::instance(); - $request = $kirby->request(); + $request = App::instance()->request(); + $dialog = new PageCreateDialog( + parentId: $request->get('parent'), + sectionId: $request->get('section'), + slug: $request->get('slug'), + template: $request->get('template'), + title: $request->get('title'), + viewId: $request->get('view'), + ); - // the parent model for the new page - $parent = $request->get('parent', 'site'); - - // the view on which the add button is located - // this is important to find the right section - // and provide the correct templates for the new page - $view = $request->get('view', $parent); - - // templates will be fetched depending on the - // section settings in the blueprint - $section = $request->get('section'); - - // this is the parent model - $model = Find::parent($parent); - - // this is the view model - // i.e. site if the add button is on - // the dashboard - $view = Find::parent($view); - - // available blueprints/templates for the new page - // are always loaded depending on the matching section - // in the view model blueprint - $blueprints = $view->blueprints($section); - - // the pre-selected template - $template = $blueprints[0]['name'] ?? $blueprints[0]['value'] ?? null; - - $fields = [ - 'parent' => Field::hidden(), - 'title' => Field::title([ - 'required' => true, - 'preselect' => true - ]), - 'slug' => Field::slug([ - 'required' => true, - 'sync' => 'title', - 'path' => empty($model->id()) === false ? '/' . $model->id() . '/' : '/' - ]), - 'template' => Field::hidden() - ]; - - // only show template field if > 1 templates available - // or when in debug mode - if (count($blueprints) > 1 || $kirby->option('debug') === true) { - $fields['template'] = Field::template($blueprints, [ - 'required' => true - ]); - } - - return [ - 'component' => 'k-form-dialog', - 'props' => [ - 'fields' => $fields, - 'submitButton' => I18n::translate('page.draft.create'), - 'value' => [ - 'parent' => $parent, - 'slug' => '', - 'template' => $template, - 'title' => '', - ] - ] - ]; + return $dialog->load(); }, 'submit' => function () { $request = App::instance()->request(); - $title = trim($request->get('title', '')); + $dialog = new PageCreateDialog( + parentId: $request->get('parent'), + sectionId: $request->get('section'), + slug: $request->get('slug'), + template: $request->get('template'), + title: $request->get('title'), + viewId: $request->get('view'), + ); - if (Str::length($title) === 0) { - throw new InvalidArgumentException([ - 'key' => 'page.changeTitle.empty' - ]); - } - - $page = Find::parent($request->get('parent', 'site'))->createChild([ - 'content' => ['title' => $title], - 'slug' => $request->get('slug'), - 'template' => $request->get('template'), - ]); - - return [ - 'event' => 'page.create', - 'redirect' => $page->panel()->url(true) - ]; + return $dialog->submit($request->get()); } ], @@ -479,7 +432,7 @@ return [ ]; } - $slugAppendix = Str::slug(I18n::translate('page.duplicate.appendix')); + $slugAppendix = Url::slug(I18n::translate('page.duplicate.appendix')); $titleAppendix = I18n::translate('page.duplicate.appendix'); // if the item to be duplicated already exists @@ -529,6 +482,13 @@ return [ } ], + // page field dialogs + 'page.fields' => [ + 'pattern' => '(pages/.*?)/fields/(:any)/(:all?)', + 'load' => $fields['model']['load'], + 'submit' => $fields['model']['submit'] + ], + // change filename 'page.file.changeName' => [ 'pattern' => '(pages/.*?)/files/(:any)/changeName', @@ -543,6 +503,13 @@ return [ 'submit' => $files['changeSort']['submit'], ], + // change template + 'page.file.changeTemplate' => [ + 'pattern' => '(pages/.*?)/files/(:any)/changeTemplate', + 'load' => $files['changeTemplate']['load'], + 'submit' => $files['changeTemplate']['submit'], + ], + // delete 'page.file.delete' => [ 'pattern' => '(pages/.*?)/files/(:any)/delete', @@ -550,6 +517,56 @@ return [ 'submit' => $files['delete']['submit'], ], + // page file field dialogs + 'page.file.fields' => [ + 'pattern' => '(pages/.*?)/files/(:any)/fields/(:any)/(:all?)', + 'load' => $fields['file']['load'], + 'submit' => $fields['file']['submit'], + ], + + // move page + 'page.move' => [ + 'pattern' => 'pages/(:any)/move', + 'load' => function (string $id) { + $page = Find::page($id); + $parent = $page->parentModel(); + + if (Uuids::enabled() === false) { + $parentId = $parent?->id() ?? '/'; + } else { + $parentId = $parent?->uuid()->toString() ?? 'site://'; + } + + return [ + 'component' => 'k-page-move-dialog', + 'props' => [ + 'value' => [ + 'move' => $page->panel()->url(true), + 'parent' => $parentId + ] + ] + ]; + }, + 'submit' => function (string $id) { + $kirby = App::instance(); + $parentId = $kirby->request()->get('parent'); + $parent = (empty($parentId) === true || $parentId === '/' || $parentId === 'site://') ? $kirby->site() : Find::page($parentId); + $oldPage = Find::page($id); + $newPage = $oldPage->move($parent); + + return [ + 'event' => 'page.move', + 'redirect' => $newPage->panel()->url(true), + 'dispatch' => [ + 'content/move' => [ + $oldPage->panel()->url(true), + $newPage->panel()->url(true) + ] + ], + ]; + } + ], + // change site title 'site.changeTitle' => [ 'pattern' => 'site/changeTitle', @@ -572,14 +589,21 @@ return [ }, 'submit' => function () { $kirby = App::instance(); - $kirby->site()->changeTitle($kirby->request()->get('title')); + return [ 'event' => 'site.changeTitle', ]; } ], + // site field dialogs + 'site.fields' => [ + 'pattern' => '(site)/fields/(:any)/(:all?)', + 'load' => $fields['model']['load'], + 'submit' => $fields['model']['submit'], + ], + // change filename 'site.file.changeName' => [ 'pattern' => '(site)/files/(:any)/changeName', @@ -594,6 +618,13 @@ return [ 'submit' => $files['changeSort']['submit'], ], + // change template + 'site.file.changeTemplate' => [ + 'pattern' => '(site)/files/(:any)/changeTemplate', + 'load' => $files['changeTemplate']['load'], + 'submit' => $files['changeTemplate']['submit'], + ], + // delete 'site.file.delete' => [ 'pattern' => '(site)/files/(:any)/delete', @@ -601,4 +632,24 @@ return [ 'submit' => $files['delete']['submit'], ], + // site file field dialogs + 'site.file.fields' => [ + 'pattern' => '(site)/files/(:any)/fields/(:any)/(:all?)', + 'load' => $fields['file']['load'], + 'submit' => $fields['file']['submit'], + ], + + // content changes + 'changes' => [ + 'pattern' => 'changes', + 'load' => function () { + $dialog = new ChangesDialog(); + return $dialog->load(); + }, + 'submit' => function () { + $dialog = new ChangesDialog(); + $ids = App::instance()->request()->get('ids'); + return $dialog->submit($ids); + } + ], ]; diff --git a/kirby/config/areas/site/drawers.php b/kirby/config/areas/site/drawers.php new file mode 100644 index 0000000..7bdf4da --- /dev/null +++ b/kirby/config/areas/site/drawers.php @@ -0,0 +1,33 @@ + [ + 'pattern' => '(pages/.*?)/fields/(:any)/(:all?)', + 'load' => $fields['model']['load'], + 'submit' => $fields['model']['submit'] + ], + + // page file field drawers + 'page.file.fields' => [ + 'pattern' => '(pages/.*?)/files/(:any)/fields/(:any)/(:all?)', + 'load' => $fields['file']['load'], + 'submit' => $fields['file']['submit'], + ], + + // site field drawers + 'site.fields' => [ + 'pattern' => '(site)/fields/(:any)/(:all?)', + 'load' => $fields['model']['load'], + 'submit' => $fields['model']['submit'], + ], + + // site file field drawers + 'site.file.fields' => [ + 'pattern' => '(site)/files/(:any)/fields/(:any)/(:all?)', + 'load' => $fields['file']['load'], + 'submit' => $fields['file']['submit'], + ], +]; diff --git a/kirby/config/areas/site/dropdowns.php b/kirby/config/areas/site/dropdowns.php index c08853b..b756cf4 100644 --- a/kirby/config/areas/site/dropdowns.php +++ b/kirby/config/areas/site/dropdowns.php @@ -1,14 +1,9 @@ [ - 'pattern' => 'changes', - 'options' => fn () => Dropdown::changes() - ], 'page' => [ 'pattern' => 'pages/(:any)', 'options' => function (string $path) { diff --git a/kirby/config/areas/site/requests.php b/kirby/config/areas/site/requests.php new file mode 100644 index 0000000..74a0228 --- /dev/null +++ b/kirby/config/areas/site/requests.php @@ -0,0 +1,90 @@ + [ + 'pattern' => 'site/tree', + 'action' => function () { + $kirby = App::instance(); + $request = $kirby->request(); + $move = $request->get('move'); + $move = $move ? Find::parent($move) : null; + $parent = $request->get('parent'); + + if ($parent === null) { + $site = $kirby->site(); + $panel = $site->panel(); + $uuid = $site->uuid()?->toString(); + $url = $site->url(); + $value = $uuid ?? '/'; + + return [ + [ + 'children' => $panel->url(true), + 'disabled' => $move?->isMovableTo($site) === false, + 'hasChildren' => true, + 'icon' => 'home', + 'id' => '/', + 'label' => I18n::translate('view.site'), + 'open' => false, + 'url' => $url, + 'uuid' => $uuid, + 'value' => $value + ] + ]; + } + + $parent = Find::parent($parent); + $pages = []; + + foreach ($parent->childrenAndDrafts()->filterBy('isListable', true) as $child) { + $panel = $child->panel(); + $uuid = $child->uuid()?->toString(); + $url = $child->url(); + $value = $uuid ?? $child->id(); + + $pages[] = [ + 'children' => $panel->url(true), + 'disabled' => $move?->isMovableTo($child) === false, + 'hasChildren' => $child->hasChildren() === true || $child->hasDrafts() === true, + 'icon' => $panel->image()['icon'] ?? null, + 'id' => $child->id(), + 'open' => false, + 'label' => $child->title()->value(), + 'url' => $url, + 'uuid' => $uuid, + 'value' => $value + ]; + } + + return $pages; + } + ], + 'tree.parents' => [ + 'pattern' => 'site/tree/parents', + 'action' => function () { + $kirby = App::instance(); + $request = $kirby->request(); + $root = $request->get('root'); + $page = $kirby->page($request->get('page')); + $parents = $page?->parents()->flip()->values( + fn ($parent) => $parent->uuid()?->toString() ?? $parent->id() + ) ?? []; + + // if root is included, add the site as top-level parent + if ($root === 'true') { + array_unshift($parents, $kirby->site()->uuid()?->toString() ?? '/'); + } + + return [ + 'data' => $parents + ]; + } + ] + // @codeCoverageIgnoreEnd +]; diff --git a/kirby/config/areas/site/searches.php b/kirby/config/areas/site/searches.php index 6719bb2..f40f218 100644 --- a/kirby/config/areas/site/searches.php +++ b/kirby/config/areas/site/searches.php @@ -8,50 +8,49 @@ return [ 'pages' => [ 'label' => I18n::translate('pages'), 'icon' => 'page', - 'query' => function (string $query = null) { - $pages = App::instance()->site() + 'query' => function (string|null $query, int $limit, int $page) { + $kirby = App::instance(); + $pages = $kirby->site() ->index(true) ->search($query) - ->filter('isReadable', true) - ->limit(10); + ->filter('isListable', true) + ->paginate($limit, $page); - $results = []; - - foreach ($pages as $page) { - $results[] = [ + return [ + 'results' => $pages->values(fn ($page) => [ 'image' => $page->panel()->image(), 'text' => Escape::html($page->title()->value()), 'link' => $page->panel()->url(true), - 'info' => Escape::html($page->id()) - ]; - } - - return $results; + 'info' => Escape::html($page->id()), + 'uuid' => $page->uuid()?->toString(), + ]), + 'pagination' => $pages->pagination()->toArray() + ]; } ], 'files' => [ 'label' => I18n::translate('files'), 'icon' => 'image', - 'query' => function (string $query = null) { - $files = App::instance()->site() + 'query' => function (string|null $query, int $limit, int $page) { + $kirby = App::instance(); + $files = $kirby->site() ->index(true) - ->filter('isReadable', true) + ->filter('isListable', true) ->files() + ->filter('isListable', true) ->search($query) - ->limit(10); + ->paginate($limit, $page); - $results = []; - - foreach ($files as $file) { - $results[] = [ + return [ + 'results' => $files->values(fn ($file) => [ 'image' => $file->panel()->image(), 'text' => Escape::html($file->filename()), 'link' => $file->panel()->url(true), - 'info' => Escape::html($file->id()) - ]; - } - - return $results; + 'info' => Escape::html($file->id()), + 'uuid' => $file->uuid()->toString(), + ]), + 'pagination' => $files->pagination()->toArray() + ]; } ] ]; diff --git a/kirby/config/areas/system/dialogs.php b/kirby/config/areas/system/dialogs.php index 00502a8..e8dd694 100644 --- a/kirby/config/areas/system/dialogs.php +++ b/kirby/config/areas/system/dialogs.php @@ -1,6 +1,7 @@ [ 'load' => function () { - $license = App::instance()->system()->license(); - - // @codeCoverageIgnoreStart - // the system is registered but the license - // key is only visible for admins - if ($license === true) { - $license = 'Kirby 3'; - } - // @codeCoverageIgnoreEnd + $kirby = App::instance(); + $license = $kirby->system()->license(); + $obfuscated = $kirby->user()->isAdmin() === false; + $status = $license->status(); + $renewable = $status->renewable(); return [ - 'component' => 'k-form-dialog', + 'component' => 'k-license-dialog', 'props' => [ - 'size' => 'medium', - 'fields' => [ - 'license' => [ - 'type' => 'info', - 'label' => I18n::translate('license'), - 'text' => $license ? $license : I18n::translate('license.unregistered.label'), - 'theme' => $license ? 'code' : 'negative', - 'help' => $license ? - // @codeCoverageIgnoreStart - '' . I18n::translate('license.manage') . ' →' : - // @codeCoverageIgnoreEnd - '' . I18n::translate('license.buy') . ' →' - ] + 'license' => [ + 'code' => $license->code($obfuscated), + 'icon' => $status->icon(), + 'info' => $status->info($license->renewal('Y-m-d', 'date')), + 'theme' => $status->theme(), + 'type' => $license->label(), ], - 'submitButton' => false, - 'cancelButton' => false, + 'cancelButton' => $renewable, + 'submitButton' => $renewable ? [ + 'icon' => 'refresh', + 'text' => I18n::translate('renew'), + 'theme' => 'love', + ] : false, ] ]; + }, + 'submit' => function () { + // @codeCoverageIgnoreStart + $response = App::instance()->system()->license()->upgrade(); + + // the upgrade is still needed + if ($response['status'] === 'upgrade') { + return [ + 'redirect' => $response['url'] + ]; + } + + // the upgrade has already been completed + if ($response['status'] === 'complete') { + return [ + 'event' => 'system.renew', + 'message' => I18n::translate('license.success') + ]; + } + + throw new LogicException('The upgrade failed'); + // @codeCoverageIgnoreEnd } ], // license registration 'registration' => [ 'load' => function () { $system = App::instance()->system(); + $local = $system->isLocal(); return [ 'component' => 'k-form-dialog', 'props' => [ 'fields' => [ 'domain' => [ + 'label' => I18n::translate('license.activate.label'), 'type' => 'info', - 'theme' => $system->isLocal() ? 'notice' : 'info', - 'text' => I18n::template('license.register.' . ($system->isLocal() ? 'local' : 'domain'), ['host' => $system->indexUrl()]) + 'theme' => $local ? 'warning' : 'info', + 'text' => I18n::template('license.activate.' . ($local ? 'local' : 'domain'), ['host' => $system->indexUrl()]) ], 'license' => [ - 'label' => I18n::translate('license.register.label'), + 'label' => I18n::translate('license.code.label'), 'type' => 'text', 'required' => true, 'counter' => false, - 'placeholder' => 'K3-', - 'help' => I18n::translate('license.register.help') + 'placeholder' => 'K-', + 'help' => I18n::translate('license.code.help') . ' ' . '' . I18n::translate('license.buy') . ' →' ], 'email' => Field::email(['required' => true]) ], - 'submitButton' => I18n::translate('license.register'), + 'submitButton' => [ + 'icon' => 'key', + 'text' => I18n::translate('activate'), + 'theme' => 'love', + ], 'value' => [ 'license' => null, 'email' => null @@ -83,7 +105,7 @@ return [ return [ 'event' => 'system.register', - 'message' => I18n::translate('license.register.success') + 'message' => I18n::translate('license.success') ]; // @codeCoverageIgnoreEnd } diff --git a/kirby/config/areas/system/views.php b/kirby/config/areas/system/views.php index dab7de6..87ad888 100644 --- a/kirby/config/areas/system/views.php +++ b/kirby/config/areas/system/views.php @@ -11,31 +11,34 @@ return [ $system = $kirby->system(); $updateStatus = $system->updateStatus(); $license = $system->license(); + $debugMode = $kirby->option('debug', false) === true; + $isLocal = $system->isLocal(); $environment = [ [ - 'label' => $license ? I18n::translate('license') : I18n::translate('license.register.label'), - 'value' => $license ? 'Kirby 3' : I18n::translate('license.unregistered.label'), - 'theme' => $license ? null : 'negative', - 'dialog' => $license ? 'license' : 'registration' + 'label' => $license->status()->label(), + 'value' => $license->label(), + 'theme' => $license->status()->theme(), + 'icon' => $license->status()->icon(), + 'dialog' => $license->status()->dialog() ], [ 'label' => $updateStatus?->label() ?? I18n::translate('version'), 'value' => $kirby->version(), - 'link' => ( - $updateStatus ? - $updateStatus->url() : - 'https://github.com/getkirby/kirby/releases/tag/' . $kirby->version() - ), - 'theme' => $updateStatus?->theme() + 'link' => $updateStatus?->url() ?? + 'https://github.com/getkirby/kirby/releases/tag/' . $kirby->version(), + 'theme' => $updateStatus?->theme(), + 'icon' => $updateStatus?->icon() ?? 'info' ], [ 'label' => 'PHP', - 'value' => phpversion() + 'value' => phpversion(), + 'icon' => 'code' ], [ 'label' => I18n::translate('server'), - 'value' => $system->serverSoftware() ?? '?' + 'value' => $system->serverSoftwareShort() ?? '?', + 'icon' => 'server' ] ]; @@ -44,10 +47,14 @@ return [ $plugins = $system->plugins()->values(function ($plugin) use (&$exceptions) { $authors = $plugin->authorsNames(); $updateStatus = $plugin->updateStatus(); - $version = $updateStatus?->toArray() ?? $plugin->version() ?? '–'; + $version = $updateStatus?->toArray(); + $version ??= $plugin->version() ?? '–'; if ($updateStatus !== null) { - $exceptions = array_merge($exceptions, $updateStatus->exceptionMessages()); + $exceptions = [ + ...$exceptions, + ...$updateStatus->exceptionMessages() + ]; } return [ @@ -63,15 +70,29 @@ return [ $security = $updateStatus?->messages() ?? []; - if ($kirby->option('debug', false) === true) { + if ($isLocal === true) { $security[] = [ - 'id' => 'debug', - 'text' => I18n::translate('system.issues.debug'), - 'link' => 'https://getkirby.com/security/debug' + 'id' => 'local', + 'icon' => 'info', + 'theme' => 'info', + 'text' => I18n::translate('system.issues.local') ]; } - if ($kirby->environment()->https() !== true) { + if ($debugMode === true) { + $security[] = [ + 'id' => 'debug', + 'icon' => $isLocal ? 'info' : 'alert', + 'theme' => $isLocal ? 'info' : 'negative', + 'text' => I18n::translate('system.issues.debug'), + 'link' => 'https://getkirby.com/security/debug' + ]; + } + + if ( + $isLocal === false && + $kirby->environment()->https() !== true + ) { $security[] = [ 'id' => 'https', 'text' => I18n::translate('system.issues.https'), @@ -79,19 +100,34 @@ return [ ]; } + if ($kirby->option('panel.vue.compiler', null) === null) { + $security[] = [ + 'id' => 'vue-compiler', + 'link' => 'https://getkirby.com/security/vue-compiler', + 'text' => I18n::translate('system.issues.vue.compiler'), + 'theme' => 'notice' + ]; + } + + // sensitive URLs + if ($isLocal === false) { + $sensitive = [ + 'content' => $system->exposedFileUrl('content'), + 'git' => $system->exposedFileUrl('git'), + 'kirby' => $system->exposedFileUrl('kirby'), + 'site' => $system->exposedFileUrl('site') + ]; + } + return [ 'component' => 'k-system-view', 'props' => [ 'environment' => $environment, - 'exceptions' => $kirby->option('debug') === true ? $exceptions : [], + 'exceptions' => $debugMode ? $exceptions : [], + 'info' => $system->info(), 'plugins' => $plugins, 'security' => $security, - 'urls' => [ - 'content' => $system->exposedFileUrl('content'), - 'git' => $system->exposedFileUrl('git'), - 'kirby' => $system->exposedFileUrl('kirby'), - 'site' => $system->exposedFileUrl('site') - ] + 'urls' => $sensitive ?? null ] ]; } diff --git a/kirby/config/areas/users.php b/kirby/config/areas/users.php index ff7e130..dbccc5f 100644 --- a/kirby/config/areas/users.php +++ b/kirby/config/areas/users.php @@ -9,6 +9,7 @@ return function ($kirby) { 'search' => 'users', 'menu' => true, 'dialogs' => require __DIR__ . '/users/dialogs.php', + 'drawers' => require __DIR__ . '/users/drawers.php', 'dropdowns' => require __DIR__ . '/users/dropdowns.php', 'searches' => require __DIR__ . '/users/searches.php', 'views' => require __DIR__ . '/users/views.php' diff --git a/kirby/config/areas/users/dialogs.php b/kirby/config/areas/users/dialogs.php index 291abb3..6c47bca 100644 --- a/kirby/config/areas/users/dialogs.php +++ b/kirby/config/areas/users/dialogs.php @@ -3,12 +3,15 @@ use Kirby\Cms\App; use Kirby\Cms\Find; use Kirby\Cms\UserRules; +use Kirby\Exception\Exception; use Kirby\Exception\InvalidArgumentException; use Kirby\Panel\Field; use Kirby\Panel\Panel; +use Kirby\Panel\UserTotpDisableDialog; use Kirby\Toolkit\Escape; use Kirby\Toolkit\I18n; +$fields = require __DIR__ . '/../fields/dialogs.php'; $files = require __DIR__ . '/../files/dialogs.php'; return [ @@ -18,6 +21,19 @@ return [ 'pattern' => 'users/create', 'load' => function () { $kirby = App::instance(); + $roles = $kirby->roles()->canBeCreated(); + + // get default value for role + if ($role = $kirby->request()->get('role')) { + $role = $roles->find($role)?->id(); + } + + // get role field definition, incl. available role options + $roles = Field::role( + roles: $roles, + props: ['required' => true] + ); + return [ 'component' => 'k-form-dialog', 'props' => [ @@ -27,13 +43,13 @@ return [ 'link' => false, 'required' => true ]), - 'password' => Field::password(), + 'password' => Field::password([ + 'autocomplete' => 'new-password' + ]), 'translation' => Field::translation([ 'required' => true ]), - 'role' => Field::role([ - 'required' => true - ]) + 'role' => $roles ], 'submitButton' => I18n::translate('create'), 'value' => [ @@ -41,7 +57,7 @@ return [ 'email' => '', 'password' => '', 'translation' => $kirby->panelLanguage(), - 'role' => $kirby->user()->role()->name() + 'role' => $role ?? $roles['options'][0]['value'] ?? null ] ] ]; @@ -167,17 +183,23 @@ return [ 'user.changePassword' => [ 'pattern' => 'users/(:any)/changePassword', 'load' => function (string $id) { - $user = Find::user($id); + Find::user($id); return [ 'component' => 'k-form-dialog', 'props' => [ - 'fields' => [ + 'fields' => [ + 'currentPassword' => Field::password([ + 'label' => I18n::translate('user.changePassword.current'), + 'autocomplete' => 'current-password' + ]), 'password' => Field::password([ - 'label' => I18n::translate('user.changePassword.new'), + 'label' => I18n::translate('user.changePassword.new'), + 'autocomplete' => 'new-password' ]), 'passwordConfirmation' => Field::password([ - 'label' => I18n::translate('user.changePassword.new.confirm'), + 'label' => I18n::translate('user.changePassword.new.confirm'), + 'autocomplete' => 'new-password' ]) ], 'submitButton' => I18n::translate('change'), @@ -185,13 +207,26 @@ return [ ]; }, 'submit' => function (string $id) { - $request = App::instance()->request(); + $kirby = App::instance(); + $request = $kirby->request(); $user = Find::user($id); + $currentPassword = $request->get('currentPassword'); $password = $request->get('password'); $passwordConfirmation = $request->get('passwordConfirmation'); - // validate the password + // validate the current password of the acting user + try { + $kirby->user()->validatePassword($currentPassword); + } catch (Exception) { + // catching and re-throwing exception to avoid automatic + // sign-out of current user from the Panel + throw new InvalidArgumentException([ + 'key' => 'user.password.wrong' + ]); + } + + // validate the new password UserRules::validPassword($user, $password ?? ''); // compare passwords @@ -220,10 +255,13 @@ return [ 'component' => 'k-form-dialog', 'props' => [ 'fields' => [ - 'role' => Field::role([ - 'label' => I18n::translate('user.changeRole.select'), - 'required' => true, - ]) + 'role' => Field::role( + roles: $user->roles(), + props: [ + 'label' => I18n::translate('user.changeRole.select'), + 'required' => true, + ] + ) ], 'submitButton' => I18n::translate('user.changeRole'), 'value' => [ @@ -287,6 +325,13 @@ return [ } ], + // user field dialogs + 'user.fields' => [ + 'pattern' => '(users/.*?)/fields/(:any)/(:all?)', + 'load' => $fields['model']['load'], + 'submit' => $fields['model']['submit'] + ], + // change file name 'user.file.changeName' => [ 'pattern' => '(users/.*?)/files/(:any)/changeName', @@ -301,11 +346,31 @@ return [ 'submit' => $files['changeSort']['submit'], ], + // change file template + 'user.file.changeTemplate' => [ + 'pattern' => '(users/.*?)/files/(:any)/changeTemplate', + 'load' => $files['changeTemplate']['load'], + 'submit' => $files['changeTemplate']['submit'], + ], + // delete file 'user.file.delete' => [ 'pattern' => '(users/.*?)/files/(:any)/delete', 'load' => $files['delete']['load'], 'submit' => $files['delete']['submit'], - ] + ], + // user file fields dialogs + 'user.file.fields' => [ + 'pattern' => '(users/.*?)/files/(:any)/fields/(:any)/(:all?)', + 'load' => $fields['file']['load'], + 'submit' => $fields['file']['submit'] + ], + + // user disable TOTP + 'user.totp.disable' => [ + 'pattern' => 'users/(:any)/totp/disable', + 'load' => fn (string $id) => (new UserTotpDisableDialog($id))->load(), + 'submit' => fn (string $id) => (new UserTotpDisableDialog($id))->submit() + ], ]; diff --git a/kirby/config/areas/users/drawers.php b/kirby/config/areas/users/drawers.php new file mode 100644 index 0000000..10d6bd1 --- /dev/null +++ b/kirby/config/areas/users/drawers.php @@ -0,0 +1,18 @@ + [ + 'pattern' => '(users/.*?)/fields/(:any)/(:all?)', + 'load' => $fields['model']['load'], + 'submit' => $fields['model']['submit'] + ], + // user file fields drawers + 'user.file.fields' => [ + 'pattern' => '(users/.*?)/files/(:any)/fields/(:any)/(:all?)', + 'load' => $fields['file']['load'], + 'submit' => $fields['file']['submit'] + ], +]; diff --git a/kirby/config/areas/users/searches.php b/kirby/config/areas/users/searches.php index 25a5702..b4e4a0b 100644 --- a/kirby/config/areas/users/searches.php +++ b/kirby/config/areas/users/searches.php @@ -8,20 +8,22 @@ return [ 'users' => [ 'label' => I18n::translate('users'), 'icon' => 'users', - 'query' => function (string $query = null) { - $users = App::instance()->users()->search($query)->limit(10); - $results = []; + 'query' => function (string|null $query, int $limit, int $page) { + $kirby = App::instance(); + $users = $kirby->users() + ->search($query) + ->paginate($limit, $page); - foreach ($users as $user) { - $results[] = [ + return [ + 'results' => $users->values(fn ($user) => [ 'image' => $user->panel()->image(), 'text' => Escape::html($user->username()), 'link' => $user->panel()->url(true), - 'info' => Escape::html($user->role()->title()) - ]; - } - - return $results; + 'info' => Escape::html($user->role()->title()), + 'uuid' => $user->uuid()->toString(), + ]), + 'pagination' => $users->pagination()->toArray() + ]; } ] ]; diff --git a/kirby/config/areas/users/views.php b/kirby/config/areas/users/views.php index a03a5d6..60d9536 100644 --- a/kirby/config/areas/users/views.php +++ b/kirby/config/areas/users/views.php @@ -18,7 +18,8 @@ return [ return [ 'component' => 'k-users-view', 'props' => [ - 'role' => function () use ($kirby, $roles, $role) { + 'canCreate' => $kirby->roles()->canBeCreated()->count() > 0, + 'role' => function () use ($roles, $role) { if ($role) { return $roles[$role] ?? null; } @@ -31,6 +32,10 @@ return [ $users = $users->role($role); } + // sort users alphabetically + $users = $users->sortBy('username', 'asc'); + + // paginate $users = $users->paginate([ 'limit' => 20, 'page' => $kirby->request()->get('page') diff --git a/kirby/config/blocks/gallery/gallery.yml b/kirby/config/blocks/gallery/gallery.yml index e57379b..3c6aad9 100644 --- a/kirby/config/blocks/gallery/gallery.yml +++ b/kirby/config/blocks/gallery/gallery.yml @@ -8,7 +8,7 @@ fields: query: model.images multiple: true layout: cards - size: tiny + size: small empty: field.blocks.gallery.images.empty uploads: template: blocks/image diff --git a/kirby/config/blocks/heading/heading.yml b/kirby/config/blocks/heading/heading.yml index d7ee4f0..f34fe53 100644 --- a/kirby/config/blocks/heading/heading.yml +++ b/kirby/config/blocks/heading/heading.yml @@ -5,20 +5,31 @@ preview: heading fields: level: label: field.blocks.heading.level - type: select + type: toggles empty: false default: "h2" - width: 1/6 + labels: false options: - - h1 - - h2 - - h3 - - h4 - - h5 - - h6 + - value: h1 + icon: h1 + text: H1 + - value: h2 + icon: h2 + text: H2 + - value: h3 + icon: h3 + text: H3 + - value: h4 + icon: h4 + text: H4 + - value: h5 + icon: h5 + text: H5 + - value: h6 + icon: h6 + text: H6 text: label: field.blocks.heading.text type: writer inline: true - width: 5/6 placeholder: field.blocks.heading.placeholder diff --git a/kirby/config/blocks/image/image.yml b/kirby/config/blocks/image/image.yml index 5909f8b..dc348a5 100644 --- a/kirby/config/blocks/image/image.yml +++ b/kirby/config/blocks/image/image.yml @@ -7,9 +7,10 @@ fields: type: radio columns: 2 default: "kirby" + required: true options: - kirby: Kirby - web: Web + kirby: "{{ t('field.blocks.image.location.internal') }}" + web: "{{ t('field.blocks.image.location.external') }}" image: label: field.blocks.image.name type: files diff --git a/kirby/config/blocks/video/video.php b/kirby/config/blocks/video/video.php index 1808946..3e7ed65 100644 --- a/kirby/config/blocks/video/video.php +++ b/kirby/config/blocks/video/video.php @@ -2,12 +2,31 @@ use Kirby\Cms\Html; /** @var \Kirby\Cms\Block $block */ +$caption = $block->caption(); + +if ( + $block->location() == 'kirby' && + $video = $block->video()->toFile() +) { + $url = $video->url(); + $attrs = array_filter([ + 'autoplay' => $block->autoplay()->toBool(), + 'controls' => $block->controls()->toBool(), + 'loop' => $block->loop()->toBool(), + 'muted' => $block->muted()->toBool() || $block->autoplay()->toBool(), + 'playsinline' => $block->autoplay()->toBool(), + 'poster' => $block->poster()->toFile()?->url(), + 'preload' => $block->preload()->value(), + ]); +} else { + $url = $block->url(); +} ?> -url())): ?> +
- caption()->isNotEmpty()): ?> -
caption() ?>
+ isNotEmpty()): ?> +
diff --git a/kirby/config/blocks/video/video.yml b/kirby/config/blocks/video/video.yml index 6b5223a..b5fc104 100644 --- a/kirby/config/blocks/video/video.yml +++ b/kirby/config/blocks/video/video.yml @@ -2,11 +2,77 @@ name: field.blocks.video.name icon: video preview: video fields: + location: + label: field.blocks.video.location + type: radio + columns: 2 + default: "web" + options: + kirby: "{{ t('field.blocks.image.location.internal') }}" + web: "{{ t('field.blocks.image.location.external') }}" url: label: field.blocks.video.url.label type: url placeholder: field.blocks.video.url.placeholder + when: + location: web + video: + label: field.blocks.video.name + type: files + query: model.videos + multiple: false + # you might want to add a template for videos here + when: + location: kirby + poster: + label: field.blocks.video.poster + type: files + query: model.images + multiple: false + image: + back: black + uploads: + template: blocks/image + when: + location: kirby caption: label: field.blocks.video.caption type: writer inline: true + autoplay: + label: field.blocks.video.autoplay + type: toggle + width: 1/3 + when: + location: kirby + muted: + label: field.blocks.video.muted + type: toggle + width: 1/3 + default: true + when: + location: kirby + loop: + label: field.blocks.video.loop + type: toggle + width: 1/3 + when: + location: kirby + controls: + label: field.blocks.video.controls + type: toggle + width: 1/3 + default: true + when: + location: kirby + preload: + label: field.blocks.video.preload + type: select + width: 2/3 + default: auto + options: + - auto + - metadata + - none + when: + location: kirby diff --git a/kirby/config/blueprints/blocks/code.yml b/kirby/config/blueprints/blocks/code.yml deleted file mode 100644 index a103422..0000000 --- a/kirby/config/blueprints/blocks/code.yml +++ /dev/null @@ -1,56 +0,0 @@ -name: Code -icon: code -fields: - code: - label: Code - type: textarea - buttons: false - font: monospace - language: - label: Language - type: select - default: text - options: - bash: Bash - basic: BASIC - c: C - clojure: Clojure - cpp: C++ - csharp: C# - css: CSS - diff: Diff - elixir: Elixir - elm: Elm - erlang: Erlang - go: Go - graphql: GraphQL - haskell: Haskell - html: HTML - java: Java - js: JavaScript - json: JSON - latext: LaTeX - less: Less - lisp: Lisp - lua: Lua - makefile: Makefile - markdown: Markdown - markup: Markup - objectivec: Objective-C - pascal: Pascal - perl: Perl - php: PHP - text: Plain Text - python: Python - r: R - ruby: Ruby - rust: Rust - sass: Sass - scss: SCSS - shell: Shell - sql: SQL - swift: Swift - typescript: TypeScript - vbnet: VB.net - xml: XML - yaml: YAML diff --git a/kirby/config/blueprints/blocks/heading.yml b/kirby/config/blueprints/blocks/heading.yml deleted file mode 100644 index aa42575..0000000 --- a/kirby/config/blueprints/blocks/heading.yml +++ /dev/null @@ -1,20 +0,0 @@ -icon: title -fields: - text: - type: text - level: - type: select - width: 1/2 - empty: false - default: "2" - options: - - value: "1" - text: Heading 1 - - value: "2" - text: Heading 2 - - value: "3" - text: Heading 3 - id: - type: text - label: ID - width: 1/2 diff --git a/kirby/config/blueprints/blocks/image.yml b/kirby/config/blueprints/blocks/image.yml deleted file mode 100644 index 31b1ef1..0000000 --- a/kirby/config/blueprints/blocks/image.yml +++ /dev/null @@ -1,16 +0,0 @@ -name: Image -icon: image -fields: - image: - type: files - multiple: false - alt: - type: text - icon: title - caption: - type: writer - inline: true - icon: text - link: - type: text - icon: url diff --git a/kirby/config/blueprints/blocks/quote.yml b/kirby/config/blueprints/blocks/quote.yml deleted file mode 100644 index 78bf681..0000000 --- a/kirby/config/blueprints/blocks/quote.yml +++ /dev/null @@ -1,12 +0,0 @@ -name: Quote -icon: quote -fields: - text: - label: Quote Text - type: writer - inline: true - citation: - label: Citation - type: writer - inline: true - placeholder: by … diff --git a/kirby/config/blueprints/blocks/table.yml b/kirby/config/blueprints/blocks/table.yml deleted file mode 100644 index a8513ba..0000000 --- a/kirby/config/blueprints/blocks/table.yml +++ /dev/null @@ -1,25 +0,0 @@ -name: Table -icon: menu -fields: - rows: - label: Menu - type: structure - columns: - dish: true - description: true - price: - before: € - width: 1/4 - align: right - fields: - dish: - label: Dish - type: text - description: - label: Description - type: text - price: - label: Price - type: number - before: € - step: 0.01 diff --git a/kirby/config/blueprints/blocks/text.yml b/kirby/config/blueprints/blocks/text.yml deleted file mode 100644 index c251959..0000000 --- a/kirby/config/blueprints/blocks/text.yml +++ /dev/null @@ -1,5 +0,0 @@ -name: Text -icon: text -fields: - text: - type: writer diff --git a/kirby/config/blueprints/blocks/video.yml b/kirby/config/blueprints/blocks/video.yml deleted file mode 100644 index 9131ace..0000000 --- a/kirby/config/blueprints/blocks/video.yml +++ /dev/null @@ -1,8 +0,0 @@ -name: Video -icon: video -label: "{{ url }}" -fields: - url: - type: url - caption: - type: writer diff --git a/kirby/config/blueprints/files/default.yml b/kirby/config/blueprints/files/default.yml deleted file mode 100644 index d5ef1df..0000000 --- a/kirby/config/blueprints/files/default.yml +++ /dev/null @@ -1,2 +0,0 @@ -name: File -title: file diff --git a/kirby/config/blueprints/pages/default.yml b/kirby/config/blueprints/pages/default.yml deleted file mode 100644 index ceb895a..0000000 --- a/kirby/config/blueprints/pages/default.yml +++ /dev/null @@ -1,3 +0,0 @@ -name: Page -title: Page - diff --git a/kirby/config/blueprints/site.yml b/kirby/config/blueprints/site.yml deleted file mode 100644 index 04718f3..0000000 --- a/kirby/config/blueprints/site.yml +++ /dev/null @@ -1,7 +0,0 @@ -name: Site -title: Site -sections: - pages: - headline: Pages - type: pages - diff --git a/kirby/config/components.php b/kirby/config/components.php index a9fbae5..7e5b9c7 100644 --- a/kirby/config/components.php +++ b/kirby/config/components.php @@ -8,24 +8,26 @@ use Kirby\Cms\Page; use Kirby\Cms\User; use Kirby\Data\Data; use Kirby\Email\PHPMailer as Emailer; +use Kirby\Exception\NotFoundException; use Kirby\Filesystem\F; use Kirby\Filesystem\Filename; use Kirby\Http\Uri; use Kirby\Http\Url; use Kirby\Image\Darkroom; +use Kirby\Session\SessionStore; use Kirby\Template\Snippet; use Kirby\Template\Template; use Kirby\Text\Markdown; use Kirby\Text\SmartyPants; use Kirby\Toolkit\A; use Kirby\Toolkit\Str; +use Kirby\Uuid\Uuid; return [ /** * Used by the `css()` helper * - * @param \Kirby\Cms\App $kirby Kirby instance * @param string $url Relative or absolute URL * @param string|array $options An array of attributes for the link tag or a media attribute string */ @@ -33,35 +35,39 @@ return [ /** * Add your own email provider - * - * @param \Kirby\Cms\App $kirby Kirby instance - * @param array $props - * @param bool $debug */ - 'email' => function (App $kirby, array $props = [], bool $debug = false) { + 'email' => function ( + App $kirby, + array $props = [], + bool $debug = false + ) { return new Emailer($props, $debug); }, /** * Modify URLs for file objects * - * @param \Kirby\Cms\App $kirby Kirby instance * @param \Kirby\Cms\File $file The original file object - * @return string */ - 'file::url' => function (App $kirby, File $file): string { + 'file::url' => function ( + App $kirby, + File $file + ): string { return $file->mediaUrl(); }, /** * Adapt file characteristics * - * @param \Kirby\Cms\App $kirby Kirby instance * @param \Kirby\Cms\File|\Kirby\Filesystem\Asset $file The file object * @param array $options All thumb options (width, height, crop, blur, grayscale) * @return \Kirby\Cms\File|\Kirby\Cms\FileVersion|\Kirby\Filesystem\Asset */ - 'file::version' => function (App $kirby, $file, array $options = []) { + 'file::version' => function ( + App $kirby, + $file, + array $options = [] + ) { // if file is not resizable, return if ($file->isResizable() === false) { return $file; @@ -100,7 +106,6 @@ return [ /** * Used by the `js()` helper * - * @param \Kirby\Cms\App $kirby Kirby instance * @param string $url Relative or absolute URL * @param string|array $options An array of attributes for the link tag or a media attribute string */ @@ -109,14 +114,12 @@ return [ /** * Add your own Markdown parser * - * @param \Kirby\Cms\App $kirby Kirby instance * @param string $text Text to parse * @param array $options Markdown options - * @return string */ 'markdown' => function ( App $kirby, - string $text = null, + string|null $text = null, array $options = [] ): string { static $markdown; @@ -140,9 +143,9 @@ return [ 'search' => function ( App $kirby, Collection $collection, - string|null $query = '', - array|string $params = [] - ): Collection|bool { + string|null $query = null, + string|array $params = [] + ): Collection { if (is_string($params) === true) { $params = ['fields' => Str::split($params, '|')]; } @@ -154,8 +157,9 @@ return [ 'words' => false, ]; - $options = array_merge($defaults, $params); - $query = trim($query ?? ''); + $collection = clone $collection; + $options = array_merge($defaults, $params); + $query = trim($query ?? ''); // empty or too short search query if (Str::length($query) < $options['minlength']) { @@ -227,7 +231,7 @@ return [ $scoring['score'] += 16 * $score; $scoring['hits'] += 1; - // check for exact beginning matches + // check for exact beginning matches } elseif ( $options['words'] === false && Str::startsWith($lowerValue, $query) === true @@ -235,7 +239,7 @@ return [ $scoring['score'] += 8 * $score; $scoring['hits'] += 1; - // check for exact query matches + // check for exact query matches } elseif ($matches = preg_match_all('!' . $exact . '!ui', $value, $r)) { $scoring['score'] += 2 * $score; $scoring['hits'] += $matches; @@ -259,15 +263,24 @@ return [ ); }, + /** + * Add your own session store + */ + 'session::store' => function (App $kirby): string|SessionStore { + return $kirby->root('sessions'); + }, + /** * Add your own SmartyPants parser * - * @param \Kirby\Cms\App $kirby Kirby instance * @param string $text Text to parse * @param array $options SmartyPants options - * @return string */ - 'smartypants' => function (App $kirby, string $text = null, array $options = []): string { + 'smartypants' => function ( + App $kirby, + string|null $text = null, + array $options = [] + ): string { static $smartypants; static $config; @@ -284,43 +297,55 @@ return [ /** * Add your own snippet loader * - * @param \Kirby\Cms\App $kirby Kirby instance * @param string|array $name Snippet name * @param array $data Data array for the snippet */ - 'snippet' => function (App $kirby, string|array|null $name, array $data = [], bool $slots = false): Snippet|string { + 'snippet' => function ( + App $kirby, + string|array|null $name, + array $data = [], + bool $slots = false + ): Snippet|string { return Snippet::factory($name, $data, $slots); }, /** * Add your own template engine * - * @param \Kirby\Cms\App $kirby Kirby instance * @param string $name Template name * @param string $type Extension type * @param string $defaultType Default extension type * @return \Kirby\Template\Template */ - 'template' => function (App $kirby, string $name, string $type = 'html', string $defaultType = 'html') { + 'template' => function ( + App $kirby, + string $name, + string $type = 'html', + string $defaultType = 'html' + ) { return new Template($name, $type, $defaultType); }, /** * Add your own thumb generator * - * @param \Kirby\Cms\App $kirby Kirby instance * @param string $src Root of the original file * @param string $dst Template string for the root to the desired destination * @param array $options All thumb options that should be applied: `width`, `height`, `crop`, `blur`, `grayscale` * @return string */ - 'thumb' => function (App $kirby, string $src, string $dst, array $options): string { + 'thumb' => function ( + App $kirby, + string $src, + string $dst, + array $options + ): string { $darkroom = Darkroom::factory( $kirby->option('thumbs.driver', 'gd'), $kirby->option('thumbs', []) ); - $options = $darkroom->preprocess($src, $options); - $root = (new Filename($src, $dst, $options))->toString(); + $options = $darkroom->preprocess($src, $options); + $root = (new Filename($src, $dst, $options))->toString(); F::copy($src, $root, true); $darkroom->process($root, $options); @@ -331,12 +356,15 @@ return [ /** * Modify all URLs * - * @param \Kirby\Cms\App $kirby Kirby instance * @param string|null $path URL path * @param array|string|null $options Array of options for the Uri class - * @return string + * @throws \Kirby\Exception\NotFoundException If an invalid UUID was passed */ - 'url' => function (App $kirby, string $path = null, $options = null): string { + 'url' => function ( + App $kirby, + string|null $path = null, + $options = null + ): string { $language = null; // get language from simple string option @@ -378,6 +406,23 @@ return [ return $path; } + // support UUIDs + if ( + $path !== null && + ( + Uuid::is($path, 'page') === true || + Uuid::is($path, 'file') === true + ) + ) { + $model = Uuid::for($path)->model(); + + if ($model === null) { + throw new NotFoundException('The model could not be found for "' . $path . '" uuid'); + } + + $path = $model->url(); + } + $url = Url::makeAbsolute($path, $kirby->url()); if ($options === null) { diff --git a/kirby/config/fields/checkboxes.php b/kirby/config/fields/checkboxes.php index c8d962d..00a94b8 100644 --- a/kirby/config/fields/checkboxes.php +++ b/kirby/config/fields/checkboxes.php @@ -29,13 +29,13 @@ return [ /** * Maximum number of checked boxes */ - 'max' => function (int $max = null) { + 'max' => function (int|null $max = null) { return $max; }, /** * Minimum number of checked boxes */ - 'min' => function (int $min = null) { + 'min' => function (int|null $min = null) { return $min; }, 'value' => function ($value = null) { diff --git a/kirby/config/fields/color.php b/kirby/config/fields/color.php new file mode 100644 index 0000000..8d473f1 --- /dev/null +++ b/kirby/config/fields/color.php @@ -0,0 +1,145 @@ + [ + /** + * Unset inherited props + */ + 'after' => null, + 'before' => null, + + /** + * Whether to allow alpha transparency in the color + */ + 'alpha' => function (bool $alpha = false) { + return $alpha; + }, + /** + * The CSS format (hex, rgb, hsl) to display and store the value + */ + 'format' => function (string $format = 'hex'): string { + if (in_array($format, ['hex', 'hsl', 'rgb']) === false) { + throw new InvalidArgumentException('Unsupported format for color field (supported: hex, rgb, hsl)'); + } + + return $format; + }, + /** + * Change mode to disable the color picker (`input`) or to only + * show the `options` as toggles + */ + 'mode' => function (string $mode = 'picker'): string { + if (in_array($mode, ['picker', 'input', 'options']) === false) { + throw new InvalidArgumentException('Unsupported mode for color field (supported: picker, input, options)'); + } + + return $mode; + }, + /** + * List of colors that will be shown as buttons + * to directly select them + */ + 'options' => function (array $options = []): array { + return $options; + } + ], + 'computed' => [ + 'default' => function (): string { + return Str::lower($this->default); + }, + 'options' => function (): array { + // resolve options to support manual arrays + // alongside api and query options + $props = FieldOptions::polyfill($this->props); + $options = FieldOptions::factory([ + 'text' => '{{ item.value }}', + 'value' => '{{ item.key }}', + ...$props['options'] + ]); + + $options = $options->render($this->model()); + + if (empty($options) === true) { + return []; + } + + $options = match (true) { + // simple array of values + // or value=text (from Options class) + is_numeric($options[0]['value']) || + $options[0]['value'] === $options[0]['text'] + => A::map($options, fn ($option) => [ + 'value' => $option['text'] + ]), + + // deprecated: name => value, flipping + // TODO: start throwing in warning in v5 + $this->isColor($options[0]['text']) + => A::map($options, fn ($option) => [ + 'value' => $option['text'], + // ensure that any HTML in the new text is escaped + 'text' => Escape::html($option['value']) + ]), + + default + => A::map($options, fn ($option) => [ + 'value' => $option['value'], + 'text' => $option['text'] + ]), + }; + + return $options; + } + ], + 'methods' => [ + 'isColor' => function (string $value): bool { + return + $this->isHex($value) || + $this->isRgb($value) || + $this->isHsl($value); + }, + 'isHex' => function (string $value): bool { + return preg_match('/^#([\da-f]{3,4}){1,2}$/i', $value) === 1; + }, + 'isHsl' => function (string $value): bool { + return preg_match('/^hsla?\(\s*(\d{1,3}\.?\d*)(deg|rad|grad|turn)?(?:,|\s)+(\d{1,3})%(?:,|\s)+(\d{1,3})%(?:,|\s|\/)*(\d*(?:\.\d+)?)(%?)\s*\)?$/i', $value) === 1; + }, + 'isRgb' => function (string $value): bool { + return preg_match('/^rgba?\(\s*(\d{1,3})(%?)(?:,|\s)+(\d{1,3})(%?)(?:,|\s)+(\d{1,3})(%?)(?:,|\s|\/)*(\d*(?:\.\d+)?)(%?)\s*\)?$/i', $value) === 1; + }, + ], + 'validations' => [ + 'color' => function ($value) { + if (empty($value) === true) { + return true; + } + + if ($this->format === 'hex' && $this->isHex($value) === false) { + throw new InvalidArgumentException([ + 'key' => 'validation.color', + 'data' => ['format' => 'hex'] + ]); + } + + if ($this->format === 'rgb' && $this->isRgb($value) === false) { + throw new InvalidArgumentException([ + 'key' => 'validation.color', + 'data' => ['format' => 'rgb'] + ]); + } + + if ($this->format === 'hsl' && $this->isHsl($value) === false) { + throw new InvalidArgumentException([ + 'key' => 'validation.color', + 'data' => ['format' => 'hsl'] + ]); + } + } + ] +]; diff --git a/kirby/config/fields/date.php b/kirby/config/fields/date.php index 1cfa41c..34559db 100644 --- a/kirby/config/fields/date.php +++ b/kirby/config/fields/date.php @@ -24,7 +24,7 @@ return [ /** * Default date when a new page/file/user gets created */ - 'default' => function (string $default = null): string { + 'default' => function (string|null $default = null): string { return $this->toDatetime($default) ?? ''; }, @@ -46,13 +46,13 @@ return [ /** * Latest date, which can be selected/saved (Y-m-d) */ - 'max' => function (string $max = null): string|null { + 'max' => function (string|null $max = null): string|null { return Date::optional($max); }, /** * Earliest date, which can be selected/saved (Y-m-d) */ - 'min' => function (string $min = null): string|null { + 'min' => function (string|null $min = null): string|null { return Date::optional($min); }, diff --git a/kirby/config/fields/files.php b/kirby/config/fields/files.php index 5e8fb6f..4f37765 100644 --- a/kirby/config/fields/files.php +++ b/kirby/config/fields/files.php @@ -1,5 +1,6 @@ function () { if ( is_string($this->parent) === true && - $model = $this->model()->query($this->parent, 'Kirby\Cms\Model') + $model = $this->model()->query( + $this->parent, + ModelWithContent::class + ) ) { return $model; } diff --git a/kirby/config/fields/headline.php b/kirby/config/fields/headline.php index 01994ad..3a4509e 100644 --- a/kirby/config/fields/headline.php +++ b/kirby/config/fields/headline.php @@ -14,13 +14,6 @@ return [ 'icon' => null, 'placeholder' => null, 'required' => null, - 'translate' => null, - - /** - * If `false`, the prepended number will be hidden - */ - 'numbered' => function (bool $numbered = true) { - return $numbered; - } + 'translate' => null ] ]; diff --git a/kirby/config/fields/hidden.php b/kirby/config/fields/hidden.php index 0b67a5f..4b40df5 100644 --- a/kirby/config/fields/hidden.php +++ b/kirby/config/fields/hidden.php @@ -1,3 +1,5 @@ true +]; diff --git a/kirby/config/fields/info.php b/kirby/config/fields/info.php index 4df8ed3..57907a2 100644 --- a/kirby/config/fields/info.php +++ b/kirby/config/fields/info.php @@ -12,7 +12,6 @@ return [ 'before' => null, 'default' => null, 'disabled' => null, - 'icon' => null, 'placeholder' => null, 'required' => null, 'translate' => null, @@ -27,7 +26,7 @@ return [ /** * Change the design of the info box */ - 'theme' => function (string $theme = null) { + 'theme' => function (string|null $theme = null) { return $theme; } ], diff --git a/kirby/config/fields/link.php b/kirby/config/fields/link.php new file mode 100644 index 0000000..9493c3e --- /dev/null +++ b/kirby/config/fields/link.php @@ -0,0 +1,172 @@ + [ + 'after' => null, + 'before' => null, + 'icon' => null, + 'placeholder' => null, + + /** + * @values 'anchor', 'url, 'page, 'file', 'email', 'tel', 'custom' + */ + 'options' => function (array|null $options = null): array { + // default options + if ($options === null) { + return [ + 'url', + 'page', + 'file', + 'email', + 'tel', + 'anchor' + ]; + } + + // validate options + $available = array_keys($this->availableTypes()); + + if ($unavailable = array_diff($options, $available)) { + throw new InvalidArgumentException([ + 'key' => 'field.link.options', + 'data' => ['options' => implode(', ', $unavailable)] + ]); + } + + return $options; + }, + 'value' => function (string|null $value = null) { + return $value ?? ''; + } + ], + 'methods' => [ + 'activeTypes' => function () { + return array_filter( + $this->availableTypes(), + fn (string $type) => in_array($type, $this->props['options']), + ARRAY_FILTER_USE_KEY + ); + }, + 'availableTypes' => function () { + return [ + 'anchor' => [ + 'detect' => function (string $value): bool { + return Str::startsWith($value, '#') === true; + }, + 'link' => function (string $value): string { + return $value; + }, + 'validate' => function (string $value): bool { + return Str::startsWith($value, '#') === true; + }, + ], + 'email' => [ + 'detect' => function (string $value): bool { + return Str::startsWith($value, 'mailto:') === true; + }, + 'link' => function (string $value): string { + return str_replace('mailto:', '', $value); + }, + 'validate' => function (string $value): bool { + return V::email($value); + }, + ], + 'file' => [ + 'detect' => function (string $value): bool { + return Str::startsWith($value, 'file://') === true; + }, + 'link' => function (string $value): string { + return $value; + }, + 'validate' => function (string $value): bool { + return V::uuid($value, 'file'); + }, + ], + 'page' => [ + 'detect' => function (string $value): bool { + return Str::startsWith($value, 'page://') === true; + }, + 'link' => function (string $value): string { + return $value; + }, + 'validate' => function (string $value): bool { + return V::uuid($value, 'page'); + }, + ], + 'tel' => [ + 'detect' => function (string $value): bool { + return Str::startsWith($value, 'tel:') === true; + }, + 'link' => function (string $value): string { + return str_replace('tel:', '', $value); + }, + 'validate' => function (string $value): bool { + return V::tel($value); + }, + ], + 'url' => [ + 'detect' => function (string $value): bool { + return Str::startsWith($value, 'http://') === true || Str::startsWith($value, 'https://') === true; + }, + 'link' => function (string $value): string { + return $value; + }, + 'validate' => function (string $value): bool { + return V::url($value); + }, + ], + + // needs to come last + 'custom' => [ + 'detect' => function (string $value): bool { + return true; + }, + 'link' => function (string $value): string { + return $value; + }, + 'validate' => function (): bool { + return true; + }, + ] + ]; + }, + ], + 'validations' => [ + 'value' => function (string|null $value) { + if (empty($value) === true) { + return true; + } + + $detected = false; + + foreach ($this->activeTypes() as $type => $options) { + if ($options['detect']($value) !== true) { + continue; + } + + $link = $options['link']($value); + $detected = true; + + if ($options['validate']($link) === false) { + throw new InvalidArgumentException([ + 'key' => 'validation.' . $type + ]); + } + } + + // none of the configured types has been detected + if ($detected === false) { + throw new InvalidArgumentException([ + 'key' => 'validation.linkType' + ]); + } + + return true; + }, + ] +]; diff --git a/kirby/config/fields/list.php b/kirby/config/fields/list.php index 74493a7..d1917f2 100644 --- a/kirby/config/fields/list.php +++ b/kirby/config/fields/list.php @@ -7,6 +7,12 @@ return [ */ 'marks' => function ($marks = true) { return $marks; + }, + /** + * Sets the allowed nodes. Available nodes: `bulletList`, `orderedList` + */ + 'nodes' => function ($nodes = null) { + return $nodes; } ], 'computed' => [ diff --git a/kirby/config/fields/mixins/datetime.php b/kirby/config/fields/mixins/datetime.php index b47a865..8d43d2a 100644 --- a/kirby/config/fields/mixins/datetime.php +++ b/kirby/config/fields/mixins/datetime.php @@ -7,7 +7,7 @@ return [ /** * Defines a custom format that is used when the field is saved */ - 'format' => function (string $format = null) { + 'format' => function (string|null $format = null) { return $format; } ], diff --git a/kirby/config/fields/mixins/layout.php b/kirby/config/fields/mixins/layout.php index 4ac0138..4f94b0f 100644 --- a/kirby/config/fields/mixins/layout.php +++ b/kirby/config/fields/mixins/layout.php @@ -12,7 +12,7 @@ return [ }, /** - * Layout size for cards: `tiny`, `small`, `medium`, `large` or `huge` + * Layout size for cards: `tiny`, `small`, `medium`, `large`, `huge`, `full` */ 'size' => function (string $size = 'auto') { return $size; diff --git a/kirby/config/fields/mixins/options.php b/kirby/config/fields/mixins/options.php index ce8fa92..a3625f3 100644 --- a/kirby/config/fields/mixins/options.php +++ b/kirby/config/fields/mixins/options.php @@ -36,7 +36,7 @@ return [ }, 'sanitizeOption' => function ($value) { $options = array_column($this->options(), 'value'); - return in_array($value, $options, true) === true ? $value : null; + return in_array($value, $options) === true ? $value : null; }, 'sanitizeOptions' => function ($values) { $options = array_column($this->options(), 'value'); diff --git a/kirby/config/fields/mixins/picker.php b/kirby/config/fields/mixins/picker.php index 5d95e45..0e4c5b8 100644 --- a/kirby/config/fields/mixins/picker.php +++ b/kirby/config/fields/mixins/picker.php @@ -2,6 +2,7 @@ use Kirby\Toolkit\I18n; use Kirby\Toolkit\Str; +use Kirby\Uuid\Uuids; return [ 'props' => [ @@ -22,7 +23,7 @@ return [ /** * Info text for each item */ - 'info' => function (string $info = null) { + 'info' => function (string|null $info = null) { return $info; }, @@ -36,14 +37,14 @@ return [ /** * The minimum number of required selected */ - 'min' => function (int $min = null) { + 'min' => function (int|null $min = null) { return $min; }, /** * The maximum number of allowed selected */ - 'max' => function (int $max = null) { + 'max' => function (int|null $max = null) { return $max; }, @@ -57,7 +58,7 @@ return [ /** * Query for the items to be included in the picker */ - 'query' => function (string $query = null) { + 'query' => function (string|null $query = null) { return $query; }, @@ -75,13 +76,17 @@ return [ * @param string $store 'uuid'|'id' */ 'store' => function (string $store = 'uuid') { - return Str::lower($store); + // fall back to ID, if UUIDs globally disabled + return match (Uuids::enabled()) { + false => 'id', + default => Str::lower($store) + }; }, /** * Main text for each item */ - 'text' => function (string $text = null) { + 'text' => function (string|null $text = null) { return $text; }, ], diff --git a/kirby/config/fields/mixins/upload.php b/kirby/config/fields/mixins/upload.php index daa0fef..ec136bd 100644 --- a/kirby/config/fields/mixins/upload.php +++ b/kirby/config/fields/mixins/upload.php @@ -3,6 +3,7 @@ use Kirby\Cms\Api; use Kirby\Cms\File; use Kirby\Exception\Exception; +use Kirby\Exception\InvalidArgumentException; return [ 'props' => [ @@ -22,18 +23,27 @@ return [ $uploads = []; } - $template = $uploads['template'] ?? null; + $uploads['accept'] = '*'; + + if ($preview = $this->image) { + $uploads['preview'] = $preview; + } + + if ($template = $uploads['template'] ?? null) { + // get parent object for upload target + $parent = $this->uploadParent($uploads['parent'] ?? null); + + if ($parent === null) { + throw new InvalidArgumentException('"' . $uploads['parent'] . '" could not be resolved as a valid parent for the upload'); + } - if ($template) { $file = new File([ 'filename' => 'tmp', - 'parent' => $this->model(), + 'parent' => $parent, 'template' => $template ]); - $uploads['accept'] = $file->blueprint()->acceptMime(); - } else { - $uploads['accept'] = '*'; + $uploads['accept'] = $file->blueprint()->acceptAttribute(); } return $uploads; @@ -45,15 +55,7 @@ return [ throw new Exception('Uploads are disabled for this field'); } - if ($parentQuery = ($params['parent'] ?? null)) { - $parent = $this->model()->query($parentQuery); - } else { - $parent = $this->model(); - } - - if ($parent instanceof File) { - $parent = $parent->parent(); - } + $parent = $this->uploadParent($params['parent'] ?? null); return $api->upload(function ($source, $filename) use ($parent, $params, $map) { $props = [ @@ -71,6 +73,19 @@ return [ return $map($file, $parent); }); + }, + 'uploadParent' => function (string|null $parentQuery = null) { + $parent = $this->model(); + + if ($parentQuery) { + $parent = $parent->query($parentQuery); + } + + if ($parent instanceof File) { + $parent = $parent->parent(); + } + + return $parent; } ] ]; diff --git a/kirby/config/fields/multiselect.php b/kirby/config/fields/multiselect.php index 2c77c00..6633ee3 100644 --- a/kirby/config/fields/multiselect.php +++ b/kirby/config/fields/multiselect.php @@ -1,35 +1,23 @@ 'tags', 'props' => [ /** - * Unset inherited props + * If set to `all`, any type of input is accepted. If set to `options` only the predefined options are accepted as input. */ - 'accept' => null, + 'accept' => function ($value = 'options') { + return V::in($value, ['all', 'options']) ? $value : 'all'; + }, /** * Custom icon to replace the arrow down. */ - 'icon' => function (string $icon = null) { + 'icon' => function (string $icon = 'checklist') { return $icon; }, - /** - * Enable/disable the search in the dropdown - * Also limit displayed items (display: 20) - * and set minimum number of characters to search (min: 3) - */ - 'search' => function ($search = true) { - return $search; - }, - /** - * If `true`, selected entries will be sorted - * according to their position in the dropdown - */ - 'sort' => function (bool $sort = false) { - return $sort; - }, ], 'methods' => [ 'toValues' => function ($value) { diff --git a/kirby/config/fields/number.php b/kirby/config/fields/number.php index 11334e8..cc41887 100644 --- a/kirby/config/fields/number.php +++ b/kirby/config/fields/number.php @@ -13,13 +13,13 @@ return [ /** * The lowest allowed number */ - 'min' => function (float $min = null) { + 'min' => function (float|null $min = null) { return $min; }, /** * The highest allowed number */ - 'max' => function (float $max = null) { + 'max' => function (float|null $max = null) { return $max; }, /** diff --git a/kirby/config/fields/object.php b/kirby/config/fields/object.php index cae125a..57cd2e4 100644 --- a/kirby/config/fields/object.php +++ b/kirby/config/fields/object.php @@ -47,7 +47,7 @@ return [ }, 'fields' => function () { if (empty($this->fields) === true) { - throw new Exception('Please provide some fields for the object'); + return []; } return $this->form()->fields()->toArray(); diff --git a/kirby/config/fields/pages.php b/kirby/config/fields/pages.php index 54d9aa8..3d9d56d 100644 --- a/kirby/config/fields/pages.php +++ b/kirby/config/fields/pages.php @@ -31,7 +31,7 @@ return [ /** * Optional query to select a specific set of pages */ - 'query' => function (string $query = null) { + 'query' => function (string|null $query = null) { return $query; }, diff --git a/kirby/config/fields/range.php b/kirby/config/fields/range.php index 04221f1..d203a7e 100644 --- a/kirby/config/fields/range.php +++ b/kirby/config/fields/range.php @@ -1,5 +1,7 @@ 'number', 'props' => [ @@ -18,6 +20,13 @@ return [ * Enables/disables the tooltip and set the before and after values */ 'tooltip' => function ($tooltip = true) { + if (is_array($tooltip) === true) { + $after = $tooltip['after'] ?? null; + $before = $tooltip['before'] ?? null; + $tooltip['after'] = I18n::translate($after, $after); + $tooltip['before'] = I18n::translate($before, $before); + } + return $tooltip; }, ] diff --git a/kirby/config/fields/select.php b/kirby/config/fields/select.php index 4c06b5b..2f09c2d 100644 --- a/kirby/config/fields/select.php +++ b/kirby/config/fields/select.php @@ -13,7 +13,7 @@ return [ /** * Custom icon to replace the arrow down. */ - 'icon' => function (string $icon = null) { + 'icon' => function (string|null $icon = null) { return $icon; }, /** diff --git a/kirby/config/fields/slug.php b/kirby/config/fields/slug.php index 9d8efb5..15c6839 100644 --- a/kirby/config/fields/slug.php +++ b/kirby/config/fields/slug.php @@ -28,7 +28,7 @@ return [ /** * Set prefix for the help text */ - 'path' => function (string $path = null) { + 'path' => function (string|null $path = null) { return $path; }, @@ -36,7 +36,7 @@ return [ * Name of another field that should be used to * automatically update this field's value */ - 'sync' => function (string $sync = null) { + 'sync' => function (string|null $sync = null) { return $sync; }, diff --git a/kirby/config/fields/structure.php b/kirby/config/fields/structure.php index 5f7f77f..66b614c 100644 --- a/kirby/config/fields/structure.php +++ b/kirby/config/fields/structure.php @@ -1,8 +1,11 @@ ['min'], @@ -42,51 +45,51 @@ return [ /** * Set the default rows for the structure */ - 'default' => function (array $default = null) { + 'default' => function (array|null $default = null) { return $default; }, /** * Fields setup for the structure form. Works just like fields in regular forms. */ - 'fields' => function (array $fields) { + 'fields' => function (array $fields = []) { return $fields; }, /** * The number of entries that will be displayed on a single page. Afterwards pagination kicks in. */ - 'limit' => function (int $limit = null) { + 'limit' => function (int|null $limit = null) { return $limit; }, /** * Maximum allowed entries in the structure. Afterwards the "Add" button will be switched off. */ - 'max' => function (int $max = null) { + 'max' => function (int|null $max = null) { return $max; }, /** * Minimum required entries in the structure */ - 'min' => function (int $min = null) { + 'min' => function (int|null $min = null) { return $min; }, /** * Toggles adding to the top or bottom of the list */ - 'prepend' => function (bool $prepend = null) { + 'prepend' => function (bool|null $prepend = null) { return $prepend; }, /** * Toggles drag & drop sorting */ - 'sortable' => function (bool $sortable = null) { + 'sortable' => function (bool|null $sortable = null) { return $sortable; }, /** * Sorts the entries by the given field and order (i.e. `title desc`) * Drag & drop is disabled in this case */ - 'sortBy' => function (string $sort = null) { + 'sortBy' => function (string|null $sort = null) { return $sort; } ], @@ -99,57 +102,54 @@ return [ }, 'fields' => function () { if (empty($this->fields) === true) { - throw new Exception('Please provide some fields for the structure'); + return []; } return $this->form()->fields()->toArray(); }, 'columns' => function () { - $columns = []; - $mobile = 0; + $columns = []; + $blueprint = $this->columns; - if (empty($this->columns) === true) { - foreach ($this->fields as $field) { - // Skip hidden and unsaveable fields - // They should never be included as column - if ($field['type'] === 'hidden' || $field['saveable'] === false) { - continue; - } + // if no custom columns have been defined, + // gather all fields as columns + if (empty($blueprint) === true) { + // skip hidden fields + $fields = array_filter( + $this->fields, + fn ($field) => + $field['type'] !== 'hidden' && $field['hidden'] !== true + ); + $fields = array_column($fields, 'name'); + $blueprint = array_fill_keys($fields, true); + } - $columns[$field['name']] = [ - 'type' => $field['type'], - 'label' => $field['label'] ?? $field['name'] - ]; + foreach ($blueprint as $name => $column) { + $field = $this->fields[$name] ?? null; + + // Skip empty and unsaveable fields + // They should never be included as column + if ( + empty($field) === true || + $field['saveable'] === false + ) { + continue; } - } else { - foreach ($this->columns as $columnName => $columnProps) { - if (is_array($columnProps) === false) { - $columnProps = []; - } - $field = $this->fields[$columnName] ?? null; - - if ( - empty($field) === true || - $field['saveable'] === false - ) { - continue; - } - - if (($columnProps['mobile'] ?? false) === true) { - $mobile++; - } - - $columns[$columnName] = array_merge([ - 'type' => $field['type'], - 'label' => $field['label'] ?? $field['name'] - ], $columnProps); + if (is_array($column) === false) { + $column = []; } + + $column['type'] ??= $field['type']; + $column['label'] ??= $field['label'] ?? $name; + $column['label'] = I18n::translate($column['label'], $column['label']); + + $columns[$name] = $column; } // make the first column visible on mobile // if no other mobile columns are defined - if ($mobile === 0) { + if (in_array(true, array_column($columns, 'mobile')) === false) { $columns[array_key_first($columns)]['mobile'] = true; } @@ -173,34 +173,53 @@ return [ }, 'form' => function (array $values = []) { return new Form([ - 'fields' => $this->attrs['fields'], + 'fields' => $this->attrs['fields'] ?? [], 'values' => $values, 'model' => $this->model ]); }, ], - 'api' => function () { - return [ - [ - 'pattern' => 'validate', - 'method' => 'ALL', - 'action' => function () { - return array_values($this->field()->form($this->requestBody())->errors()); - } - ] - ]; - }, 'save' => function ($value) { $data = []; foreach ($value as $row) { - $data[] = $this->form($row)->content(); + $row = $this->form($row)->content(); + + // remove frontend helper id + unset($row['_id']); + + $data[] = $row; } return $data; }, 'validations' => [ 'min', - 'max' + 'max', + 'structure' => function ($value) { + if (empty($value) === true) { + return true; + } + + $values = A::wrap($value); + + foreach ($values as $index => $value) { + $form = $this->form($value); + + foreach ($form->fields() as $field) { + $errors = $field->errors(); + + if (empty($errors) === false) { + throw new InvalidArgumentException([ + 'key' => 'structure.validation', + 'data' => [ + 'field' => $field->label() ?? Str::ucfirst($field->name()), + 'index' => $index + 1 + ] + ]); + } + } + } + } ] ]; diff --git a/kirby/config/fields/tags.php b/kirby/config/fields/tags.php index afbe1f0..90020bc 100644 --- a/kirby/config/fields/tags.php +++ b/kirby/config/fields/tags.php @@ -37,21 +37,36 @@ return [ /** * Minimum number of required entries/tags */ - 'min' => function (int $min = null) { + 'min' => function (int|null $min = null) { return $min; }, /** * Maximum number of allowed entries/tags */ - 'max' => function (int $max = null) { + 'max' => function (int|null $max = null) { return $max; }, + /** + * Enable/disable the search in the dropdown + * Also limit displayed items (display: 20) + * and set minimum number of characters to search (min: 3) + */ + 'search' => function (bool|array $search = true) { + return $search; + }, /** * Custom tags separator, which will be used to store tags in the content file */ 'separator' => function (string $separator = ',') { return $separator; }, + /** + * If `true`, selected entries will be sorted + * according to their position in the dropdown + */ + 'sort' => function (bool $sort = false) { + return $sort; + }, ], 'computed' => [ 'default' => function (): array { @@ -78,7 +93,7 @@ return [ return $value; } ], - 'save' => function (array $value = null): string { + 'save' => function (array|null $value = null): string { return A::join( $value, $this->separator() . ' ' diff --git a/kirby/config/fields/text.php b/kirby/config/fields/text.php index 0585af2..ac87d85 100644 --- a/kirby/config/fields/text.php +++ b/kirby/config/fields/text.php @@ -27,24 +27,31 @@ return [ return $counter; }, + /** + * Sets the font family (sans or monospace) + */ + 'font' => function (string|null $font = null) { + return $font === 'monospace' ? 'monospace' : 'sans-serif'; + }, + /** * Maximum number of allowed characters */ - 'maxlength' => function (int $maxlength = null) { + 'maxlength' => function (int|null $maxlength = null) { return $maxlength; }, /** * Minimum number of required characters */ - 'minlength' => function (int $minlength = null) { + 'minlength' => function (int|null $minlength = null) { return $minlength; }, /** * A regular expression, which will be used to validate the input */ - 'pattern' => function (string $pattern = null) { + 'pattern' => function (string|null $pattern = null) { return $pattern; }, diff --git a/kirby/config/fields/textarea.php b/kirby/config/fields/textarea.php index 7b51c1f..e09d6c1 100644 --- a/kirby/config/fields/textarea.php +++ b/kirby/config/fields/textarea.php @@ -26,7 +26,7 @@ return [ /** * Sets the default text when a new page/file/user is created */ - 'default' => function (string $default = null) { + 'default' => function (string|null $default = null) { return trim($default ?? ''); }, @@ -48,28 +48,28 @@ return [ /** * Sets the font family (sans or monospace) */ - 'font' => function (string $font = null) { + 'font' => function (string|null $font = null) { return $font === 'monospace' ? 'monospace' : 'sans-serif'; }, /** * Maximum number of allowed characters */ - 'maxlength' => function (int $maxlength = null) { + 'maxlength' => function (int|null $maxlength = null) { return $maxlength; }, /** * Minimum number of required characters */ - 'minlength' => function (int $minlength = null) { + 'minlength' => function (int|null $minlength = null) { return $minlength; }, /** * Changes the size of the textarea. Available sizes: `small`, `medium`, `large`, `huge` */ - 'size' => function (string $size = null) { + 'size' => function (string|null $size = null) { return $size; }, @@ -80,7 +80,7 @@ return [ return $spellcheck; }, - 'value' => function (string $value = null) { + 'value' => function (string|null $value = null) { return trim($value ?? ''); } ], diff --git a/kirby/config/fields/time.php b/kirby/config/fields/time.php index 4a0b6fc..413420b 100644 --- a/kirby/config/fields/time.php +++ b/kirby/config/fields/time.php @@ -36,13 +36,13 @@ return [ /** * Latest time, which can be selected/saved (H:i or H:i:s) */ - 'max' => function (string $max = null): string|null { + 'max' => function (string|null $max = null): string|null { return Date::optional($max); }, /** * Earliest time, which can be selected/saved (H:i or H:i:s) */ - 'min' => function (string $min = null): string|null { + 'min' => function (string|null $min = null): string|null { return Date::optional($min); }, diff --git a/kirby/config/fields/users.php b/kirby/config/fields/users.php index b962a36..f30f6ab 100644 --- a/kirby/config/fields/users.php +++ b/kirby/config/fields/users.php @@ -24,18 +24,8 @@ return [ /** * Default selected user(s) when a new page/file/user is created */ - 'default' => function ($default = null) { - if ($default === false) { - return []; - } - - if ($default === null && $user = $this->kirby()->user()) { - return [ - $this->userResponse($user) - ]; - } - - return $this->toUsers($default); + 'default' => function (string|array|bool|null $default = null) { + return $default; }, 'value' => function ($value = null) { @@ -43,10 +33,22 @@ return [ }, ], 'computed' => [ - /** - * Unset inherited computed - */ - 'default' => null + 'default' => function (): array { + if ($this->default === false) { + return []; + } + + if ( + $this->default === true && + $user = $this->kirby()->user() + ) { + return [ + $this->userResponse($user) + ]; + } + + return $this->toUsers($this->default); + } ], 'methods' => [ 'userResponse' => function ($user) { @@ -57,7 +59,7 @@ return [ 'text' => $this->text, ]); }, - 'toUsers' => function ($value = null) { + 'toUsers' => function ($value = null): array { $users = []; $kirby = App::instance(); diff --git a/kirby/config/fields/writer.php b/kirby/config/fields/writer.php index 73c4976..72440ff 100644 --- a/kirby/config/fields/writer.php +++ b/kirby/config/fields/writer.php @@ -1,9 +1,23 @@ [ + /** + * Enables/disables the character counter in the top right corner + */ + 'counter' => function (bool $counter = true) { + return $counter; + }, + /** + * Available heading levels + */ + 'headings' => function (array|null $headings = null) { + return array_intersect($headings ?? range(1, 6), range(1, 6)); + }, /** * Enables inline mode, which will not wrap new lines in paragraphs and creates hard breaks instead. * @@ -13,24 +27,74 @@ return [ return $inline; }, /** - * Sets the allowed HTML formats. Available formats: `bold`, `italic`, `underline`, `strike`, `code`, `link`, `email`. Activate them all by passing `true`. Deactivate them all by passing `false` + * Sets the allowed HTML formats. Available formats: `bold`, `italic`, `underline`, `strike`, `code`, `link`, `email`. Activate/deactivate them all by passing `true`/`false`. Default marks are `bold`, `italic`, `underline`, `strike`, `link`, `email` * @param array|bool $marks */ - 'marks' => function ($marks = true) { + 'marks' => function ($marks = null) { return $marks; }, /** - * Sets the allowed nodes. Available nodes: `paragraph`, `heading`, `bulletList`, `orderedList`. Activate/deactivate them all by passing `true`/`false`. Default nodes are `paragraph`, `heading`, `bulletList`, `orderedList`. + * Maximum number of allowed characters + */ + 'maxlength' => function (int|null $maxlength = null) { + return $maxlength; + }, + + /** + * Minimum number of required characters + */ + 'minlength' => function (int|null $minlength = null) { + return $minlength; + }, + /** + * Sets the allowed nodes. Available nodes: `paragraph`, `heading`, `bulletList`, `orderedList`, `quote`. Activate/deactivate them all by passing `true`/`false`. Default nodes are `paragraph`, `heading`, `bulletList`, `orderedList`. * @param array|bool|null $nodes */ 'nodes' => function ($nodes = null) { return $nodes; + }, + /** + * Toolbar options, incl. `marks` (to narrow down which marks should have toolbar buttons), `nodes` (to narrow down which nodes should have toolbar dropdown entries) and `inline` to set the position of the toolbar (false = sticking on top of the field) + */ + 'toolbar' => function ($toolbar = null) { + return $toolbar; } ], 'computed' => [ 'value' => function () { $value = trim($this->value ?? ''); - return Sane::sanitize($value, 'html'); + $value = Sane::sanitize($value, 'html'); + + // convert non-breaking spaces to HTML entity + // as that's how ProseMirror handles it internally; + // will allow comparing saved and current content + $value = str_replace(' ', ' ', $value); + + return $value; } ], + 'validations' => [ + 'minlength' => function ($value) { + if ( + $this->minlength && + V::minLength(strip_tags($value), $this->minlength) === false + ) { + throw new InvalidArgumentException([ + 'key' => 'validation.minlength', + 'data' => ['min' => $this->minlength] + ]); + } + }, + 'maxlength' => function ($value) { + if ( + $this->maxlength && + V::maxLength(strip_tags($value), $this->maxlength) === false + ) { + throw new InvalidArgumentException([ + 'key' => 'validation.maxlength', + 'data' => ['max' => $this->maxlength] + ]); + } + }, + ] ]; diff --git a/kirby/config/helpers.php b/kirby/config/helpers.php index a98b8a5..79e13dd 100644 --- a/kirby/config/helpers.php +++ b/kirby/config/helpers.php @@ -4,14 +4,18 @@ use Kirby\Cms\App; use Kirby\Cms\File; use Kirby\Cms\Helpers; use Kirby\Cms\Html; +use Kirby\Cms\ModelWithContent; use Kirby\Cms\Page; use Kirby\Cms\Pages; +use Kirby\Cms\Plugin; +use Kirby\Cms\PluginAssets; use Kirby\Cms\Response; use Kirby\Cms\Site; use Kirby\Cms\Url; use Kirby\Filesystem\Asset; use Kirby\Filesystem\F; use Kirby\Http\Router; +use Kirby\Image\QrCode; use Kirby\Template\Slot; use Kirby\Template\Snippet; use Kirby\Toolkit\Date; @@ -87,7 +91,7 @@ if (Helpers::hasOverride('css') === false) { // @codeCoverageIgnore * @param string|array|null $options Pass an array of attributes for the link tag or a media attribute string */ function css( - string|array $url, + string|array|Plugin|PluginAssets $url, string|array|null $options = null ): string|null { return Html::css($url, $options); @@ -107,7 +111,7 @@ if (Helpers::hasOverride('deprecated') === false) { // @codeCoverageIgnore } } -if (Helpers::hasOverride('dump') === false) { // @codeCoverageIgnore +if (Helpers::hasOverride('dump') === false && function_exists('dump') === false) { // @codeCoverageIgnore /** * Simple object and variable dumper * to help with debugging. @@ -258,7 +262,7 @@ if (Helpers::hasOverride('js') === false) { // @codeCoverageIgnore * Creates a script tag to load a javascript file */ function js( - string|array $url, + string|array|Plugin|PluginAssets $url, string|array|bool|null $options = null ): string|null { return Html::js($url, $options); @@ -432,6 +436,20 @@ if (Helpers::hasOverride('params') === false) { // @codeCoverageIgnore } } +if (Helpers::hasOverride('qr') === false) { // @codeCoverageIgnore + /** + * Creates a QR code object + */ + function qr(string|ModelWithContent $data): QrCode + { + if ($data instanceof ModelWithContent) { + $data = $data->url(); + } + + return new QrCode($data); + } +} + if (Helpers::hasOverride('r') === false) { // @codeCoverageIgnore /** * Smart version of return with an if condition as first argument @@ -586,25 +604,6 @@ if (Helpers::hasOverride('tt') === false) { // @codeCoverageIgnore } } -if (Helpers::hasOverride('twitter') === false) { // @codeCoverageIgnore - /** - * Builds a Twitter link - */ - function twitter( - string $username, - string|null $text = null, - string|null $title = null, - string|null $class = null - ): string { - return App::instance()->kirbytag([ - 'twitter' => $username, - 'text' => $text, - 'title' => $title, - 'class' => $class - ]); - } -} - if (Helpers::hasOverride('u') === false) { // @codeCoverageIgnore /** * Shortcut for url() @@ -645,8 +644,11 @@ if (Helpers::hasOverride('video') === false) { // @codeCoverageIgnore * videos. The embed Urls are automatically detected from * the given Url. */ - function video(string $url, array $options = [], array $attr = []): string|null - { + function video( + string $url, + array $options = [], + array $attr = [] + ): string|null { return Html::video($url, $options, $attr); } } @@ -655,8 +657,11 @@ if (Helpers::hasOverride('vimeo') === false) { // @codeCoverageIgnore /** * Embeds a Vimeo video by URL in an iframe */ - function vimeo(string $url, array $options = [], array $attr = []): string|null - { + function vimeo( + string $url, + array $options = [], + array $attr = [] + ): string|null { return Html::vimeo($url, $options, $attr); } } @@ -677,8 +682,11 @@ if (Helpers::hasOverride('youtube') === false) { // @codeCoverageIgnore /** * Embeds a Youtube video by URL in an iframe */ - function youtube(string $url, array $options = [], array $attr = []): string|null - { + function youtube( + string $url, + array $options = [], + array $attr = [] + ): string|null { return Html::youtube($url, $options, $attr); } } diff --git a/kirby/config/methods.php b/kirby/config/methods.php index a81780e..760ab24 100644 --- a/kirby/config/methods.php +++ b/kirby/config/methods.php @@ -2,19 +2,29 @@ use Kirby\Cms\App; use Kirby\Cms\Blocks; -use Kirby\Cms\Content; -use Kirby\Cms\Field; +use Kirby\Cms\File; use Kirby\Cms\Files; use Kirby\Cms\Html; use Kirby\Cms\Layouts; +use Kirby\Cms\Page; +use Kirby\Cms\Pages; use Kirby\Cms\Structure; use Kirby\Cms\Url; +use Kirby\Cms\User; +use Kirby\Cms\Users; +use Kirby\Content\Content; +use Kirby\Content\Field; use Kirby\Data\Data; use Kirby\Exception\Exception; use Kirby\Exception\InvalidArgumentException; +use Kirby\Exception\NotFoundException; +use Kirby\Image\QrCode; +use Kirby\Toolkit\A; +use Kirby\Toolkit\Dom; use Kirby\Toolkit\Str; use Kirby\Toolkit\V; use Kirby\Toolkit\Xml; +use Kirby\Uuid\Uuid; /** * Field method setup @@ -26,9 +36,6 @@ return function (App $app) { /** * Converts the field value into a proper boolean and inverts it - * - * @param \Kirby\Cms\Field $field - * @return bool */ 'isFalse' => function (Field $field): bool { return $field->toBool() === false; @@ -36,9 +43,6 @@ return function (App $app) { /** * Converts the field value into a proper boolean - * - * @param \Kirby\Cms\Field $field - * @return bool */ 'isTrue' => function (Field $field): bool { return $field->toBool() === true; @@ -47,22 +51,21 @@ return function (App $app) { /** * Validates the field content with the given validator and parameters * - * @param string $validator * @param mixed ...$arguments A list of optional validator arguments - * @return bool */ - 'isValid' => function (Field $field, string $validator, ...$arguments): bool { + 'isValid' => function ( + Field $field, + string $validator, + ...$arguments + ): bool { return V::$validator($field->value, ...$arguments); }, // converters /** * Converts a yaml or json field to a Blocks object - * - * @param \Kirby\Cms\Field $field - * @return \Kirby\Cms\Blocks */ - 'toBlocks' => function (Field $field) { + 'toBlocks' => function (Field $field): Blocks { try { $blocks = Blocks::parse($field->value()); $blocks = Blocks::factory($blocks, [ @@ -84,11 +87,9 @@ return function (App $app) { /** * Converts the field value into a proper boolean * - * @param \Kirby\Cms\Field $field * @param bool $default Default value if the field is empty - * @return bool */ - 'toBool' => function (Field $field, $default = false): bool { + 'toBool' => function (Field $field, bool $default = false): bool { $value = $field->isEmpty() ? $default : $field->value; return filter_var($value, FILTER_VALIDATE_BOOLEAN); }, @@ -96,11 +97,9 @@ return function (App $app) { /** * Parses the field value with the given method * - * @param \Kirby\Cms\Field $field * @param string $method [',', 'yaml', 'json'] - * @return array */ - 'toData' => function (Field $field, string $method = ',') { + 'toData' => function (Field $field, string $method = ','): array { return match ($method) { 'yaml', 'json' => Data::decode($field->value, $method), default => $field->split($method) @@ -110,12 +109,14 @@ return function (App $app) { /** * Converts the field value to a timestamp or a formatted date * - * @param \Kirby\Cms\Field $field * @param string|\IntlDateFormatter|null $format PHP date formatting string - * @param string|null $fallback Fallback string for `strtotime` (since 3.2) - * @return string|int + * @param string|null $fallback Fallback string for `strtotime` */ - 'toDate' => function (Field $field, $format = null, string $fallback = null) use ($app) { + 'toDate' => function ( + Field $field, + string|IntlDateFormatter|null $format = null, + string|null $fallback = null + ) use ($app): string|int|null { if (empty($field->value) === true && $fallback === null) { return null; } @@ -126,33 +127,28 @@ return function (App $app) { $time = strtotime($fallback); } - $handler = $app->option('date.handler', 'date'); - return Str::date($time, $format, $handler); + return Str::date($time, $format); }, /** * Returns a file object from a filename in the field - * - * @param \Kirby\Cms\Field $field - * @return \Kirby\Cms\File|null */ - 'toFile' => function (Field $field) { + 'toFile' => function (Field $field): File|null { return $field->toFiles()->first(); }, /** * Returns a file collection from a yaml list of filenames in the field - * - * @param \Kirby\Cms\Field $field - * @param string $separator - * @return \Kirby\Cms\Files */ - 'toFiles' => function (Field $field, string $separator = 'yaml') { + 'toFiles' => function ( + Field $field, + string $separator = 'yaml' + ): Files { $parent = $field->parent(); $files = new Files([]); foreach ($field->toData($separator) as $id) { - if ($file = $parent->kirby()->file($id, $parent)) { + if (is_string($id) === true && $file = $parent->kirby()->file($id, $parent)) { $files->add($file); } } @@ -163,11 +159,9 @@ return function (App $app) { /** * Converts the field value into a proper float * - * @param \Kirby\Cms\Field $field * @param float $default Default value if the field is empty - * @return float */ - 'toFloat' => function (Field $field, float $default = 0) { + 'toFloat' => function (Field $field, float $default = 0): float { $value = $field->isEmpty() ? $default : $field->value; return (float)$value; }, @@ -175,23 +169,17 @@ return function (App $app) { /** * Converts the field value into a proper integer * - * @param \Kirby\Cms\Field $field * @param int $default Default value if the field is empty - * @return int */ - 'toInt' => function (Field $field, int $default = 0) { + 'toInt' => function (Field $field, int $default = 0): int { $value = $field->isEmpty() ? $default : $field->value; return (int)$value; }, /** - * Parse layouts and turn them into - * Layout objects - * - * @param \Kirby\Cms\Field $field - * @return \Kirby\Cms\Layouts + * Parse layouts and turn them into Layout objects */ - 'toLayouts' => function (Field $field) { + 'toLayouts' => function (Field $field): Layouts { return Layouts::factory(Layouts::parse($field->value()), [ 'parent' => $field->parent(), 'field' => $field, @@ -201,12 +189,14 @@ return function (App $app) { /** * Wraps a link tag around the field value. The field value is used as the link text * - * @param \Kirby\Cms\Field $field * @param mixed $attr1 Can be an optional Url. If no Url is set, the Url of the Page, File or Site will be used. Can also be an array of link attributes * @param mixed $attr2 If `$attr1` is used to set the Url, you can use `$attr2` to pass an array of additional attributes. - * @return string */ - 'toLink' => function (Field $field, $attr1 = null, $attr2 = null) { + 'toLink' => function ( + Field $field, + string|array|null $attr1 = null, + array|null $attr2 = null + ): string { if (is_string($attr1) === true) { $href = $attr1; $attr = $attr2; @@ -225,49 +215,55 @@ return function (App $app) { /** * Parse yaml data and convert it to a * content object - * - * @param \Kirby\Cms\Field $field - * @return \Kirby\Cms\Content */ - 'toObject' => function (Field $field) { + 'toObject' => function (Field $field): Content { return new Content($field->yaml(), $field->parent(), true); }, /** * Returns a page object from a page id in the field - * - * @param \Kirby\Cms\Field $field - * @return \Kirby\Cms\Page|null */ - 'toPage' => function (Field $field) { + 'toPage' => function (Field $field): Page|null { return $field->toPages()->first(); }, /** * Returns a pages collection from a yaml list of page ids in the field * - * @param \Kirby\Cms\Field $field * @param string $separator Can be any other separator to split the field value by - * @return \Kirby\Cms\Pages */ - 'toPages' => function (Field $field, string $separator = 'yaml') use ($app) { - return $app->site()->find(false, false, ...$field->toData($separator)); + 'toPages' => function ( + Field $field, + string $separator = 'yaml' + ) use ($app): Pages { + return $app->site()->find( + false, + false, + ...$field->toData($separator) + ); + }, + + /** + * Turns the field value into an QR code object + */ + 'toQrCode' => function (Field $field): QrCode|null { + return $field->isNotEmpty() ? new QrCode($field->value) : null; }, /** * Converts a yaml field to a Structure object - * - * @param \Kirby\Cms\Field $field - * @return \Kirby\Cms\Structure */ - 'toStructure' => function (Field $field) { + 'toStructure' => function (Field $field): Structure { try { - return new Structure(Data::decode($field->value, 'yaml'), $field->parent()); + return Structure::factory( + Data::decode($field->value, 'yaml'), + ['parent' => $field->parent(), 'field' => $field] + ); } catch (Exception) { $message = 'Invalid structure data for "' . $field->key() . '" field'; if ($parent = $field->parent()) { - $message .= ' on parent "' . $parent->title() . '"'; + $message .= ' on parent "' . $parent->id() . '"'; } throw new InvalidArgumentException($message); @@ -276,9 +272,6 @@ return function (App $app) { /** * Converts the field value to a Unix timestamp - * - * @param \Kirby\Cms\Field $field - * @return int|false */ 'toTimestamp' => function (Field $field): int|false { return strtotime($field->value ?? ''); @@ -286,33 +279,35 @@ return function (App $app) { /** * Turns the field value into an absolute Url - * - * @param \Kirby\Cms\Field $field - * @return string */ - 'toUrl' => function (Field $field): string { - return Url::to($field->value); + 'toUrl' => function (Field $field): string|null { + try { + return $field->isNotEmpty() ? Url::to($field->value) : null; + } catch (NotFoundException) { + return null; + } }, /** * Converts a user email address to a user object - * - * @param \Kirby\Cms\Field $field - * @return \Kirby\Cms\User|null */ - 'toUser' => function (Field $field) { + 'toUser' => function (Field $field): User|null { return $field->toUsers()->first(); }, /** - * Returns a users collection from a yaml list of user email addresses in the field - * - * @param \Kirby\Cms\Field $field - * @param string $separator - * @return \Kirby\Cms\Users + * Returns a users collection from a yaml list + * of user email addresses in the field */ - 'toUsers' => function (Field $field, string $separator = 'yaml') use ($app) { - return $app->users()->find(false, false, ...$field->toData($separator)); + 'toUsers' => function ( + Field $field, + string $separator = 'yaml' + ) use ($app): Users { + return $app->users()->find( + false, + false, + ...$field->toData($separator) + ); }, // inspectors @@ -320,14 +315,14 @@ return function (App $app) { /** * Returns the length of the field content */ - 'length' => function (Field $field) { + 'length' => function (Field $field): int { return Str::length($field->value); }, /** * Returns the number of words in the text */ - 'words' => function (Field $field) { + 'words' => function (Field $field): int { return str_word_count(strip_tags($field->value ?? '')); }, @@ -336,11 +331,8 @@ return function (App $app) { /** * Applies the callback function to the field * @since 3.4.0 - * - * @param \Kirby\Cms\Field $field - * @param Closure $callback */ - 'callback' => function (Field $field, Closure $callback) { + 'callback' => function (Field $field, Closure $callback): mixed { return $callback($field); }, @@ -348,10 +340,9 @@ return function (App $app) { * Escapes the field value to be safely used in HTML * templates without the risk of XSS attacks * - * @param \Kirby\Cms\Field $field * @param string $context Location of output (`html`, `attr`, `js`, `css`, `url` or `xml`) */ - 'escape' => function (Field $field, string $context = 'html') { + 'escape' => function (Field $field, string $context = 'html'): Field { $field->value = Str::esc($field->value ?? '', $context); return $field; }, @@ -359,25 +350,26 @@ return function (App $app) { /** * Creates an excerpt of the field value without html * or any other formatting. - * - * @param \Kirby\Cms\Field $field - * @param int $cahrs - * @param bool $strip - * @param string $rep - * @return \Kirby\Cms\Field */ - 'excerpt' => function (Field $field, int $chars = 0, bool $strip = true, string $rep = ' …') { - $field->value = Str::excerpt($field->kirbytext()->value(), $chars, $strip, $rep); + 'excerpt' => function ( + Field $field, + int $chars = 0, + bool $strip = true, + string $rep = ' …' + ): Field { + $field->value = Str::excerpt( + $field->kirbytext()->value(), + $chars, + $strip, + $rep + ); return $field; }, /** * Converts the field content to valid HTML - * - * @param \Kirby\Cms\Field $field - * @return \Kirby\Cms\Field */ - 'html' => function (Field $field) { + 'html' => function (Field $field): Field { $field->value = Html::encode($field->value); return $field; }, @@ -387,11 +379,8 @@ return function (App $app) { * it can be safely placed inside of other inline elements * without the risk of breaking the HTML structure. * @since 3.3.0 - * - * @param \Kirby\Cms\Field $field - * @return \Kirby\Cms\Field */ - 'inline' => function (Field $field) { + 'inline' => function (Field $field): Field { // List of valid inline elements taken from: https://developer.mozilla.org/de/docs/Web/HTML/Inline_elemente // Obsolete elements, script tags, image maps and form elements have // been excluded for safety reasons and as they are most likely not @@ -402,12 +391,11 @@ return function (App $app) { /** * Converts the field content from Markdown/Kirbytext to valid HTML - * - * @param \Kirby\Cms\Field $field - * @param array $options - * @return \Kirby\Cms\Field */ - 'kirbytext' => function (Field $field, array $options = []) use ($app) { + 'kirbytext' => function ( + Field $field, + array $options = [] + ) use ($app): Field { $field->value = $app->kirbytext($field->value, A::merge($options, [ 'parent' => $field->parent(), 'field' => $field @@ -420,12 +408,11 @@ return function (App $app) { * Converts the field content from inline Markdown/Kirbytext * to valid HTML * @since 3.1.0 - * - * @param \Kirby\Cms\Field $field - * @param array $options - * @return \Kirby\Cms\Field */ - 'kirbytextinline' => function (Field $field, array $options = []) use ($app) { + 'kirbytextinline' => function ( + Field $field, + array $options = [] + ) use ($app): Field { $field->value = $app->kirbytext($field->value, A::merge($options, [ 'parent' => $field->parent(), 'field' => $field, @@ -439,11 +426,8 @@ return function (App $app) { /** * Parses all KirbyTags without also parsing Markdown - * - * @param \Kirby\Cms\Field $field - * @return \Kirby\Cms\Field */ - 'kirbytags' => function (Field $field) use ($app) { + 'kirbytags' => function (Field $field) use ($app): Field { $field->value = $app->kirbytags($field->value, [ 'parent' => $field->parent(), 'field' => $field @@ -454,23 +438,19 @@ return function (App $app) { /** * Converts the field content to lowercase - * - * @param \Kirby\Cms\Field $field - * @return \Kirby\Cms\Field */ - 'lower' => function (Field $field) { + 'lower' => function (Field $field): Field { $field->value = Str::lower($field->value); return $field; }, /** * Converts markdown to valid HTML - * - * @param \Kirby\Cms\Field $field - * @param array $options - * @return \Kirby\Cms\Field */ - 'markdown' => function (Field $field, array $options = []) use ($app) { + 'markdown' => function ( + Field $field, + array $options = [] + ) use ($app): Field { $field->value = $app->markdown($field->value, $options); return $field; }, @@ -478,23 +458,54 @@ return function (App $app) { /** * Converts all line breaks in the field content to `
` tags. * @since 3.3.0 - * - * @param \Kirby\Cms\Field $field - * @return \Kirby\Cms\Field */ - 'nl2br' => function (Field $field) { + 'nl2br' => function (Field $field): Field { $field->value = nl2br($field->value ?? '', false); return $field; }, /** - * Uses the field value as Kirby query + * Parses the field value as DOM and replaces + * any permalinks in href/src attributes with + * the regular url * - * @param \Kirby\Cms\Field $field - * @param string|null $expect - * @return mixed + * This method is still experimental! You can use + * it to solve potential problems with permalinks + * already, but it might change in the future. */ - 'query' => function (Field $field, string $expect = null) use ($app) { + 'permalinksToUrls' => function (Field $field): Field { + if ($field->isNotEmpty() === true) { + $dom = new Dom($field->value); + $attributes = ['href', 'src']; + $elements = $dom->query('//*[' . implode(' | ', A::map($attributes, fn ($attribute) => '@' . $attribute)) . ']'); + + foreach ($elements as $element) { + foreach ($attributes as $attribute) { + if ($element->hasAttribute($attribute) && $uuid = $element->getAttribute($attribute)) { + try { + if ($url = Uuid::for($uuid)?->model()?->url()) { + $element->setAttribute($attribute, $url); + } + } catch (InvalidArgumentException) { + // ignore anything else than permalinks + } + } + } + } + + $field->value = $dom->toString(); + } + + return $field; + }, + + /** + * Uses the field value as Kirby query + */ + 'query' => function ( + Field $field, + string|null $expect = null + ) use ($app): mixed { if ($parent = $field->parent()) { return $parent->query($field->value, $expect); } @@ -509,13 +520,13 @@ return function (App $app) { /** * It parses any queries found in the field value. * - * @param \Kirby\Cms\Field $field - * @param array $data - * @param string|null $fallback Fallback for tokens in the template that cannot be replaced - * (`null` to keep the original token) - * @return \Kirby\Cms\Field + * @param string|null $fallback Fallback for tokens in the template that cannot be replaced (`null` to keep the original token) */ - 'replace' => function (Field $field, array $data = [], string|null $fallback = '') use ($app) { + 'replace' => function ( + Field $field, + array $data = [], + string|null $fallback = '' + ) use ($app): Field { if ($parent = $field->parent()) { // never pass `null` as the $template to avoid the fallback to the model ID $field->value = $parent->toString($field->value ?? '', $data, $fallback); @@ -534,55 +545,45 @@ return function (App $app) { * Cuts the string after the given length and * adds "…" if it is longer * - * @param \Kirby\Cms\Field $field * @param int $length The number of characters in the string * @param string $appendix An optional replacement for the missing rest - * @return \Kirby\Cms\Field */ - 'short' => function (Field $field, int $length, string $appendix = '…') { + 'short' => function ( + Field $field, + int $length, + string $appendix = '…' + ): Field { $field->value = Str::short($field->value, $length, $appendix); return $field; }, /** * Converts the field content to a slug - * - * @param \Kirby\Cms\Field $field - * @return \Kirby\Cms\Field */ - 'slug' => function (Field $field) { + 'slug' => function (Field $field): Field { $field->value = Str::slug($field->value); return $field; }, /** * Applies SmartyPants to the field - * - * @param \Kirby\Cms\Field $field - * @return \Kirby\Cms\Field */ - 'smartypants' => function (Field $field) use ($app) { + 'smartypants' => function (Field $field) use ($app): Field { $field->value = $app->smartypants($field->value); return $field; }, /** * Splits the field content into an array - * - * @param \Kirby\Cms\Field $field - * @return array */ - 'split' => function (Field $field, $separator = ',') { + 'split' => function (Field $field, $separator = ','): array { return Str::split((string)$field->value, $separator); }, /** * Converts the field content to uppercase - * - * @param \Kirby\Cms\Field $field - * @return \Kirby\Cms\Field */ - 'upper' => function (Field $field) { + 'upper' => function (Field $field): Field { $field->value = Str::upper($field->value); return $field; }, @@ -590,22 +591,16 @@ return function (App $app) { /** * Avoids typographical widows in strings by replacing * the last space with ` ` - * - * @param \Kirby\Cms\Field $field - * @return \Kirby\Cms\Field */ - 'widont' => function (Field $field) { + 'widont' => function (Field $field): Field { $field->value = Str::widont($field->value); return $field; }, /** * Converts the field content to valid XML - * - * @param \Kirby\Cms\Field $field - * @return \Kirby\Cms\Field */ - 'xml' => function (Field $field) { + 'xml' => function (Field $field): Field { $field->value = Xml::encode($field->value); return $field; }, @@ -614,9 +609,6 @@ return function (App $app) { /** * Parses yaml in the field content and returns an array - * - * @param \Kirby\Cms\Field $field - * @return array */ 'yaml' => function (Field $field): array { return $field->toData('yaml'); diff --git a/kirby/config/presets/page.php b/kirby/config/presets/page.php index e01bcf1..a2102ef 100644 --- a/kirby/config/presets/page.php +++ b/kirby/config/presets/page.php @@ -44,7 +44,7 @@ return function ($props) { } if (empty($sidebar) === true) { - $props['fields'] = $props['fields'] ?? []; + $props['fields'] ??= []; unset( $props['files'], diff --git a/kirby/config/presets/pages.php b/kirby/config/presets/pages.php index 32a4589..a2c7714 100644 --- a/kirby/config/presets/pages.php +++ b/kirby/config/presets/pages.php @@ -26,7 +26,7 @@ return function (array $props) { // inject the global templates definition if (empty($templates) === false) { - $props['templates'] = $props['templates'] ?? $templates; + $props['templates'] ??= $templates; } return array_replace_recursive($defaults, $props); diff --git a/kirby/config/routes.php b/kirby/config/routes.php index d8f4962..0699ef1 100644 --- a/kirby/config/routes.php +++ b/kirby/config/routes.php @@ -1,5 +1,6 @@ option('api.slug', 'api'); $panel = $kirby->option('panel.slug', 'panel'); $index = $kirby->url('index'); @@ -32,7 +33,7 @@ return function ($kirby) { 'pattern' => $api . '/(:all)', 'method' => 'ALL', 'env' => 'api', - 'action' => function ($path = null) use ($kirby) { + 'action' => function (string|null $path = null) use ($kirby) { if ($kirby->option('api') === false) { return null; } @@ -60,37 +61,63 @@ return function ($kirby) { } ], [ - 'pattern' => $media . '/plugins/(:any)/(:any)/(:all)\.(css|map|gif|js|mjs|jpg|png|svg|webp|avif|woff2|woff|json)', + // TODO: change to '/plugins/(:any)/(:any)/(:any)/(:all)' once + // the hash is made mandatory + 'pattern' => $media . '/plugins/(:any)/(:any)/(?:(:any)/)?(:all)', 'env' => 'media', - 'action' => function (string $provider, string $pluginName, string $filename, string $extension) { - return PluginAssets::resolve($provider . '/' . $pluginName, $filename . '.' . $extension); + 'action' => function ( + string $provider, + string $pluginName, + string $hash, + string $path + ) { + return PluginAssets::resolve( + $provider . '/' . $pluginName, + $hash, + $path + ); } ], [ 'pattern' => $media . '/pages/(:all)/(:any)/(:any)', 'env' => 'media', - 'action' => function ($path, $hash, $filename) use ($kirby) { + 'action' => function ( + string $path, + string $hash, + string $filename + ) use ($kirby) { return Media::link($kirby->page($path), $hash, $filename); } ], [ 'pattern' => $media . '/site/(:any)/(:any)', 'env' => 'media', - 'action' => function ($hash, $filename) use ($kirby) { + 'action' => function ( + string $hash, + string $filename + ) use ($kirby) { return Media::link($kirby->site(), $hash, $filename); } ], [ 'pattern' => $media . '/users/(:any)/(:any)/(:any)', 'env' => 'media', - 'action' => function ($id, $hash, $filename) use ($kirby) { + 'action' => function ( + string $id, + string $hash, + string $filename + ) use ($kirby) { return Media::link($kirby->user($id), $hash, $filename); } ], [ 'pattern' => $media . '/assets/(:all)/(:any)/(:any)', 'env' => 'media', - 'action' => function ($path, $hash, $filename) { + 'action' => function ( + string $path, + string $hash, + string $filename + ) { return Media::thumb($path, $hash, $filename); } ], @@ -98,7 +125,7 @@ return function ($kirby) { 'pattern' => $panel . '/(:all?)', 'method' => 'ALL', 'env' => 'panel', - 'action' => function ($path = null) { + 'action' => function (string|null $path = null) { return Panel::router($path); } ], diff --git a/kirby/config/sections/files.php b/kirby/config/sections/files.php index 1b123d7..8bab322 100644 --- a/kirby/config/sections/files.php +++ b/kirby/config/sections/files.php @@ -1,6 +1,7 @@ [ + /** + * Filters pages by a query. Sorting will be disabled + */ + 'query' => function (string|null $query = null) { + return $query; + }, /** * Filters all files by template and also sets the template, which will be used for all uploads */ - 'template' => function (string $template = null) { + 'template' => function (string|null $template = null) { return $template; }, /** @@ -40,7 +47,7 @@ return [ 'template' => $this->template ]); - return $file->blueprint()->acceptMime(); + return $file->blueprint()->acceptAttribute(); } return null; @@ -48,11 +55,18 @@ return [ 'parent' => function () { return $this->parentModel(); }, - 'files' => function () { - $files = $this->parent->files()->template($this->template); + 'models' => function () { + if ($this->query !== null) { + $files = $this->parent->query($this->query, Files::class) ?? new Files([]); + } else { + $files = $this->parent->files(); + } - // filter out all protected files - $files = $files->filter('isReadable', true); + // filter files by template + $files = $files->template($this->template); + + // filter out all protected and hidden files + $files = $files->filter('isListable', true); // search if ($this->search === true && empty($this->searchterm()) === false) { @@ -85,6 +99,9 @@ return [ return $files; }, + 'files' => function () { + return $this->models; + }, 'data' => function () { $data = []; @@ -92,7 +109,7 @@ return [ // a different parent model $dragTextAbsolute = $this->model->is($this->parent) === false; - foreach ($this->files as $file) { + foreach ($this->models as $file) { $panel = $file->panel(); $item = [ @@ -123,7 +140,7 @@ return [ return $data; }, 'total' => function () { - return $this->files->pagination()->total(); + return $this->models->pagination()->total(); }, 'errors' => function () { $errors = []; @@ -177,24 +194,44 @@ return [ 'multiple' => $multiple, 'max' => $max, 'api' => $this->parent->apiUrl(true) . '/files', - 'attributes' => array_filter([ + 'preview' => $this->image, + 'attributes' => [ // TODO: an edge issue that needs to be solved: - // if multiple users load the same section at the same time - // and upload a file, uploaded files have the same sort number + // if multiple users load the same section + // at the same time and upload a file, + // uploaded files have the same sort number 'sort' => $this->sortable === true ? $this->total + 1 : null, 'template' => $template - ]) + ] ]; } ], + // @codeCoverageIgnoreStart + 'api' => function () { + return [ + [ + 'pattern' => 'sort', + 'method' => 'PATCH', + 'action' => function () { + $this->section()->model()->files()->changeSort( + $this->requestBody('files'), + $this->requestBody('index') + ); + + return true; + } + ] + ]; + }, + // @codeCoverageIgnoreEnd 'toArray' => function () { return [ 'data' => $this->data, 'errors' => $this->errors, 'options' => [ 'accept' => $this->accept, - 'apiUrl' => $this->parent->apiUrl(true), - 'columns' => $this->columns, + 'apiUrl' => $this->parent->apiUrl(true) . '/sections/' . $this->name, + 'columns' => $this->columnsWithTypes(), 'empty' => $this->empty, 'headline' => $this->headline, 'help' => $this->help, diff --git a/kirby/config/sections/info.php b/kirby/config/sections/info.php index bc390c7..20a288d 100644 --- a/kirby/config/sections/info.php +++ b/kirby/config/sections/info.php @@ -7,10 +7,13 @@ return [ 'headline', ], 'props' => [ + 'icon' => function (string|null $icon = null) { + return $icon; + }, 'text' => function ($text = null) { return I18n::translate($text, $text); }, - 'theme' => function (string $theme = null) { + 'theme' => function (string|null $theme = null) { return $theme; } ], @@ -25,6 +28,7 @@ return [ ], 'toArray' => function () { return [ + 'icon' => $this->icon, 'label' => $this->headline, 'text' => $this->text, 'theme' => $this->theme diff --git a/kirby/config/sections/mixins/layout.php b/kirby/config/sections/mixins/layout.php index d362a7c..b348176 100644 --- a/kirby/config/sections/mixins/layout.php +++ b/kirby/config/sections/mixins/layout.php @@ -1,5 +1,7 @@ function (array $columns = null) { + 'columns' => function (array|null $columns = null) { return $columns ?? []; }, /** @@ -20,7 +22,15 @@ return [ return in_array($layout, $layouts) ? $layout : 'list'; }, /** - * The size option controls the size of cards. By default cards are auto-sized and the cards grid will always fill the full width. With a size you can disable auto-sizing. Available sizes: `tiny`, `small`, `medium`, `large`, `huge` + * Whether the raw content file values should be used for the table column previews. Should not be used unless it eases performance issues in your setup introduced with Kirby 4.2 + * + * @todo remove when Form classes have been refactored + */ + 'rawvalues' => function (bool $rawvalues = false) { + return $rawvalues; + }, + /** + * The size option controls the size of cards. By default cards are auto-sized and the cards grid will always fill the full width. With a size you can disable auto-sizing. Available sizes: `tiny`, `small`, `medium`, `large`, `huge`, `full` */ 'size' => function (string $size = 'auto') { return $size; @@ -28,7 +38,7 @@ return [ ], 'computed' => [ 'columns' => function () { - $columns = []; + $columns = []; if ($this->layout !== 'table') { return []; @@ -76,9 +86,12 @@ return [ // keep the original column name as id $column['id'] = $columnName; - // add the custom column to the array with a key that won't - // override the system columns - $columns[$columnName . 'Cell'] = $column; + // add the custom column to the array + // allowing to extend/overwrite existing columns + $columns[$columnName] = [ + ...$columns[$columnName] ?? [], + ...$column + ]; } if ($this->type === 'pages') { @@ -94,7 +107,27 @@ return [ }, ], 'methods' => [ - 'columnsValues' => function (array $item, $model) { + 'columnsWithTypes' => function () { + $columns = $this->columns; + + // add the type to the columns for the table layout + if ($this->layout === 'table') { + $blueprint = $this->models->first()?->blueprint(); + + if ($blueprint === null) { + return $columns; + } + + foreach ($columns as $columnName => $column) { + if ($id = $column['id'] ?? null) { + $columns[$columnName]['type'] ??= $blueprint->field($id)['type'] ?? null; + } + } + } + + return $columns; + }, + 'columnsValues' => function (array $item, ModelWithContent $model) { $item['title'] = [ // override toSafeString() coming from `$item` // because the table cells don't use v-html @@ -108,19 +141,35 @@ return [ $item['info'] = $model->toString($this->info); } + // if forcing raw values, get those directly from content file + // TODO: remove once Form classes have been refactored + // @codeCoverageIgnoreStart + if ($this->rawvalues === true) { + foreach ($this->columns as $columnName => $column) { + $item[$columnName] = match (empty($column['value'])) { + // if column value defined, resolve the query + false => $model->toString($column['value']), + // otherwise use the form value, + // but don't overwrite columns + default => $item[$columnName] ?? $model->content()->get($column['id'] ?? $columnName)->value() + }; + } + + return $item; + } + // @codeCoverageIgnoreEnd + + // Use form to get the proper values for the columns + $form = Form::for($model)->values(); + foreach ($this->columns as $columnName => $column) { - // don't overwrite essential columns - if (isset($item[$columnName]) === true) { - continue; - } - - if (empty($column['value']) === false) { - $value = $model->toString($column['value']); - } else { - $value = $model->content()->get($column['id'] ?? $columnName)->value(); - } - - $item[$columnName] = $value; + $item[$columnName] = match (empty($column['value'])) { + // if column value defined, resolve the query + false => $model->toString($column['value']), + // otherwise use the form value, + // but don't overwrite columns + default => $item[$columnName] ?? $form[$column['id'] ?? $columnName] ?? null + }; } return $item; diff --git a/kirby/config/sections/mixins/max.php b/kirby/config/sections/mixins/max.php index a87c1cc..b49c627 100644 --- a/kirby/config/sections/mixins/max.php +++ b/kirby/config/sections/mixins/max.php @@ -5,7 +5,7 @@ return [ /** * Sets the maximum number of allowed entries in the section */ - 'max' => function (int $max = null) { + 'max' => function (int|null $max = null) { return $max; } ], diff --git a/kirby/config/sections/mixins/min.php b/kirby/config/sections/mixins/min.php index 6295f2d..40fa82e 100644 --- a/kirby/config/sections/mixins/min.php +++ b/kirby/config/sections/mixins/min.php @@ -5,7 +5,7 @@ return [ /** * Sets the minimum number of required entries in the section */ - 'min' => function (int $min = null) { + 'min' => function (int|null $min = null) { return $min; } ], diff --git a/kirby/config/sections/mixins/pagination.php b/kirby/config/sections/mixins/pagination.php index 3b2a2b0..39f8d0a 100644 --- a/kirby/config/sections/mixins/pagination.php +++ b/kirby/config/sections/mixins/pagination.php @@ -12,9 +12,9 @@ return [ return $limit; }, /** - * Sets the default page for the pagination. This will overwrite default pagination. + * Sets the default page for the pagination. */ - 'page' => function (int $page = null) { + 'page' => function (int|null $page = null) { return App::instance()->request()->get('page', $page); }, ], diff --git a/kirby/config/sections/mixins/parent.php b/kirby/config/sections/mixins/parent.php index 1096930..1217411 100644 --- a/kirby/config/sections/mixins/parent.php +++ b/kirby/config/sections/mixins/parent.php @@ -11,7 +11,7 @@ return [ /** * Sets the query to a parent to find items for the list */ - 'parent' => function (string $parent = null) { + 'parent' => function (string|null $parent = null) { return $parent; } ], diff --git a/kirby/config/sections/mixins/sort.php b/kirby/config/sections/mixins/sort.php index 4387bae..118e03b 100644 --- a/kirby/config/sections/mixins/sort.php +++ b/kirby/config/sections/mixins/sort.php @@ -17,7 +17,7 @@ return [ /** * Overwrites manual sorting and sorts by the given field and sorting direction (i.e. `date desc`) */ - 'sortBy' => function (string $sortBy = null) { + 'sortBy' => function (string|null $sortBy = null) { return $sortBy; }, ], @@ -39,6 +39,10 @@ return [ return false; } + if ($this->query !== null) { + return false; + } + if ($this->sortBy !== null) { return false; } diff --git a/kirby/config/sections/pages.php b/kirby/config/sections/pages.php index c9b23b7..6ca1090 100644 --- a/kirby/config/sections/pages.php +++ b/kirby/config/sections/pages.php @@ -2,6 +2,7 @@ use Kirby\Cms\Blueprint; use Kirby\Cms\Page; +use Kirby\Cms\Pages; use Kirby\Cms\Site; use Kirby\Exception\InvalidArgumentException; use Kirby\Toolkit\A; @@ -29,6 +30,12 @@ return [ 'create' => function ($create = null) { return $create; }, + /** + * Filters pages by a query. Sorting will be disabled + */ + 'query' => function (string|null $query = null) { + return $query; + }, /** * Filters pages by their status. Available status settings: `draft`, `unlisted`, `listed`, `published`, `all`. */ @@ -43,11 +50,23 @@ return [ return $status; }, + /** + * Filters the list by single template. + */ + 'template' => function (string|array|null $template = null) { + return $template; + }, /** * Filters the list by templates and sets template options when adding new pages to the section. */ 'templates' => function ($templates = null) { return A::wrap($templates ?? $this->template); + }, + /** + * Excludes the selected templates. + */ + 'templatesIgnore' => function ($templates = null) { + return A::wrap($templates); } ], 'computed' => [ @@ -63,14 +82,18 @@ return [ return $parent; }, - 'pages' => function () { - $pages = match ($this->status) { - 'draft' => $this->parent->drafts(), - 'listed' => $this->parent->children()->listed(), - 'published' => $this->parent->children(), - 'unlisted' => $this->parent->children()->unlisted(), - default => $this->parent->childrenAndDrafts() - }; + 'models' => function () { + if ($this->query !== null) { + $pages = $this->parent->query($this->query, Pages::class) ?? new Pages([]); + } else { + $pages = match ($this->status) { + 'draft' => $this->parent->drafts(), + 'listed' => $this->parent->children()->listed(), + 'published' => $this->parent->children(), + 'unlisted' => $this->parent->children()->unlisted(), + default => $this->parent->childrenAndDrafts() + }; + } // filters pages that are protected and not in the templates list // internal `filter()` method used instead of foreach loop that previously included `unset()` @@ -78,13 +101,26 @@ return [ // also it has been tested that there is no performance difference // even in 0.1 seconds on 100k virtual pages $pages = $pages->filter(function ($page) { - // remove all protected pages - if ($page->isReadable() === false) { + // remove all protected and hidden pages + if ($page->isListable() === false) { return false; } + $intendedTemplate = $page->intendedTemplate()->name(); + // filter by all set templates - if ($this->templates && in_array($page->intendedTemplate()->name(), $this->templates) === false) { + if ( + $this->templates && + in_array($intendedTemplate, $this->templates) === false + ) { + return false; + } + + // exclude by all ignored templates + if ( + $this->templatesIgnore && + in_array($intendedTemplate, $this->templatesIgnore) === true + ) { return false; } @@ -120,13 +156,16 @@ return [ return $pages; }, + 'pages' => function () { + return $this->models; + }, 'total' => function () { - return $this->pages->pagination()->total(); + return $this->models->pagination()->total(); }, 'data' => function () { $data = []; - foreach ($this->pages as $page) { + foreach ($this->models as $page) { $panel = $page->panel(); $permissions = $page->permissions(); @@ -193,11 +232,39 @@ return [ return false; } - if (in_array($this->status, ['draft', 'all']) === false) { + if ($this->isFull() === true) { return false; } - if ($this->isFull() === true) { + // form here on, we need to check with which status + // the pages are created and if the section can show + // these newly created pages + + // if the section shows pages no matter what status they have, + // we can always show the add button + if ($this->status === 'all') { + return true; + } + + // collect all statuses of the blueprints + // that are allowed to be created + $statuses = []; + + foreach ($this->blueprintNames() as $blueprint) { + try { + $props = Blueprint::load('pages/' . $blueprint); + $statuses[] = $props['create']['status'] ?? 'draft'; + } catch (Throwable) { + $statuses[] = 'draft'; // @codeCoverageIgnore + } + } + + $statuses = array_unique($statuses); + + // if there are multiple statuses or if the section is showing + // a different status than new pages would be created with, + // we cannot show the add button + if (count($statuses) > 1 || $this->status !== $statuses[0]) { return false; } @@ -210,17 +277,12 @@ return [ 'methods' => [ 'blueprints' => function () { $blueprints = []; - $templates = empty($this->create) === false ? A::wrap($this->create) : $this->templates; - - if (empty($templates) === true) { - $templates = $this->kirby()->blueprints(); - } // convert every template to a usable option array // for the template select box - foreach ($templates as $template) { + foreach ($this->blueprintNames() as $blueprint) { try { - $props = Blueprint::load('pages/' . $template); + $props = Blueprint::load('pages/' . $blueprint); $blueprints[] = [ 'name' => basename($props['name']), @@ -228,14 +290,28 @@ return [ ]; } catch (Throwable) { $blueprints[] = [ - 'name' => basename($template), - 'title' => ucfirst($template), + 'name' => basename($blueprint), + 'title' => ucfirst($blueprint), ]; } } return $blueprints; - } + }, + 'blueprintNames' => function () { + $blueprints = empty($this->create) === false ? A::wrap($this->create) : $this->templates; + + if (empty($blueprints) === true) { + $blueprints = $this->kirby()->blueprints(); + } + + // excludes ignored templates + if ($templatesIgnore = $this->templatesIgnore) { + $blueprints = array_diff($blueprints, $templatesIgnore); + } + + return $blueprints; + }, ], 'toArray' => function () { return [ @@ -243,7 +319,7 @@ return [ 'errors' => $this->errors, 'options' => [ 'add' => $this->add, - 'columns' => $this->columns, + 'columns' => $this->columnsWithTypes(), 'empty' => $this->empty, 'headline' => $this->headline, 'help' => $this->help, diff --git a/kirby/config/sections/stats.php b/kirby/config/sections/stats.php index e18eba0..86efe96 100644 --- a/kirby/config/sections/stats.php +++ b/kirby/config/sections/stats.php @@ -8,7 +8,7 @@ return [ ], 'props' => [ /** - * Array or query string for reports. Each report needs a `label` and `value` and can have additional `info`, `link` and `theme` settings. + * Array or query string for reports. Each report needs a `label` and `value` and can have additional `info`, `link`, `icon` and `theme` settings. */ 'reports' => function ($reports = null) { if ($reports === null) { @@ -53,6 +53,7 @@ return [ $value = $report['value'] ?? null; $reports[] = [ + 'icon' => $toString($report['icon'] ?? null), 'info' => $toString(I18n::translate($info, $info)), 'label' => $toString(I18n::translate($label, $label)), 'link' => $toString(I18n::translate($link, $link)), diff --git a/kirby/config/setup.php b/kirby/config/setup.php index bbe61c1..09b73bb 100644 --- a/kirby/config/setup.php +++ b/kirby/config/setup.php @@ -1,11 +1,5 @@ [ 'attr' => [], - 'html' => function ($tag) { - return strtolower($tag->date) === 'year' ? date('Y') : date($tag->date); + 'html' => function (KirbyTag $tag): string { + if (strtolower($tag->date) === 'year') { + return date('Y'); + } + + return date($tag->date); } ], @@ -31,7 +38,7 @@ return [ 'text', 'title' ], - 'html' => function ($tag) { + 'html' => function (KirbyTag $tag): string { return Html::email($tag->value, $tag->text, [ 'class' => $tag->class, 'rel' => $tag->rel, @@ -53,9 +60,9 @@ return [ 'text', 'title' ], - 'html' => function ($tag) { + 'html' => function (KirbyTag $tag): string { if (!$file = $tag->file($tag->value)) { - return $tag->text; + return $tag->text ?? $tag->value; } // use filename if the text is empty and make sure to @@ -81,7 +88,7 @@ return [ 'attr' => [ 'file' ], - 'html' => function ($tag) { + 'html' => function (KirbyTag $tag): string { return Html::gist($tag->value, $tag->file); } ], @@ -99,16 +106,41 @@ return [ 'link', 'linkclass', 'rel', + 'srcset', 'target', 'title', 'width' ], - 'html' => function ($tag) { + 'html' => function (KirbyTag $tag): string { + $kirby = $tag->kirby(); + + $tag->width ??= $kirby->option('kirbytext.image.width'); + $tag->height ??= $kirby->option('kirbytext.image.height'); + if ($tag->file = $tag->file($tag->value)) { - $tag->src = $tag->file->url(); - $tag->alt = $tag->alt ?? $tag->file->alt()->or(' ')->value(); - $tag->title = $tag->title ?? $tag->file->title()->value(); - $tag->caption = $tag->caption ?? $tag->file->caption()->value(); + $tag->src = $tag->file->url(); + $tag->alt ??= $tag->file->alt()->or('')->value(); + $tag->title ??= $tag->file->title()->value(); + $tag->caption ??= $tag->file->caption()->value(); + + if ($srcset = $tag->srcset) { + $srcset = Str::split($srcset); + $srcset = match (count($srcset) > 1) { + // comma-separated list of sizes + true => A::map($srcset, fn ($size) => (int)trim($size)), + // srcset config name + default => $srcset[0] + }; + + $tag->srcset = $tag->file->srcset($srcset); + } + + if ($tag->width === 'auto') { + $tag->width = $tag->file->width(); + } + if ($tag->height === 'auto') { + $tag->height = $tag->file->height(); + } } else { $tag->src = Url::to($tag->value); } @@ -129,25 +161,26 @@ return [ }; $image = Html::img($tag->src, [ + 'srcset' => $tag->srcset, 'width' => $tag->width, 'height' => $tag->height, 'class' => $tag->imgclass, 'title' => $tag->title, - 'alt' => $tag->alt ?? ' ' + 'alt' => $tag->alt ?? '' ]); - if ($tag->kirby()->option('kirbytext.image.figure', true) === false) { + if ($kirby->option('kirbytext.image.figure', true) === false) { return $link($image); } // render KirbyText in caption if ($tag->caption) { $options = ['markdown' => ['inline' => true]]; - $caption = $tag->kirby()->kirbytext($tag->caption, $options); + $caption = $kirby->kirbytext($tag->caption, $options); $tag->caption = [$caption]; } - return Html::figure([ $link($image) ], $tag->caption, [ + return Html::figure([$link($image)], $tag->caption, [ 'class' => $tag->class ]); } @@ -166,7 +199,7 @@ return [ 'title', 'text', ], - 'html' => function ($tag) { + 'html' => function (KirbyTag $tag): string { if (empty($tag->lang) === false) { $tag->value = Url::to($tag->value, $tag->lang); } @@ -177,7 +210,20 @@ return [ Uuid::is($tag->value, 'page') === true || Uuid::is($tag->value, 'file') === true ) { - $tag->value = Uuid::for($tag->value)->model()->url(); + $tag->value = Uuid::for($tag->value)->model()?->url(); + } + + // if url is empty, throw exception or link to the error page + if ($tag->value === null) { + if ($tag->kirby()->option('debug', false) === true) { + if (empty($tag->text) === false) { + throw new NotFoundException('The linked page cannot be found for the link text "' . $tag->text . '"'); + } else { + throw new NotFoundException('The linked page cannot be found'); + } + } else { + $tag->value = Url::to($tag->kirby()->site()->errorPageId()); + } } return Html::a($tag->value, $tag->text, [ @@ -200,7 +246,7 @@ return [ 'text', 'title' ], - 'html' => function ($tag) { + 'html' => function (KirbyTag $tag): string { return Html::tel($tag->value, $tag->text, [ 'class' => $tag->class, 'rel' => $tag->rel, @@ -209,37 +255,6 @@ return [ } ], - /** - * Twitter - */ - 'twitter' => [ - 'attr' => [ - 'class', - 'rel', - 'target', - 'text', - 'title' - ], - 'html' => function ($tag) { - // get and sanitize the username - $username = str_replace('@', '', $tag->value); - - // build the profile url - $url = 'https://twitter.com/' . $username; - - // sanitize the link text - $text = $tag->text ?? '@' . $username; - - // build the final link - return Html::a($url, $text, [ - 'class' => $tag->class, - 'rel' => $tag->rel, - 'target' => $tag->target, - 'title' => $tag->title, - ]); - } - ], - /** * Video */ @@ -249,6 +264,7 @@ return [ 'caption', 'controls', 'class', + 'disablepictureinpicture', 'height', 'loop', 'muted', @@ -258,7 +274,7 @@ return [ 'style', 'width', ], - 'html' => function ($tag) { + 'html' => function (KirbyTag $tag): string { // checks and gets if poster is local file if ( empty($tag->poster) === false && @@ -291,12 +307,15 @@ return [ // don't use attributes that iframe doesn't support if ($isProviderVideo === false) { - // converts tag attributes to supported formats (listed below) to output correct html - // booleans: autoplay, controls, loop, muted - // strings : poster, preload - // for ex : `autoplay` will not work if `false` is a `string` instead of a `boolean` + // convert tag attributes to supported formats (bool, string) + // to output correct html attributes + // + // for ex: + // `autoplay` will not work if `false` is a string + // instead of a boolean $attrs['autoplay'] = $autoplay = Str::toType($tag->autoplay, 'bool'); $attrs['controls'] = Str::toType($tag->controls ?? true, 'bool'); + $attrs['disablepictureinpicture'] = Str::toType($tag->disablepictureinpicture ?? false, 'bool'); $attrs['loop'] = Str::toType($tag->loop, 'bool'); $attrs['muted'] = Str::toType($tag->muted ?? $autoplay, 'bool'); $attrs['playsinline'] = Str::toType($tag->playsinline ?? $autoplay, 'bool'); diff --git a/kirby/dependencies/parsedown-extra/ParsedownExtra.php b/kirby/dependencies/parsedown-extra/ParsedownExtra.php index 2f9c62d..390edd7 100644 --- a/kirby/dependencies/parsedown-extra/ParsedownExtra.php +++ b/kirby/dependencies/parsedown-extra/ParsedownExtra.php @@ -17,7 +17,7 @@ class ParsedownExtra extends Parsedown { # ~ - public const version = '0.8.0-beta-1'; + public const version = '0.8.0-beta-2'; # ~ @@ -297,7 +297,7 @@ class ParsedownExtra extends Parsedown # # Setext - protected function blockSetextHeader($Line, array $Block = null) + protected function blockSetextHeader($Line, array|null $Block = null) { $Block = parent::blockSetextHeader($Line, $Block); diff --git a/kirby/dependencies/parsedown/Parsedown.php b/kirby/dependencies/parsedown/Parsedown.php index ab72225..76b2a7c 100644 --- a/kirby/dependencies/parsedown/Parsedown.php +++ b/kirby/dependencies/parsedown/Parsedown.php @@ -17,7 +17,7 @@ class Parsedown { # ~ - public const version = '1.8.0-beta-7'; + public const version = '1.8.0-beta-8'; # ~ @@ -526,7 +526,7 @@ class Parsedown # # List - protected function blockList($Line, array $CurrentBlock = null) + protected function blockList($Line, array|null $CurrentBlock = null) { list($name, $pattern) = $Line['text'][0] <= '-' ? array('ul', '[*+-]') : array('ol', '[0-9]{1,9}+[.\)]'); @@ -741,7 +741,7 @@ class Parsedown # # Setext - protected function blockSetextHeader($Line, array $Block = null) + protected function blockSetextHeader($Line, array|null $Block = null) { if (! isset($Block) or $Block['type'] !== 'Paragraph' or isset($Block['interrupted'])) { return; @@ -821,7 +821,7 @@ class Parsedown # # Table - protected function blockTable($Line, array $Block = null) + protected function blockTable($Line, array|null $Block = null) { if (! isset($Block) or $Block['type'] !== 'Paragraph' or isset($Block['interrupted'])) { return; diff --git a/kirby/i18n/translations/bg.json b/kirby/i18n/translations/bg.json index 8d7fde9..4a483a3 100644 --- a/kirby/i18n/translations/bg.json +++ b/kirby/i18n/translations/bg.json @@ -3,57 +3,75 @@ "account.delete": "Delete your account", "account.delete.confirm": "Do you really want to delete your account? You will be logged out immediately. Your account cannot be recovered.", - "add": "Добави", + "activate": "Activate", + "add": "\u0414\u043e\u0431\u0430\u0432\u0438", + "alpha": "Alpha", "author": "Author", "avatar": "Профилна снимка", "back": "Назад", - "cancel": "Откажи", - "change": "Промени", - "close": "Затвори", + "cancel": "\u041e\u0442\u043a\u0430\u0436\u0438", + "change": "\u041f\u0440\u043e\u043c\u0435\u043d\u0438", + "close": "\u0417\u0430\u0442\u0432\u043e\u0440\u0438", + "changes": "Changes", "confirm": "Ок", "collapse": "Collapse", "collapse.all": "Collapse All", + "color": "Color", + "coordinates": "Coordinates", "copy": "Копирай", "copy.all": "Copy all", + "copy.success": "{count} copied!", + "copy.success.multiple": "{count} copied!", + "copy.url": "Copy URL", "create": "Създай", + "custom": "Custom", "date": "Дата", "date.select": "Select a date", "day": "Day", - "days.fri": "Пт", - "days.mon": "Пн", - "days.sat": "Сб", - "days.sun": "Нд", - "days.thu": "Чт", - "days.tue": "Вт", - "days.wed": "Ср", + "days.fri": "\u041f\u0442", + "days.mon": "\u041f\u043d", + "days.sat": "\u0421\u0431", + "days.sun": "\u041d\u0434", + "days.thu": "\u0427\u0442", + "days.tue": "\u0412\u0442", + "days.wed": "\u0421\u0440", "debugging": "Debugging", - "delete": "Изтрий", + "delete": "\u0418\u0437\u0442\u0440\u0438\u0439", "delete.all": "Delete all", + "dialog.fields.empty": "This dialog has no fields", "dialog.files.empty": "No files to select", "dialog.pages.empty": "No pages to select", + "dialog.text.empty": "This dialog does not define any text", "dialog.users.empty": "No users to select", "dimensions": "Размери", + "disable": "Disable", "disabled": "Disabled", - "discard": "Отмени", + "discard": "\u041e\u0442\u043c\u0435\u043d\u0438", + + "drawer.fields.empty": "This drawer has no fields", + + "domain": "Domain", "download": "Download", "duplicate": "Duplicate", - "edit": "Редактирай", + "edit": "\u0420\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u0430\u0439", "email": "Email", "email.placeholder": "mail@example.com", + "enter": "Enter", "entries": "Entries", "entry": "Entry", "environment": "Environment", + "error": "Error", "error.access.code": "Invalid code", "error.access.login": "Invalid login", "error.access.panel": "Нямате права за достъп до панела", @@ -74,13 +92,31 @@ "error.cache.type.invalid": "Invalid cache type \"{type}\"", + "error.content.lock.delete": "The version is locked and cannot be deleted", + "error.content.lock.move": "The source version is locked and cannot be moved", + "error.content.lock.publish": "This version is already published", + "error.content.lock.replace": "The version is locked and cannot be replaced", + "error.content.lock.update": "The version is locked and cannot be updated", + + "error.entries.max.plural": "You must not add more than {max} entries", + "error.entries.max.singular": "You must not add more than one entry", + "error.entries.min.plural": "You must add at least {min} entries", + "error.entries.min.singular": "You must add at least one entry", + "error.entries.supports": "\"{type}\" field type is not supported for the entries field", + "error.entries.validation": "There's an error on the \"{field}\" field in row {index}", + "error.email.preset.notFound": "Email шаблонът \"{name}\" не може да бъде открит", "error.field.converter.invalid": "Невалиден конвертор \"{converter}\"", + "error.field.link.options": "Invalid options: {options}", "error.field.type.missing": "Field \"{ name }\": The field type \"{ type }\" does not exist", "error.file.changeName.empty": "The name must not be empty", "error.file.changeName.permission": "Не можете да смените името на \"{filename}\"", + "error.file.changeTemplate.invalid": "The template for the file \"{id}\" cannot be changed to \"{template}\" (valid: \"{blueprints}\")", + "error.file.changeTemplate.permission": "You are not allowed to change the template for the file \"{id}\"", + + "error.file.delete.multiple": "Not all files could be deleted. Try each remaining file individually to see the specific error that prevents deletion.", "error.file.duplicate": "Файл с име \"{filename}\" вече съществува", "error.file.extension.forbidden": "Файловото разширение \"{extension}\" не е позволено", "error.file.extension.invalid": "Invalid extension: {extension}", @@ -95,33 +131,43 @@ "error.file.minheight": "The height of the image must be at least {height} pixels", "error.file.minsize": "The file is too small", "error.file.minwidth": "The width of the image must be at least {width} pixels", + "error.file.name.unique": "The filename must be unique", "error.file.name.missing": "Името на файла е задължително", "error.file.notFound": "Файлът \"{filename}\" не може да бъде намерен", "error.file.orientation": "The orientation of the image must be \"{orientation}\"", + "error.file.sort.permission": "You are not allowed to change the sorting of \"{filename}\"", "error.file.type.forbidden": "Не е позволен ъплоуда на файлове от тип {type}", "error.file.type.invalid": "Invalid file type: {type}", - "error.file.undefined": "Файлът не може да бъде намерен", + "error.file.undefined": "\u0424\u0430\u0439\u043b\u044a\u0442 \u043d\u0435 \u043c\u043e\u0436\u0435 \u0434\u0430 \u0431\u044a\u0434\u0435 \u043d\u0430\u043c\u0435\u0440\u0435\u043d", "error.form.incomplete": "Моля коригирайте всички грешки във формата...", "error.form.notSaved": "Формата не може да бъде запазена", "error.language.code": "Please enter a valid code for the language", + "error.language.create.permission": "You are not allowed to create a language", + "error.language.delete.permission": "You are not allowed to delete the language", "error.language.duplicate": "The language already exists", "error.language.name": "Please enter a valid name for the language", "error.language.notFound": "The language could not be found", + "error.language.update.permission": "You are not allowed to update the language", "error.layout.validation.block": "There's an error on the \"{field}\" field in block {blockIndex} using the \"{fieldset}\" block type in layout {layoutIndex}", "error.layout.validation.settings": "There's an error in layout {index} settings", - "error.license.format": "Please enter a valid license key", + "error.license.domain": "The domain for the license is missing", "error.license.email": "Моля въведете валиден email адрес", + "error.license.format": "Please enter a valid license code", "error.license.verification": "The license could not be verified", + "error.login.totp.confirm.invalid": "Invalid code", + "error.login.totp.confirm.missing": "Please enter the current code", + "error.object.validation": "There’s an error in the \"{label}\" field:\n{message}", "error.offline": "The Panel is currently offline", "error.page.changeSlug.permission": "Не можете да смените URL на \"{slug}\"", + "error.page.changeSlug.reserved": "The path of top-level pages must not start with \"{path}\"", "error.page.changeStatus.incomplete": "Страницата съдържа грешки и не може да бъде публикувана", "error.page.changeStatus.permission": "Статусът на страницата не може да бъде променен", "error.page.changeStatus.toDraft.invalid": "Страницата \"{slug}\" не може да бъде променена в чернова", @@ -133,17 +179,25 @@ "error.page.delete": "Страницата \"{slug}\" не може да бъде изтрита", "error.page.delete.confirm": "Моля въведете името на страницата, за да потвърдите", "error.page.delete.hasChildren": "Страницата има подстраници и не може да бъде изтрита", + "error.page.delete.multiple": "Not all pages could be deleted. Try each remaining page individually to see the specific error that prevents deletion.", "error.page.delete.permission": "Не можете да изтриете \"{slug}\"", "error.page.draft.duplicate": "Вече съществува чернова с URL-добавка \"{slug}\"", "error.page.duplicate": "Страница с URL-добавка \"{slug}\" вече съществува", "error.page.duplicate.permission": "You are not allowed to duplicate \"{slug}\"", + "error.page.move.ancestor": "The page cannot be moved into itself", + "error.page.move.directory": "The page directory cannot be moved", + "error.page.move.duplicate": "A sub page with the URL appendix \"{slug}\" already exists", + "error.page.move.noSections": "The page \"{parent}\" cannot be a parent of any page because it lacks any pages sections in its blueprint", + "error.page.move.notFound": "The moved page could not be found", + "error.page.move.permission": "You are not allowed to move \"{slug}\"", + "error.page.move.template": "The \"{template}\" template is not accepted as a subpage of \"{parent}\"", "error.page.notFound": "Страницата \"{slug}\" не може да бъде намерена", "error.page.num.invalid": "Моля въведете валидно число за сортиране. Числата не трябва да са негативни.", "error.page.slug.invalid": "Please enter a valid URL appendix", "error.page.slug.maxlength": "Slug length must be less than \"{length}\" characters", "error.page.sort.permission": "Страницата \"{slug}\" не може да бъде сортирана", "error.page.status.invalid": "Моля изберете валиден статус на страницата", - "error.page.undefined": "Страницата не може да бъде намерена", + "error.page.undefined": "\u0421\u0442\u0440\u0430\u043d\u0438\u0446\u0430\u0442\u0430 \u043d\u0435 \u043c\u043e\u0436\u0435 \u0434\u0430 \u0431\u044a\u0434\u0435 \u043d\u0430\u043c\u0435\u0440\u0435\u043d\u0430", "error.page.update.permission": "Не можете да обновите \"{slug}\"", "error.section.files.max.plural": "Не можете да добавяте повече от {max} файлa в секция \"{section}\"", @@ -163,6 +217,8 @@ "error.site.changeTitle.permission": "Не може да променяте заглавието на сайта", "error.site.update.permission": "Нямате права за да обновите сайта", + "error.structure.validation": "There's an error on the \"{field}\" field in row {index}", + "error.template.default.notFound": "Стандартният шаблон не съществува", "error.unexpected": "An unexpected error occurred! Enable debug mode for more info: https://getkirby.com/docs/reference/system/options/debug", @@ -175,17 +231,17 @@ "error.user.changeRole.permission": "Нямате права да промените ролята на този потребител \"{name}\"", "error.user.changeRole.toAdmin": "You are not allowed to promote someone to the admin role", "error.user.create.permission": "Нямате права да създадете този потребител", - "error.user.delete": "Потребителят не може да бъде изтрит", - "error.user.delete.lastAdmin": "Не можете да изтриете последния администратор", + "error.user.delete": "\u041f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u044f\u0442 \u043d\u0435 \u043c\u043e\u0436\u0435 \u0434\u0430 \u0431\u044a\u0434\u0435 \u0438\u0437\u0442\u0440\u0438\u0442", + "error.user.delete.lastAdmin": "\u041d\u0435 \u043c\u043e\u0436\u0435\u0442\u0435 \u0434\u0430 \u0438\u0437\u0442\u0440\u0438\u0435\u0442\u0435 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u044f \u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440", "error.user.delete.lastUser": "Последният потребител не може да бъде изтрит", - "error.user.delete.permission": "Не е позволено да изтривате този потребител", + "error.user.delete.permission": "\u041d\u0435 \u0435 \u043f\u043e\u0437\u0432\u043e\u043b\u0435\u043d\u043e \u0434\u0430 \u0438\u0437\u0442\u0440\u0438\u0432\u0430\u0442\u0435 \u0442\u043e\u0437\u0438 \u043f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b", "error.user.duplicate": "Потребител с имейл \"{email}\" вече съществува", "error.user.email.invalid": "Моля въведете валиден email адрес", "error.user.language.invalid": "Моля въведете валиден език", - "error.user.notFound": "Потребителят не може да бъде намерен.", + "error.user.notFound": "\u041f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u044f\u0442 \u043d\u0435 \u043c\u043e\u0436\u0435 \u0434\u0430 \u0431\u044a\u0434\u0435 \u043d\u0430\u043c\u0435\u0440\u0435\u043d.", "error.user.password.excessive": "Please enter a valid password. Passwords must not be longer than 1000 characters.", "error.user.password.invalid": "Моля въведете валидна парола. Тя трабва да съдържа поне 8 символа.", - "error.user.password.notSame": "Моля, потвърдете паролата", + "error.user.password.notSame": "\u041c\u043e\u043b\u044f, \u043f\u043e\u0442\u0432\u044a\u0440\u0434\u0435\u0442\u0435 \u043f\u0430\u0440\u043e\u043b\u0430\u0442\u0430", "error.user.password.undefined": "Потребителят няма парола", "error.user.password.wrong": "Wrong password", "error.user.role.invalid": "Моля въведете валидна роля", @@ -195,8 +251,10 @@ "error.validation.accepted": "Моля потвърдете", "error.validation.alpha": "Моля въвдете символи измежду a-z", "error.validation.alphanum": "Моля въвдете символи измежду a-z или цифри 0-9", + "error.validation.anchor": "Please enter a correct link anchor", "error.validation.between": "Моля въведете стойност между \"{min}\" и \"{max}\"", "error.validation.boolean": "Моля потвърдете или откажете", + "error.validation.color": "Please enter a valid color in the {format} format", "error.validation.contains": "Моля въведете стойност, която съдържа \"{needle}\"", "error.validation.date": "Моля въведете валидна дата", "error.validation.date.after": "Please enter a date after {date}", @@ -211,6 +269,7 @@ "error.validation.integer": "Моля въведете валидно цяло число", "error.validation.ip": "Моля въведете валиден IP адрес", "error.validation.less": "Моля въведете стойност по-ниска от {max}", + "error.validation.linkType": "The link type is not allowed", "error.validation.match": "Стойността не съвпада с очаквания модел", "error.validation.max": "Please enter a value equal to or lower than {max}", "error.validation.maxlength": "Моля въведете по-къса стойност. (макс. {max} символа)", @@ -227,15 +286,18 @@ "error.validation.same": "Моля въведете \"{other}\"", "error.validation.size": "Размерът на стойността трябва да бъде \"{size}\"", "error.validation.startswith": "Стойността трябва да започва с \"{start}\"", + "error.validation.tel": "Please enter an unformatted phone number", "error.validation.time": "Моля въведете валидно време", "error.validation.time.after": "Please enter a time after {time}", "error.validation.time.before": "Please enter a time before {time}", "error.validation.time.between": "Please enter a time between {min} and {max}", + "error.validation.uuid": "Please enter a valid UUID", "error.validation.url": "Моля въведете валиден URL", "expand": "Expand", "expand.all": "Expand All", + "field.invalid": "The field is invalid", "field.required": "The field is required", "field.blocks.changeType": "Change type", "field.blocks.code.name": "Код", @@ -245,8 +307,9 @@ "field.blocks.delete.confirm.all": "Do you really want to delete all blocks?", "field.blocks.delete.confirm.selected": "Do you really want to delete the selected blocks?", "field.blocks.empty": "No blocks yet", + "field.blocks.fieldsets.empty": "No fieldsets yet", "field.blocks.fieldsets.label": "Please select a block type …", - "field.blocks.fieldsets.paste": "Press {{ shortcut }} to paste/import blocks from your clipboard", + "field.blocks.fieldsets.paste": "Press {{ shortcut }} to import layouts/blocks from your clipboard Only those allowed in the current field will get inserted.", "field.blocks.gallery.name": "Gallery", "field.blocks.gallery.images.empty": "No images yet", "field.blocks.gallery.images.label": "Images", @@ -254,11 +317,16 @@ "field.blocks.heading.name": "Heading", "field.blocks.heading.text": "Text", "field.blocks.heading.placeholder": "Heading …", + "field.blocks.figure.back.plain": "Plain", + "field.blocks.figure.back.pattern.light": "Pattern (light)", + "field.blocks.figure.back.pattern.dark": "Pattern (dark)", "field.blocks.image.alt": "Alternative text", "field.blocks.image.caption": "Caption", "field.blocks.image.crop": "Crop", "field.blocks.image.link": "Връзка", "field.blocks.image.location": "Location", + "field.blocks.image.location.internal": "This website", + "field.blocks.image.location.external": "External source", "field.blocks.image.name": "Изображение", "field.blocks.image.placeholder": "Select an image", "field.blocks.image.ratio": "Ratio", @@ -275,41 +343,75 @@ "field.blocks.quote.citation.placeholder": "by …", "field.blocks.text.name": "Text", "field.blocks.text.placeholder": "Text …", + "field.blocks.video.autoplay": "Autoplay", "field.blocks.video.caption": "Caption", + "field.blocks.video.controls": "Controls", + "field.blocks.video.location": "Location", + "field.blocks.video.loop": "Loop", + "field.blocks.video.muted": "Muted", "field.blocks.video.name": "Video", "field.blocks.video.placeholder": "Enter a video URL", + "field.blocks.video.poster": "Poster", + "field.blocks.video.preload": "Preload", "field.blocks.video.url.label": "Video-URL", "field.blocks.video.url.placeholder": "https://youtube.com/?v=", - "field.files.empty": "Все още не са избрани файлове", + "field.entries.delete.confirm.all": "Do you really want to delete all entries?", + "field.entries.empty": "Все още няма статии", + "field.files.empty": "Все още не са избрани файлове", + "field.files.empty.single": "No file selected yet", + + "field.layout.change": "Change layout", "field.layout.delete": "Delete layout", "field.layout.delete.confirm": "Do you really want to delete this layout?", + "field.layout.delete.confirm.all": "Do you really want to delete all layouts?", "field.layout.empty": "No rows yet", "field.layout.select": "Select a layout", "field.object.empty": "No information yet", "field.pages.empty": "Все още не са избрани страници", + "field.pages.empty.single": "No page selected yet", "field.structure.delete.confirm": "Сигурни ли сте, че искате да изтриете това вписване?", "field.structure.delete.confirm.all": "Do you really want to delete all entries?", "field.structure.empty": "Все още няма статии", "field.users.empty": "Все още не са избрани потребители", + "field.users.empty.single": "No user selected yet", + "fields.empty": "No fields yet", + + "file": "Файл", "file.blueprint": "This file has no blueprint yet. You can define the setup in /site/blueprints/files/{blueprint}.yml", + "file.changeTemplate": "Промени шаблон", + "file.changeTemplate.notice": "Changing the file's template will remove content for fields that don't match in type. If the new template defines certain rules, e.g. image dimensions, those will also be applied irreversibly. Use with caution.", "file.delete.confirm": "Сигурни ли сте, че искате да изтриете
{filename}?", + "file.focus.placeholder": "Set focal point", + "file.focus.reset": "Remove focal point", + "file.focus.title": "Focus", "file.sort": "Change position", "files": "Файлове", + "files.delete.confirm.selected": "Do you really want to delete the selected files? This action cannot be undone.", "files.empty": "Няма файлове", + "filter": "Filter", + + "form.discard": "Discard changes", + "form.discard.confirm": "Do you really want to discard all your changes?", + "form.locked": "This content is disabled for you as it is currently edited by another user", + "form.unsaved": "The current changes have not yet been saved", + "form.preview": "Preview changes", + "form.preview.draft": "Preview draft", + "hide": "Hide", "hour": "Hour", + "hue": "Hue", "import": "Import", "info": "Info", - "insert": "Вмъкни", + "insert": "\u0412\u043c\u044a\u043a\u043d\u0438", "insert.after": "Insert after", "insert.before": "Insert before", "install": "Инсталирай", @@ -324,14 +426,14 @@ "installation.issues.mbstring": "Изисква се разширението MB String", "installation.issues.media": "Папката /media не съществува или няма права за запис", "installation.issues.php": "Бъдете сигурни, че използвате PHP 8+", - "installation.issues.server": "Kirby изисква Apache, Nginx или Caddy", "installation.issues.sessions": "The /site/sessions folder does not exist or is not writable", - "language": "Език", + "language": "\u0415\u0437\u0438\u043a", "language.code": "Код", "language.convert": "Направи по подразбиране", "language.convert.confirm": "

Сигурни ли сте, че искате да зададете {name} за език по подразбиране? Действието не може да бъде отменено.

В случай, че в {name} има непреведено съдържание, то части от сайта ви могат да останат празни.

", "language.create": "Добавете нов език", + "language.default": "Език по подразбиране", "language.delete.confirm": "Сигурни ли сте, че искате да изтриете език {name}, включително всички негови преводи? Действието не може да бъде отменено!", "language.deleted": "Езикът беше изтрит", "language.direction": "Посока на четене", @@ -340,7 +442,16 @@ "language.locale": "PHP locale string", "language.locale.warning": "You are using a custom locale set up. Please modify it in the language file in /site/languages", "language.name": "Име", + "language.secondary": "Secondary language", + "language.settings": "Language settings", "language.updated": "Езикът беше обновен", + "language.variables": "Language variables", + "language.variables.empty": "No translations yet", + + "language.variable.delete.confirm": "Do you really want to delete the variable for {key}?", + "language.variable.key": "Key", + "language.variable.notFound": "The variable could not be found", + "language.variable.value": "Value", "languages": "Езици", "languages.default": "Език по подразбиране", @@ -348,36 +459,57 @@ "languages.secondary": "Второстепенни езици", "languages.secondary.empty": "Все още няма второстепенни езици", - "license": "Лиценз за Kirby", + "license": "\u041b\u0438\u0446\u0435\u043d\u0437 \u0437\u0430 Kirby", + "license.activate": "Activate it now", + "license.activate.label": "Please activate your license", + "license.activate.domain": "Your license will be activated for {host}.", + "license.activate.local": "You are about to activate your Kirby license for your local domain {host}. If this site will be deployed to a public domain, please activate it there instead. If {host} is the domain you want to use your license for, please continue.", + "license.activated": "Activated", "license.buy": "Купи лиценз", - "license.register": "Регистрирай", + "license.code": "Код", + "license.code.help": "You received your license code after the purchase via email. Please copy and paste it here.", + "license.code.label": "Please enter your license code", + "license.status.active.info": "Includes new major versions until {date}", + "license.status.active.label": "Valid license", + "license.status.demo.info": "This is a demo installation", + "license.status.demo.label": "Demo", + "license.status.inactive.info": "Renew license to update to new major versions", + "license.status.inactive.label": "No new major versions", + "license.status.legacy.bubble": "Ready to renew your license?", + "license.status.legacy.info": "Your license does not cover this version", + "license.status.legacy.label": "Please renew your license", + "license.status.missing.bubble": "Ready to launch your site?", + "license.status.missing.info": "No valid license", + "license.status.missing.label": "Please activate your license", + "license.status.unknown.info": "The license status is unknown", + "license.status.unknown.label": "Unknown", "license.manage": "Manage your licenses", - "license.register.help": "You received your license code after the purchase via email. Please copy and paste it to register.", - "license.register.label": "Please enter your license code", - "license.register.domain": "Your license will be registered to {host}.", - "license.register.local": "You are about to register your license for your local domain {host}. If this site will be deployed to a public domain, please register it there instead. If {host} is the domain you want to license Kirby to, please continue.", - "license.register.success": "Thank you for supporting Kirby", - "license.unregistered": "Това е нерегистрирана демо версия на Kirby", + "license.purchased": "Purchased", + "license.success": "Thank you for supporting Kirby", "license.unregistered.label": "Unregistered", - "link": "Връзка", + "link": "\u0412\u0440\u044a\u0437\u043a\u0430", "link.text": "Текстова връзка", "loading": "Зареждане", "lock.unsaved": "Unsaved changes", "lock.unsaved.empty": "There are no more unsaved changes", - "lock.isLocked": "Unsaved changes by {email}", - "lock.file.isLocked": "The file is currently being edited by {email} and cannot be changed.", - "lock.page.isLocked": "The page is currently being edited by {email} and cannot be changed.", + "lock.unsaved.files": "Unsaved files", + "lock.unsaved.pages": "Unsaved pages", + "lock.unsaved.users": "Unsaved accounts", + "lock.isLocked": "Unsaved changes by {email}", "lock.unlock": "Unlock", - "lock.isUnlocked": "Your unsaved changes have been overwritten by another user. You can download your changes to merge them manually.", + "lock.unlock.submit": "Unlock and overwrite unsaved changes by {email}", + "lock.isUnlocked": "Was unlocked by another user", "login": "Подписване", "login.code.label.login": "Login code", "login.code.label.password-reset": "Password reset code", "login.code.placeholder.email": "000 000", + "login.code.placeholder.totp": "000000", "login.code.text.email": "If your email address is registered, the requested code was sent via email.", + "login.code.text.totp": "Please enter the one‑time code from your authenticator app.", "login.email.login.body": "Hi {user.nameOrEmail},\n\nYou recently requested a login code for the Panel of {site}.\nThe following login code will be valid for {timeout} minutes:\n\n{code}\n\nIf you did not request a login code, please ignore this email or contact your administrator if you have questions.\nFor security, please DO NOT forward this email.", "login.email.login.subject": "Your login code", "login.email.password-reset.body": "Hi {user.nameOrEmail},\n\nYou recently requested a password reset code for the Panel of {site}.\nThe following password reset code will be valid for {timeout} minutes:\n\n{code}\n\nIf you did not request a password reset code, please ignore this email or contact your administrator if you have questions.\nFor security, please DO NOT forward this email.", @@ -388,58 +520,80 @@ "login.toggleText.code.email-password": "Login with password", "login.toggleText.password-reset.email": "Forgot your password?", "login.toggleText.password-reset.email-password": "← Back to login", + "login.totp.enable.option": "Set up one‑time codes", + "login.totp.enable.intro": "Authenticator apps can generate one‑time codes that are used as a second factor when signing into your account.", + "login.totp.enable.qr.label": "1. Scan this QR code", + "login.totp.enable.qr.help": "Unable to scan? Add the setup key {secret} manually to your authenticator app.", + "login.totp.enable.confirm.headline": "2. Confirm with generated code", + "login.totp.enable.confirm.text": "Your app generates a new one‑time code every 30 seconds. Enter the current code to complete the setup:", + "login.totp.enable.confirm.label": "Current code", + "login.totp.enable.confirm.help": "After this setup, we will ask you for a one‑time code every time you log in.", + "login.totp.enable.success": "One‑time codes enabled", + "login.totp.disable.option": "Disable one‑time codes", + "login.totp.disable.label": "Enter your password to disable one‑time codes", + "login.totp.disable.help": "In the future, a different second factor like a login code sent via email will be requested when you log in. You can always set up one‑time codes again later.", + "login.totp.disable.admin": "

This will disable one‑time codes for {user}.

In the future, a different second factor like a login code sent via email will be requested when they log in. {user} can set up one‑time codes again after their next login.

", + "login.totp.disable.success": "One‑time codes disabled", "logout": "Изход", + "merge": "Merge", "menu": "Меню", "meridiem": "AM/PM", "mime": "Media Type", "minutes": "Minutes", "month": "Month", - "months.april": "Април", - "months.august": "Август", - "months.december": "Декември", + "months.april": "\u0410\u043f\u0440\u0438\u043b", + "months.august": "\u0410\u0432\u0433\u0443\u0441\u0442", + "months.december": "\u0414\u0435\u043a\u0435\u043c\u0432\u0440\u0438", "months.february": "Февруари", - "months.january": "Януари", - "months.july": "Юли", - "months.june": "Юни", - "months.march": "Март", - "months.may": "Май", - "months.november": "Ноември", - "months.october": "Октомври", - "months.september": "Септември", + "months.january": "\u042f\u043d\u0443\u0430\u0440\u0438", + "months.july": "\u042e\u043b\u0438", + "months.june": "\u042e\u043d\u0438", + "months.march": "\u041c\u0430\u0440\u0442", + "months.may": "\u041c\u0430\u0439", + "months.november": "\u041d\u043e\u0435\u043c\u0432\u0440\u0438", + "months.october": "\u041e\u043a\u0442\u043e\u043c\u0432\u0440\u0438", + "months.september": "\u0421\u0435\u043f\u0442\u0435\u043c\u0432\u0440\u0438", "more": "Още", + "move": "Move", "name": "Име", "next": "Next", + "night": "Night", "no": "no", "off": "off", "on": "on", "open": "Отвори", "open.newWindow": "Open in new window", + "option": "Option", "options": "Options", "options.none": "No options", + "options.all": "Show all {count} options", "orientation": "Ориентация", "orientation.landscape": "Пейзаж", "orientation.portrait": "Портрет", "orientation.square": "Квадрат", + "page": "Страница", "page.blueprint": "This page has no blueprint yet. You can define the setup in /site/blueprints/pages/{blueprint}.yml", - "page.changeSlug": "Промени URL", - "page.changeSlug.fromTitle": "Създайте от заглавието", + "page.changeSlug": "\u041f\u0440\u043e\u043c\u0435\u043d\u0438 URL", + "page.changeSlug.fromTitle": "\u0421\u044a\u0437\u0434\u0430\u0439\u0442\u0435 \u043e\u0442 \u0437\u0430\u0433\u043b\u0430\u0432\u0438\u0435\u0442\u043e", "page.changeStatus": "Промени статус", "page.changeStatus.position": "Моля изберете позиция", "page.changeStatus.select": "Изберете нов статус", "page.changeTemplate": "Промени шаблон", + "page.changeTemplate.notice": "Changing the page's template will remove content for fields that don't match in type. Use with caution.", + "page.create": "Create as {status}", "page.delete.confirm": "Сигурни ли сте, че искате да изтриете {title}?", "page.delete.confirm.subpages": "Тази страница има подстраници.
Всички подстраници също ще бъдат изтрити.", "page.delete.confirm.title": "Въведи заглавие на страница за да потвърдиш", - "page.draft.create": "Създай чернова", "page.duplicate.appendix": "Копирай", "page.duplicate.files": "Copy files", "page.duplicate.pages": "Copy pages", + "page.move": "Move page", "page.sort": "Change position", "page.status": "Status", "page.status.draft": "Чернова", @@ -450,6 +604,7 @@ "page.status.unlisted.description": "Страницата е достъпна само чрез URL", "pages": "Страници", + "pages.delete.confirm.selected": "Do you really want to delete the selected pages? This action cannot be undone.", "pages.empty": "Все още няма страници", "pages.status.draft": "Drafts", "pages.status.listed": "Published", @@ -457,22 +612,29 @@ "pagination.page": "Страница", - "password": "Парола", + "password": "\u041f\u0430\u0440\u043e\u043b\u0430", "paste": "Paste", "paste.after": "Paste after", + "paste.success": "{count} pasted!", "pixel": "Пиксел", "plugin": "Plugin", "plugins": "Plugins", "prev": "Previous", "preview": "Preview", + + "publish": "Publish", + "published": "Published", + "remove": "Премахни", "rename": "Преименувай", - "replace": "Замести", - "retry": "Опитай пак", - "revert": "Отмени", + "renew": "Renew", + "replace": "\u0417\u0430\u043c\u0435\u0441\u0442\u0438", + "replace.with": "Replace with", + "retry": "\u041e\u043f\u0438\u0442\u0430\u0439 \u043f\u0430\u043a", + "revert": "\u041e\u0442\u043c\u0435\u043d\u0438", "revert.confirm": "Do you really want to delete all unsaved changes?", - "role": "Роля", + "role": "\u0420\u043e\u043b\u044f", "role.admin.description": "The admin has all rights", "role.admin.title": "Admin", "role.all": "Всички", @@ -481,12 +643,15 @@ "role.nobody.description": "This is a fallback role without any permissions", "role.nobody.title": "Nobody", - "save": "Запиши", + "save": "\u0417\u0430\u043f\u0438\u0448\u0438", + "saved": "Saved", "search": "Търси", + "searching": "Searching", "search.min": "Enter {min} characters to search", - "search.all": "Show all", + "search.all": "Show all {count} results", "search.results.none": "No results", + "section.invalid": "The section is invalid", "section.required": "The section is required", "security": "Security", @@ -496,18 +661,27 @@ "show": "Show", "site.blueprint": "The site has no blueprint yet. You can define the setup in /site/blueprints/site.yml", "size": "Размер", - "slug": "URL-добавка", + "slug": "URL-\u0434\u043e\u0431\u0430\u0432\u043a\u0430", "sort": "Сортирай", + "sort.drag": "Drag to sort …", + "split": "Split", "stats.empty": "No reports", + "status": "Status", + + "system.info.copy": "Copy info", + "system.info.copied": "System info copied", "system.issues.content": "The content folder seems to be exposed", "system.issues.eol.kirby": "Your installed Kirby version has reached end-of-life and will not receive further security updates", "system.issues.eol.plugin": "Your installed version of the { plugin } plugin is has reached end-of-life and will not receive further security updates", + "system.issues.eol.php": "Your installed PHP release { release } has reached end-of-life and will not receive further security updates", "system.issues.debug": "Debugging must be turned off in production", "system.issues.git": "The .git folder seems to be exposed", "system.issues.https": "We recommend HTTPS for all your sites", "system.issues.kirby": "The kirby folder seems to be exposed", + "system.issues.local": "The site is running locally with relaxed security checks", "system.issues.site": "The site folder seems to be exposed", + "system.issues.vue.compiler": "The Vue template compiler is enabled", "system.issues.vulnerability.kirby": "Your installation might be affected by the following vulnerability ({ severity } severity): { description }", "system.issues.vulnerability.plugin": "Your installation might be affected by the following vulnerability in the { plugin } plugin ({ severity } severity): { description }", "system.updateStatus": "Update status", @@ -520,12 +694,21 @@ "system.updateStatus.update": "Free update { version } available", "system.updateStatus.upgrade": "Upgrade { version } available", - "title": "Заглавие", + "tel": "Phone", + "tel.placeholder": "+49123456789", "template": "Образец", + + "theme": "Theme", + "theme.light": "Lights on", + "theme.dark": "Lights off", + "theme.automatic": "Match system default", + + "title": "Заглавие", "today": "Днес", + "toolbar.button.clear": "Clear formatting", "toolbar.button.code": "Код", - "toolbar.button.bold": "Получер шрифт", + "toolbar.button.bold": "\u041f\u043e\u043b\u0443\u0447\u0435\u0440 \u0448\u0440\u0438\u0444\u0442", "toolbar.button.email": "Email", "toolbar.button.headings": "Заглавия", "toolbar.button.heading.1": "Заглавие 1", @@ -534,13 +717,15 @@ "toolbar.button.heading.4": "Heading 4", "toolbar.button.heading.5": "Heading 5", "toolbar.button.heading.6": "Heading 6", - "toolbar.button.italic": "Наклонен шрифт", + "toolbar.button.italic": "\u041d\u0430\u043a\u043b\u043e\u043d\u0435\u043d \u0448\u0440\u0438\u0444\u0442", "toolbar.button.file": "Файл", "toolbar.button.file.select": "Select a file", "toolbar.button.file.upload": "Upload a file", - "toolbar.button.link": "Връзка", + "toolbar.button.link": "\u0412\u0440\u044a\u0437\u043a\u0430", "toolbar.button.paragraph": "Paragraph", "toolbar.button.strike": "Strike-through", + "toolbar.button.sub": "Subscript", + "toolbar.button.sup": "Superscript", "toolbar.button.ol": "Подреден списък", "toolbar.button.underline": "Underline", "toolbar.button.ul": "Списък", @@ -550,6 +735,8 @@ "translation.name": "Български", "translation.locale": "bg_BG", + "type": "Type", + "upload": "Прикачи", "upload.error.cantMove": "The uploaded file could not be moved", "upload.error.cantWrite": "Failed to write file to disk", @@ -562,7 +749,7 @@ "upload.error.noFiles": "No files were uploaded", "upload.error.partial": "The uploaded file was only partially uploaded", "upload.error.tmpDir": "Missing a temporary folder", - "upload.errors": "Error", + "upload.errors": "Грешка", "upload.progress": "Uploading…", "url": "Url", @@ -574,6 +761,7 @@ "user.changeLanguage": "Промени език", "user.changeName": "Преименувай този потребител", "user.changePassword": "Промени парола", + "user.changePassword.current": "Your current password", "user.changePassword.new": "Нова парола", "user.changePassword.new.confirm": "Потвърдете новата парола...", "user.changeRole": "Променете роля", @@ -584,18 +772,21 @@ "users": "Потребители", - "version": "Версия на Kirby", + "version": "\u0412\u0435\u0440\u0441\u0438\u044f \u043d\u0430 Kirby", + "version.changes": "Changed version", + "version.compare": "Compare versions", "version.current": "Current version", "version.latest": "Latest version", "versionInformation": "Version information", - "view.account": "Вашия акаунт", - "view.installation": "Инсталация", + "view": "View", + "view.account": "\u0412\u0430\u0448\u0438\u044f \u0430\u043a\u0430\u0443\u043d\u0442", + "view.installation": "\u0418\u043d\u0441\u0442\u0430\u043b\u0430\u0446\u0438\u044f", "view.languages": "Езици", "view.resetPassword": "Reset password", "view.site": "Сайт", "view.system": "System", - "view.users": "Потребители", + "view.users": "\u041f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u0438", "welcome": "Добре дошли", "year": "Year", diff --git a/kirby/i18n/translations/ca.json b/kirby/i18n/translations/ca.json index 92129e8..6a0a49a 100644 --- a/kirby/i18n/translations/ca.json +++ b/kirby/i18n/translations/ca.json @@ -3,19 +3,28 @@ "account.delete": "Delete your account", "account.delete.confirm": "Do you really want to delete your account? You will be logged out immediately. Your account cannot be recovered.", + "activate": "Activate", "add": "Afegir", + "alpha": "Alpha", "author": "Author", "avatar": "Imatge del perfil", "back": "Tornar", - "cancel": "Cancel·lar", + "cancel": "Cancel\u00b7lar", "change": "Canviar", "close": "Tancar", + "changes": "Changes", "confirm": "Ok", "collapse": "Col·lapsar", "collapse.all": "Col·lapsar tot", + "color": "Color", + "coordinates": "Coordinates", "copy": "Copiar", "copy.all": "Copy all", + "copy.success": "{count} copied!", + "copy.success.multiple": "{count} copied!", + "copy.url": "Copy URL", "create": "Crear", + "custom": "Custom", "date": "Data", "date.select": "Selecciona una data", @@ -34,13 +43,20 @@ "delete": "Eliminar", "delete.all": "Eliminar tot", + "dialog.fields.empty": "This dialog has no fields", "dialog.files.empty": "No hi ha cap fitxer per seleccionar", "dialog.pages.empty": "No hi ha cap pàgina per seleccionar", + "dialog.text.empty": "This dialog does not define any text", "dialog.users.empty": "No hi ha cap usuari per seleccionar", "dimensions": "Dimensions", + "disable": "Disable", "disabled": "Desactivat", "discard": "Descartar", + + "drawer.fields.empty": "This drawer has no fields", + + "domain": "Domain", "download": "Descarregar", "duplicate": "Duplicar", @@ -49,11 +65,13 @@ "email": "Email", "email.placeholder": "mail@exemple.com", + "enter": "Enter", "entries": "Entries", "entry": "Entry", "environment": "Environment", + "error": "Error", "error.access.code": "Codi invàlid", "error.access.login": "Inici de sessió no vàlid", "error.access.panel": "No tens permís per accedir al panell", @@ -74,13 +92,31 @@ "error.cache.type.invalid": "Invalid cache type \"{type}\"", + "error.content.lock.delete": "The version is locked and cannot be deleted", + "error.content.lock.move": "The source version is locked and cannot be moved", + "error.content.lock.publish": "This version is already published", + "error.content.lock.replace": "The version is locked and cannot be replaced", + "error.content.lock.update": "The version is locked and cannot be updated", + + "error.entries.max.plural": "You must not add more than {max} entries", + "error.entries.max.singular": "You must not add more than one entry", + "error.entries.min.plural": "You must add at least {min} entries", + "error.entries.min.singular": "You must add at least one entry", + "error.entries.supports": "\"{type}\" field type is not supported for the entries field", + "error.entries.validation": "There's an error on the \"{field}\" field in row {index}", + "error.email.preset.notFound": "No es pot trobar la configuració de correu electrònic \"{name}\"", "error.field.converter.invalid": "Convertidor no vàlid \"{converter}\"", + "error.field.link.options": "Invalid options: {options}", "error.field.type.missing": "Field \"{ name }\": The field type \"{ type }\" does not exist", "error.file.changeName.empty": "El nom no pot estar buit", "error.file.changeName.permission": "No tens permís per canviar el nom de \"{filename}\"", + "error.file.changeTemplate.invalid": "The template for the file \"{id}\" cannot be changed to \"{template}\" (valid: \"{blueprints}\")", + "error.file.changeTemplate.permission": "You are not allowed to change the template for the file \"{id}\"", + + "error.file.delete.multiple": "Not all files could be deleted. Try each remaining file individually to see the specific error that prevents deletion.", "error.file.duplicate": "Ja existeix un fitxer amb el nom \"{filename}\"", "error.file.extension.forbidden": "L'extensió de l'arxiu \"{extension}\" no està permesa", "error.file.extension.invalid": "Invalid extension: {extension}", @@ -95,9 +131,11 @@ "error.file.minheight": "L'alçada de la imatge ha de ser com a mínim de {height} píxels", "error.file.minsize": "El fitxer és massa petit", "error.file.minwidth": "L'amplada de la imatge ha de ser com a mínim de {width} píxels", + "error.file.name.unique": "The filename must be unique", "error.file.name.missing": "El nom del fitxer no pot estar buit", "error.file.notFound": "L'arxiu \"{filename}\" no s'ha trobat", "error.file.orientation": "L’orientació de la imatge ha de ser \"{orientation}\"", + "error.file.sort.permission": "You are not allowed to change the sorting of \"{filename}\"", "error.file.type.forbidden": "No tens permís per penjar fitxers {type}", "error.file.type.invalid": "Invalid file type: {type}", "error.file.undefined": "L'arxiu no s'ha trobat", @@ -106,22 +144,30 @@ "error.form.notSaved": "No s'ha pogut desar el formulari", "error.language.code": "Introdueix un codi vàlid per a l’idioma", + "error.language.create.permission": "You are not allowed to create a language", + "error.language.delete.permission": "You are not allowed to delete the language", "error.language.duplicate": "L'idioma ja existeix", "error.language.name": "Introdueix un nom vàlid per a l'idioma", "error.language.notFound": "The language could not be found", + "error.language.update.permission": "You are not allowed to update the language", "error.layout.validation.block": "There's an error on the \"{field}\" field in block {blockIndex} using the \"{fieldset}\" block type in layout {layoutIndex}", "error.layout.validation.settings": "There's an error in layout {index} settings", - "error.license.format": "Introduïu una clau de llicència vàlida", + "error.license.domain": "The domain for the license is missing", "error.license.email": "Si us plau, introdueix una adreça de correu electrònic vàlida", + "error.license.format": "Please enter a valid license code", "error.license.verification": "No s’ha pogut verificar la llicència", + "error.login.totp.confirm.invalid": "Codi invàlid", + "error.login.totp.confirm.missing": "Please enter the current code", + "error.object.validation": "There’s an error in the \"{label}\" field:\n{message}", "error.offline": "The Panel is currently offline", "error.page.changeSlug.permission": "No teniu permís per canviar l'apèndix d'URL per a \"{slug}\"", + "error.page.changeSlug.reserved": "The path of top-level pages must not start with \"{path}\"", "error.page.changeStatus.incomplete": "La pàgina té errors i no es pot publicar", "error.page.changeStatus.permission": "No es pot canviar l'estat d'aquesta pàgina", "error.page.changeStatus.toDraft.invalid": "La pàgina \"{slug}\" no es pot convertir en un esborrany", @@ -133,17 +179,25 @@ "error.page.delete": "La pàgina \"{slug}\" no es pot esborrar", "error.page.delete.confirm": "Si us plau, introdueix el títol de la pàgina per confirmar", "error.page.delete.hasChildren": "La pàgina té subpàgines i no es pot esborrar", + "error.page.delete.multiple": "Not all pages could be deleted. Try each remaining page individually to see the specific error that prevents deletion.", "error.page.delete.permission": "No tens permís per esborrar \"{slug}\"", "error.page.draft.duplicate": "Ja existeix un esborrany de pàgina amb l'apèndix d'URL \"{slug}\"", "error.page.duplicate": "Ja existeix una pàgina amb l'apèndix d'URL \"{slug}\"", "error.page.duplicate.permission": "No tens permís per duplicar \"{slug}\"", + "error.page.move.ancestor": "The page cannot be moved into itself", + "error.page.move.directory": "The page directory cannot be moved", + "error.page.move.duplicate": "A sub page with the URL appendix \"{slug}\" already exists", + "error.page.move.noSections": "The page \"{parent}\" cannot be a parent of any page because it lacks any pages sections in its blueprint", + "error.page.move.notFound": "The moved page could not be found", + "error.page.move.permission": "You are not allowed to move \"{slug}\"", + "error.page.move.template": "The \"{template}\" template is not accepted as a subpage of \"{parent}\"", "error.page.notFound": "La pàgina \"{slug}\" no s'ha trobat", "error.page.num.invalid": "Si us plau, introdueix un número d 'ordenació vàlid. Els números no poden ser negatius.", "error.page.slug.invalid": "Please enter a valid URL appendix", "error.page.slug.maxlength": "La longitud del nom ha de tenir menys de caràcters \"{length}\"", "error.page.sort.permission": "La pàgina \"{slug}\" no es pot ordenar", "error.page.status.invalid": "Si us plau, estableix un estat de pàgina vàlid", - "error.page.undefined": "La pàgina no s'ha trobat", + "error.page.undefined": "La p\u00e0gina no s'ha trobat", "error.page.update.permission": "No tens permís per actualitzar \"{slug}\"", "error.section.files.max.plural": "No has d'afegir més de {max} fitxers a la secció \"{section}\"", @@ -163,6 +217,8 @@ "error.site.changeTitle.permission": "No tens permís per canviar el títol del lloc web", "error.site.update.permission": "No tens permís per actualitzar el lloc web", + "error.structure.validation": "There's an error on the \"{field}\" field in row {index}", + "error.template.default.notFound": "La plantilla predeterminada no existeix", "error.unexpected": "An unexpected error occurred! Enable debug mode for more info: https://getkirby.com/docs/reference/system/options/debug", @@ -176,7 +232,7 @@ "error.user.changeRole.toAdmin": "No tens permís per promocionar algú al rol d’administrador", "error.user.create.permission": "No tens permís per crear aquest usuari", "error.user.delete": "L'usuari \"{name}\" no es pot eliminar", - "error.user.delete.lastAdmin": "No es pot eliminar l'últim administrador", + "error.user.delete.lastAdmin": "No es pot eliminar l'\u00faltim administrador", "error.user.delete.lastUser": "El darrer usuari no es pot eliminar", "error.user.delete.permission": "No pots eliminar l'usuari \"{name}\"", "error.user.duplicate": "Ja existeix un usuari amb l'adreça electrònica \"{email}\"", @@ -195,8 +251,10 @@ "error.validation.accepted": "Si us plau confirma", "error.validation.alpha": "Si us plau, introdueix únicament caràcters entre a-z", "error.validation.alphanum": "Si us plau, introdueix únicament caràcters entre a-z o números de 0-9", + "error.validation.anchor": "Please enter a correct link anchor", "error.validation.between": "Introdueix un valor entre \"{min}\" i \"{max}\"", "error.validation.boolean": "Si us plau confirma o denega", + "error.validation.color": "Please enter a valid color in the {format} format", "error.validation.contains": "Si us plau, introduïu un valor que contingui \"{needle}\"", "error.validation.date": "Si us plau, introdueix una data vàlida", "error.validation.date.after": "Introdueix una data posterior {date}", @@ -211,6 +269,7 @@ "error.validation.integer": "Si us plau, introduïu un nombre enter vàlid", "error.validation.ip": "Si us plau, introduïu una adreça IP vàlida", "error.validation.less": "Si us plau, introduïu un valor inferior a {max}", + "error.validation.linkType": "The link type is not allowed", "error.validation.match": "El valor no coincideix amb el patró esperat", "error.validation.max": "Si us plau, introduïu un valor igual o inferior a {max}", "error.validation.maxlength": "Si us plau, introduïu un valor més curt. (màxim {max} caràcters)", @@ -227,15 +286,18 @@ "error.validation.same": "Si us plau, introduïu \"{other}\"", "error.validation.size": "La mida del valor ha de ser \"{size}\"", "error.validation.startswith": "El valor ha de començar amb \"{start}\"", + "error.validation.tel": "Please enter an unformatted phone number", "error.validation.time": "Si us plau, introduïu una hora vàlida", "error.validation.time.after": "Please enter a time after {time}", "error.validation.time.before": "Please enter a time before {time}", "error.validation.time.between": "Please enter a time between {min} and {max}", + "error.validation.uuid": "Please enter a valid UUID", "error.validation.url": "Si us plau, introduïu una URL vàlida", "expand": "Expandir", "expand.all": "Expandir tot", + "field.invalid": "The field is invalid", "field.required": "El camp és obligatori", "field.blocks.changeType": "Change type", "field.blocks.code.name": "Codi", @@ -245,8 +307,9 @@ "field.blocks.delete.confirm.all": "Do you really want to delete all blocks?", "field.blocks.delete.confirm.selected": "Do you really want to delete the selected blocks?", "field.blocks.empty": "No blocks yet", + "field.blocks.fieldsets.empty": "No fieldsets yet", "field.blocks.fieldsets.label": "Please select a block type …", - "field.blocks.fieldsets.paste": "Press {{ shortcut }} to paste/import blocks from your clipboard", + "field.blocks.fieldsets.paste": "Press {{ shortcut }} to import layouts/blocks from your clipboard Only those allowed in the current field will get inserted.", "field.blocks.gallery.name": "Gallery", "field.blocks.gallery.images.empty": "No images yet", "field.blocks.gallery.images.label": "Images", @@ -254,11 +317,16 @@ "field.blocks.heading.name": "Heading", "field.blocks.heading.text": "Text", "field.blocks.heading.placeholder": "Heading …", + "field.blocks.figure.back.plain": "Plain", + "field.blocks.figure.back.pattern.light": "Pattern (light)", + "field.blocks.figure.back.pattern.dark": "Pattern (dark)", "field.blocks.image.alt": "Alternative text", "field.blocks.image.caption": "Caption", "field.blocks.image.crop": "Crop", "field.blocks.image.link": "Enllaç", "field.blocks.image.location": "Location", + "field.blocks.image.location.internal": "This website", + "field.blocks.image.location.external": "External source", "field.blocks.image.name": "Imatge", "field.blocks.image.placeholder": "Select an image", "field.blocks.image.ratio": "Ratio", @@ -275,38 +343,72 @@ "field.blocks.quote.citation.placeholder": "by …", "field.blocks.text.name": "Text", "field.blocks.text.placeholder": "Text …", + "field.blocks.video.autoplay": "Autoplay", "field.blocks.video.caption": "Caption", + "field.blocks.video.controls": "Controls", + "field.blocks.video.location": "Location", + "field.blocks.video.loop": "Loop", + "field.blocks.video.muted": "Muted", "field.blocks.video.name": "Video", "field.blocks.video.placeholder": "Enter a video URL", + "field.blocks.video.poster": "Poster", + "field.blocks.video.preload": "Preload", "field.blocks.video.url.label": "Video-URL", "field.blocks.video.url.placeholder": "https://youtube.com/?v=", - "field.files.empty": "Encara no hi ha cap fitxer seleccionat", + "field.entries.delete.confirm.all": "Do you really want to delete all entries?", + "field.entries.empty": "Encara no hi ha entrades.", + "field.files.empty": "Encara no hi ha cap fitxer seleccionat", + "field.files.empty.single": "No file selected yet", + + "field.layout.change": "Change layout", "field.layout.delete": "Delete layout", "field.layout.delete.confirm": "Do you really want to delete this layout?", + "field.layout.delete.confirm.all": "Do you really want to delete all layouts?", "field.layout.empty": "No rows yet", "field.layout.select": "Select a layout", "field.object.empty": "No information yet", "field.pages.empty": "Encara no s'ha seleccionat cap pàgina", + "field.pages.empty.single": "No page selected yet", "field.structure.delete.confirm": "Segur que voleu eliminar aquesta fila?", "field.structure.delete.confirm.all": "Do you really want to delete all entries?", "field.structure.empty": "Encara no hi ha entrades.", "field.users.empty": "Encara no s'ha seleccionat cap usuari", + "field.users.empty.single": "No user selected yet", + "fields.empty": "No fields yet", + + "file": "Arxiu", "file.blueprint": "This file has no blueprint yet. You can define the setup in /site/blueprints/files/{blueprint}.yml", + "file.changeTemplate": "Canviar la plantilla", + "file.changeTemplate.notice": "Changing the file's template will remove content for fields that don't match in type. If the new template defines certain rules, e.g. image dimensions, those will also be applied irreversibly. Use with caution.", "file.delete.confirm": "Esteu segurs d'eliminar
{filename}?", + "file.focus.placeholder": "Set focal point", + "file.focus.reset": "Remove focal point", + "file.focus.title": "Focus", "file.sort": "Change position", "files": "Arxius", + "files.delete.confirm.selected": "Do you really want to delete the selected files? This action cannot be undone.", "files.empty": "Encara no hi ha fitxers", + "filter": "Filter", + + "form.discard": "Discard changes", + "form.discard.confirm": "Do you really want to discard all your changes?", + "form.locked": "This content is disabled for you as it is currently edited by another user", + "form.unsaved": "The current changes have not yet been saved", + "form.preview": "Preview changes", + "form.preview.draft": "Preview draft", + "hide": "Hide", "hour": "Hora", + "hue": "Hue", "import": "Import", "info": "Info", "insert": "Insertar", @@ -324,7 +426,6 @@ "installation.issues.mbstring": "Es requereix l'extensió de MB String", "installation.issues.media": "La carpeta /media no existeix o no es pot escriure", "installation.issues.php": "Assegureu-vos d'utilitzar PHP 8+", - "installation.issues.server": "Kirby requereix Apache, Nginx o Caddy", "installation.issues.sessions": "La carpeta /site/sessions no existeix o no es pot escriure", "language": "Idioma", @@ -332,6 +433,7 @@ "language.convert": "Fer per defecte", "language.convert.confirm": "

Segur que voleu convertir {name} a l'idioma predeterminat? Això no es pot desfer.

Si {name} té contingut no traduït, ja no podreu tornar enrere i algunes parts del vostre lloc poden quedar buides.

", "language.create": "Afegir un nou idioma", + "language.default": "Idioma per defecte", "language.delete.confirm": "Segur que voleu eliminar l'idioma {name} incloent totes les traduccions? Això no es pot desfer!", "language.deleted": "S'ha suprimit l'idioma", "language.direction": "Direcció de lectura", @@ -340,7 +442,16 @@ "language.locale": "Cadena local de PHP", "language.locale.warning": "S'està fent servir una configuració regional personalitzada. Modifica el fitxer d'idioma a /site/languages", "language.name": "Nom", + "language.secondary": "Secondary language", + "language.settings": "Language settings", "language.updated": "S'ha actualitzat l'idioma", + "language.variables": "Language variables", + "language.variables.empty": "No translations yet", + + "language.variable.delete.confirm": "Do you really want to delete the variable for {key}?", + "language.variable.key": "Key", + "language.variable.notFound": "The variable could not be found", + "language.variable.value": "Value", "languages": "Idiomes", "languages.default": "Idioma per defecte", @@ -348,36 +459,57 @@ "languages.secondary": "Idiomes secundaris", "languages.secondary.empty": "Encara no hi ha idiomes secundaris", - "license": "Llicència Kirby", + "license": "Llic\u00e8ncia Kirby", + "license.activate": "Activate it now", + "license.activate.label": "Please activate your license", + "license.activate.domain": "Your license will be activated for {host}.", + "license.activate.local": "You are about to activate your Kirby license for your local domain {host}. If this site will be deployed to a public domain, please activate it there instead. If {host} is the domain you want to use your license for, please continue.", + "license.activated": "Activated", "license.buy": "Comprar una llicència", - "license.register": "Registrar", + "license.code": "Codi", + "license.code.help": "You received your license code after the purchase via email. Please copy and paste it here.", + "license.code.label": "Si us plau, introdueixi el seu codi de llicència", + "license.status.active.info": "Includes new major versions until {date}", + "license.status.active.label": "Valid license", + "license.status.demo.info": "This is a demo installation", + "license.status.demo.label": "Demo", + "license.status.inactive.info": "Renew license to update to new major versions", + "license.status.inactive.label": "No new major versions", + "license.status.legacy.bubble": "Ready to renew your license?", + "license.status.legacy.info": "Your license does not cover this version", + "license.status.legacy.label": "Please renew your license", + "license.status.missing.bubble": "Ready to launch your site?", + "license.status.missing.info": "No valid license", + "license.status.missing.label": "Please activate your license", + "license.status.unknown.info": "The license status is unknown", + "license.status.unknown.label": "Unknown", "license.manage": "Manage your licenses", - "license.register.help": "Heu rebut el codi de la vostra llicència després de la compra, per correu electrònic. Copieu-lo i enganxeu-lo per registrar-vos.", - "license.register.label": "Si us plau, introdueixi el seu codi de llicència", - "license.register.domain": "Your license will be registered to {host}.", - "license.register.local": "You are about to register your license for your local domain {host}. If this site will be deployed to a public domain, please register it there instead. If {host} is the domain you want to license Kirby to, please continue.", - "license.register.success": "Gràcies per donar suport a Kirby", - "license.unregistered": "Aquesta és una demo no registrada de Kirby", + "license.purchased": "Purchased", + "license.success": "Gràcies per donar suport a Kirby", "license.unregistered.label": "Unregistered", - "link": "Enllaç", + "link": "Enlla\u00e7", "link.text": "Enllaç de text", "loading": "Carregant", "lock.unsaved": "Canvis no guardats", "lock.unsaved.empty": "Ja no hi ha canvis no guardats", - "lock.isLocked": "Canvis no guardats per {email}", - "lock.file.isLocked": "El fitxer està sent editat actualment per {email} i no pot ser modificat.", - "lock.page.isLocked": "La pàgina està sent editat actualment per {email} i no pot ser modificat.", + "lock.unsaved.files": "Unsaved files", + "lock.unsaved.pages": "Unsaved pages", + "lock.unsaved.users": "Unsaved accounts", + "lock.isLocked": "Unsaved changes by {email}", "lock.unlock": "Desbloquejar", - "lock.isUnlocked": "Els teus canvis sense guardar han estat sobreescrits per a un altra usuario. Pots descarregar els teus canvis per combinar-los manualment.", + "lock.unlock.submit": "Unlock and overwrite unsaved changes by {email}", + "lock.isUnlocked": "Was unlocked by another user", "login": "Entrar", "login.code.label.login": "Login code", "login.code.label.password-reset": "Password reset code", "login.code.placeholder.email": "000 000", + "login.code.placeholder.totp": "000000", "login.code.text.email": "If your email address is registered, the requested code was sent via email.", + "login.code.text.totp": "Please enter the one‑time code from your authenticator app.", "login.email.login.body": "Hi {user.nameOrEmail},\n\nYou recently requested a login code for the Panel of {site}.\nThe following login code will be valid for {timeout} minutes:\n\n{code}\n\nIf you did not request a login code, please ignore this email or contact your administrator if you have questions.\nFor security, please DO NOT forward this email.", "login.email.login.subject": "Your login code", "login.email.password-reset.body": "Hi {user.nameOrEmail},\n\nYou recently requested a password reset code for the Panel of {site}.\nThe following password reset code will be valid for {timeout} minutes:\n\n{code}\n\nIf you did not request a password reset code, please ignore this email or contact your administrator if you have questions.\nFor security, please DO NOT forward this email.", @@ -388,9 +520,24 @@ "login.toggleText.code.email-password": "Login with password", "login.toggleText.password-reset.email": "Forgot your password?", "login.toggleText.password-reset.email-password": "← Back to login", + "login.totp.enable.option": "Set up one‑time codes", + "login.totp.enable.intro": "Authenticator apps can generate one‑time codes that are used as a second factor when signing into your account.", + "login.totp.enable.qr.label": "1. Scan this QR code", + "login.totp.enable.qr.help": "Unable to scan? Add the setup key {secret} manually to your authenticator app.", + "login.totp.enable.confirm.headline": "2. Confirm with generated code", + "login.totp.enable.confirm.text": "Your app generates a new one‑time code every 30 seconds. Enter the current code to complete the setup:", + "login.totp.enable.confirm.label": "Current code", + "login.totp.enable.confirm.help": "After this setup, we will ask you for a one‑time code every time you log in.", + "login.totp.enable.success": "One‑time codes enabled", + "login.totp.disable.option": "Disable one‑time codes", + "login.totp.disable.label": "Enter your password to disable one‑time codes", + "login.totp.disable.help": "In the future, a different second factor like a login code sent via email will be requested when you log in. You can always set up one‑time codes again later.", + "login.totp.disable.admin": "

This will disable one‑time codes for {user}.

In the future, a different second factor like a login code sent via email will be requested when they log in. {user} can set up one‑time codes again after their next login.

", + "login.totp.disable.success": "One‑time codes disabled", "logout": "Tancar sessió", + "merge": "Merge", "menu": "Menú", "meridiem": "AM/PM", "mime": "Tipus de mitjà", @@ -404,42 +551,49 @@ "months.january": "Gener", "months.july": "Juliol", "months.june": "Juny", - "months.march": "Març", + "months.march": "Mar\u00e7", "months.may": "Maig", "months.november": "Novembre", "months.october": "Octubre", "months.september": "Setembre", "more": "Més", + "move": "Move", "name": "Nom", "next": "Següent", + "night": "Night", "no": "no", "off": "apagat", "on": "encès", "open": "Obrir", "open.newWindow": "Open in new window", + "option": "Option", "options": "Opcions", "options.none": "Sense opcions", + "options.all": "Show all {count} options", "orientation": "Orientació", "orientation.landscape": "Horitzontal", "orientation.portrait": "Vertical", "orientation.square": "Quadrat", + "page": "Page", "page.blueprint": "This page has no blueprint yet. You can define the setup in /site/blueprints/pages/{blueprint}.yml", "page.changeSlug": "Canviar URL", - "page.changeSlug.fromTitle": "Crear a partir del títol", + "page.changeSlug.fromTitle": "Crear a partir del t\u00edtol", "page.changeStatus": "Canviar l'estat", "page.changeStatus.position": "Si us plau, seleccioneu una posició", "page.changeStatus.select": "Seleccioneu un nou estat", "page.changeTemplate": "Canviar la plantilla", + "page.changeTemplate.notice": "Changing the page's template will remove content for fields that don't match in type. Use with caution.", + "page.create": "Create as {status}", "page.delete.confirm": "Segur que voleu eliminar {title}?", "page.delete.confirm.subpages": "Aquesta pàgina té subpàgines.
Totes les subpàgines també s'eliminaran.", "page.delete.confirm.title": "Introduïu el títol de la pàgina per confirmar", - "page.draft.create": "Crear un esborrany", "page.duplicate.appendix": "Copiar", "page.duplicate.files": "Copiar fitxers", "page.duplicate.pages": "Copiar pàgines", + "page.move": "Move page", "page.sort": "Change position", "page.status": "Estat", "page.status.draft": "Esborrany", @@ -450,24 +604,32 @@ "page.status.unlisted.description": "La pàgina només es pot accedir a través de l'URL", "pages": "Pàgines", + "pages.delete.confirm.selected": "Do you really want to delete the selected pages? This action cannot be undone.", "pages.empty": "Encara no hi ha pàgines", "pages.status.draft": "Esborranys", "pages.status.listed": "Publicat", "pages.status.unlisted": "Sense classificar", - "pagination.page": "Page", + "pagination.page": "Pàgina", "password": "Contrasenya", "paste": "Paste", "paste.after": "Paste after", + "paste.success": "{count} pasted!", "pixel": "Pixel", "plugin": "Plugin", "plugins": "Plugins", "prev": "Anterior", "preview": "Preview", + + "publish": "Publish", + "published": "Publicat", + "remove": "Eliminar", "rename": "Canviar el nom", - "replace": "Reemplaçar", + "renew": "Renew", + "replace": "Reempla\u00e7ar", + "replace.with": "Replace with", "retry": "Reintentar", "revert": "Revertir", "revert.confirm": "Segur que voleu eliminar tots els canvis pendents desar?", @@ -482,11 +644,14 @@ "role.nobody.title": "Ningú", "save": "Desar", + "saved": "Saved", "search": "Cercar", + "searching": "Searching", "search.min": "Introduïu {min} caràcters per cercar", - "search.all": "Mostrar tots", + "search.all": "Show all {count} results", "search.results.none": "Sense resultats", + "section.invalid": "The section is invalid", "section.required": "La secció és obligatòria", "security": "Security", @@ -496,18 +661,27 @@ "show": "Show", "site.blueprint": "The site has no blueprint yet. You can define the setup in /site/blueprints/site.yml", "size": "Tamany", - "slug": "URL-apèndix", + "slug": "URL-ap\u00e8ndix", "sort": "Ordenar", + "sort.drag": "Drag to sort …", + "split": "Split", "stats.empty": "No reports", + "status": "Estat", + + "system.info.copy": "Copy info", + "system.info.copied": "System info copied", "system.issues.content": "The content folder seems to be exposed", "system.issues.eol.kirby": "Your installed Kirby version has reached end-of-life and will not receive further security updates", "system.issues.eol.plugin": "Your installed version of the { plugin } plugin is has reached end-of-life and will not receive further security updates", + "system.issues.eol.php": "Your installed PHP release { release } has reached end-of-life and will not receive further security updates", "system.issues.debug": "Debugging must be turned off in production", "system.issues.git": "The .git folder seems to be exposed", "system.issues.https": "We recommend HTTPS for all your sites", "system.issues.kirby": "The kirby folder seems to be exposed", + "system.issues.local": "The site is running locally with relaxed security checks", "system.issues.site": "The site folder seems to be exposed", + "system.issues.vue.compiler": "The Vue template compiler is enabled", "system.issues.vulnerability.kirby": "Your installation might be affected by the following vulnerability ({ severity } severity): { description }", "system.issues.vulnerability.plugin": "Your installation might be affected by the following vulnerability in the { plugin } plugin ({ severity } severity): { description }", "system.updateStatus": "Update status", @@ -520,10 +694,19 @@ "system.updateStatus.update": "Free update { version } available", "system.updateStatus.upgrade": "Upgrade { version } available", - "title": "Títol", + "tel": "Phone", + "tel.placeholder": "+49123456789", "template": "Plantilla", + + "theme": "Theme", + "theme.light": "Lights on", + "theme.dark": "Lights off", + "theme.automatic": "Match system default", + + "title": "Títol", "today": "Avui", + "toolbar.button.clear": "Clear formatting", "toolbar.button.code": "Codi", "toolbar.button.bold": "Negreta", "toolbar.button.email": "Email", @@ -538,9 +721,11 @@ "toolbar.button.file": "Arxiu", "toolbar.button.file.select": "Selecciona un fitxer", "toolbar.button.file.upload": "Carrega un fitxer", - "toolbar.button.link": "Enllaç", + "toolbar.button.link": "Enlla\u00e7", "toolbar.button.paragraph": "Paragraph", "toolbar.button.strike": "Strike-through", + "toolbar.button.sub": "Subscript", + "toolbar.button.sup": "Superscript", "toolbar.button.ol": "Llista ordenada", "toolbar.button.underline": "Underline", "toolbar.button.ul": "Llista de vinyetes", @@ -550,6 +735,8 @@ "translation.name": "Catalan", "translation.locale": "ca_ES", + "type": "Type", + "upload": "Carregar", "upload.error.cantMove": "El fitxer carregat no s'ha pogut moure", "upload.error.cantWrite": "No s'ha pogut escriure el fitxer al disc", @@ -574,6 +761,7 @@ "user.changeLanguage": "Canviar idioma", "user.changeName": "Canviar el nom d'aquest usuari", "user.changePassword": "Canviar contrasenya", + "user.changePassword.current": "Your current password", "user.changePassword.new": "Nova contrasenya", "user.changePassword.new.confirm": "Confirma la nova contrasenya ...", "user.changeRole": "Canviar el rol", @@ -584,11 +772,14 @@ "users": "Usuaris", - "version": "Versió de Kirby", + "version": "Versi\u00f3 de Kirby", + "version.changes": "Changed version", + "version.compare": "Compare versions", "version.current": "Current version", "version.latest": "Latest version", "versionInformation": "Version information", + "view": "View", "view.account": "La teva compta", "view.installation": "Instal·lació", "view.languages": "Idiomes", diff --git a/kirby/i18n/translations/cs.json b/kirby/i18n/translations/cs.json index b094bd0..f2564fb 100644 --- a/kirby/i18n/translations/cs.json +++ b/kirby/i18n/translations/cs.json @@ -1,32 +1,41 @@ { - "account.changeName": "Přejmenovat uživatele", + "account.changeName": "Změnit jméno", "account.delete": "Smazat účet", "account.delete.confirm": "Opravdu chcete smazat svůj účet? Budete okamžitě odhlášeni. Účet nemůže být zpětně obnoven.", - "add": "Přidat", + "activate": "Aktivovat", + "add": "P\u0159idat", + "alpha": "Alfa", "author": "Autor", - "avatar": "Profilový obrázek", + "avatar": "Profilov\u00fd obr\u00e1zek", "back": "Zpět", - "cancel": "Zrušit", - "change": "Změnit", + "cancel": "Zru\u0161it", + "change": "Zm\u011bnit", "close": "Zavřít", + "changes": "Změny", "confirm": "Ok", "collapse": "Sbalit", "collapse.all": "Sbalit vše", + "color": "Barva", + "coordinates": "Souřadnice", "copy": "Kopírovat", "copy.all": "Kopírovat vše", + "copy.success": "{count} zkopírováno!", + "copy.success.multiple": "{count} copied!", + "copy.url": "Kopírovat URL", "create": "Vytvořit", + "custom": "Vlastní", "date": "Datum", "date.select": "Vyberte datum", "day": "Den", - "days.fri": "pá", + "days.fri": "p\u00e1", "days.mon": "po", "days.sat": "so", "days.sun": "ne", - "days.thu": "čt", - "days.tue": "út", + "days.thu": "\u010dt", + "days.tue": "\u00fat", "days.wed": "st", "debugging": "Ladění", @@ -34,13 +43,20 @@ "delete": "Smazat", "delete.all": "Smazat vše", + "dialog.fields.empty": "Tento dialog neobsahuje žádná pole", "dialog.files.empty": "Žádné soubory k výběru", "dialog.pages.empty": "Žádné stránky k výběru", + "dialog.text.empty": "Tento dialog nemá definovaný žádný text", "dialog.users.empty": "Žádní uživatelé k výběru", "dimensions": "Rozměry", + "disable": "Deaktivovat", "disabled": "Zakázáno", "discard": "Zahodit", + + "drawer.fields.empty": "Tento vysouvací panel nemá žádná pole", + + "domain": "Doména", "download": "Stáhnout", "duplicate": "Duplikovat", @@ -49,18 +65,20 @@ "email": "Email", "email.placeholder": "mail@example.com", + "enter": "Zapsat", "entries": "Záznamy", "entry": "Záznam", "environment": "Prostředí", + "error": "Chyba", "error.access.code": "Neplatný kód", "error.access.login": "Neplatné přihlášení", "error.access.panel": "Nemáte oprávnění k přihlášení do panelu", "error.access.view": "Nemáte oprávnění ke vstupu do této části panelu.", "error.avatar.create.fail": "Nebylo možné nahrát profilový obrázek", - "error.avatar.delete.fail": "Nebylo možné smazat profilový obrázek", + "error.avatar.delete.fail": "Nebylo mo\u017en\u00e9 smazat profilov\u00fd obr\u00e1zek", "error.avatar.dimensions.invalid": "Šířka a výška obrázku musí být pod 3000 pixelů", "error.avatar.mime.forbidden": "Profilový obrázek musí být ve formátu JPEG nebo PNG", @@ -74,17 +92,35 @@ "error.cache.type.invalid": "Neplatný typ cache \"{type}\"", + "error.content.lock.delete": "Tato verze je uzamčená a nelze jí smazat", + "error.content.lock.move": "Zdrojová verze je uzamčená a nemůže být přesunuta", + "error.content.lock.publish": "Tato verze je již zveřejněná", + "error.content.lock.replace": "Tato verze je uzamčená a nemůže být nahrazena", + "error.content.lock.update": "Tato verze je uzamčená a nemůže být aktualizována", + + "error.entries.max.plural": "Nelze přidat více než {max} záznamů", + "error.entries.max.singular": "Nelze přidat více než jeden záznam", + "error.entries.min.plural": "Musíte přidat alespoň {min} záznamů", + "error.entries.min.singular": "Musíte přidat alespoň jeden záznam", + "error.entries.supports": "\"{type}\" typ pole není pro záznamy podporován", + "error.entries.validation": "Chyba v poli \"{field}\" na řádku {index}", + "error.email.preset.notFound": "Nelze nalézt emailové přednastavení \"{name}\"", "error.field.converter.invalid": "Neplatný konvertor \"{converter}\"", + "error.field.link.options": "Neplatné volby: {options}", "error.field.type.missing": "Pole \"{ name }\": Typ pole \"{ type }\" neexistuje", "error.file.changeName.empty": "Toto jméno nesmí být prázdné", "error.file.changeName.permission": "Nemáte povoleno změnit jméno souboru \"{filename}\"", + "error.file.changeTemplate.invalid": "Šablonu souboru \"{id}\" nelze změnit na \"{template}\" (platné: \"{blueprints}\")", + "error.file.changeTemplate.permission": "Nemáte dovoleno změnit šablonu souboru \"{id}\"", + + "error.file.delete.multiple": "Nebylo možné vymazat všechny soubory. Zkuste zbývající soubory vymazat postupně, abyste nalezli chybu, která bránila hromadnému smazání.", "error.file.duplicate": "Soubor s názvem \"{filename}\" již existuje", "error.file.extension.forbidden": "Přípona souboru \"{extension}\" není povolena", "error.file.extension.invalid": "Neplatná přípona souboru: {extension}", - "error.file.extension.missing": "Nemůžete nahrát soubor bez přípony", + "error.file.extension.missing": "Nem\u016f\u017eete nahr\u00e1t soubor bez p\u0159\u00edpony", "error.file.maxheight": "Výška obrázku nesmí přesáhnout {height} pixelů", "error.file.maxsize": "Soubor je příliš velký", "error.file.maxwidth": "Šířka obrázku nesmí přesáhnout {width} pixelů", @@ -95,33 +131,43 @@ "error.file.minheight": "Výška obrázku musí být alespoň {height} pixelů", "error.file.minsize": "Soubor je příliš malý", "error.file.minwidth": "Šířka obrázku musí být alespoň {width} pixelů", + "error.file.name.unique": "Název souboru musí být unikátní", "error.file.name.missing": "Název souboru nesmí být prázdný", - "error.file.notFound": "Soubor se nepodařilo nalézt", + "error.file.notFound": "Soubor se nepoda\u0159ilo nal\u00e9zt", "error.file.orientation": "Orientace obrázku másí být \"{orientation}\"", + "error.file.sort.permission": "Nemáte dovoleno změnit pozici \"{filename}\"", "error.file.type.forbidden": "Nemáte povoleno nahrávat soubory typu {type} ", "error.file.type.invalid": "Neplatný typ souboru: {type}", - "error.file.undefined": "Soubor se nepodařilo nalézt", + "error.file.undefined": "Soubor se nepoda\u0159ilo nal\u00e9zt", "error.form.incomplete": "Prosím opravte všechny chyby ve formuláři", "error.form.notSaved": "Formulář nemohl být uložen", "error.language.code": "Zadejte prosím platný kód jazyka", + "error.language.create.permission": "Nemáte dovoleno vytvořit jazyk", + "error.language.delete.permission": "Nemáte dovoleno jazyk vymazat", "error.language.duplicate": "Jazyk již existuje", "error.language.name": "Zadejte prosím platné jméno jazyka", "error.language.notFound": "Jazyk nebyl nalezen", + "error.language.update.permission": "Nemáte dovoleno aktualizovat jazyk", "error.layout.validation.block": "V rozvržení {layoutIndex} je v poli \"{field}\" v bloku {blockIndex} při použití \"{fieldset}\" typu chyba", "error.layout.validation.settings": "Chyba v nastavení rozvržení {index}", - "error.license.format": "Zadejte prosím platné licenční číslo", + "error.license.domain": "Licenčnímu klíči chybí doména", "error.license.email": "Zadejte prosím platnou emailovou adresu", + "error.license.format": "Zadejte prosím platné licenční číslo", "error.license.verification": "Licenci nelze ověřit", + "error.login.totp.confirm.invalid": "Neplatný kód", + "error.login.totp.confirm.missing": "Zadejte prosím licenční kód", + "error.object.validation": "V poli \"{label}\" je chyba:\n{message}", "error.offline": "Panel je v současnosti off-line", - "error.page.changeSlug.permission": "Nemůžete změnit URL této stránky", + "error.page.changeSlug.permission": "Nem\u016f\u017eete zm\u011bnit URL t\u00e9to str\u00e1nky", + "error.page.changeSlug.reserved": "Cesta k stránkám na nevyšší úrovni nesmí začínat jako \"{path}\"", "error.page.changeStatus.incomplete": "Stránka obsahuje chyby a nemohla být zveřejněna", "error.page.changeStatus.permission": "Status této stránky nelze změnit", "error.page.changeStatus.toDraft.invalid": "Stránka \"{slug}\" nemůže být převedena na koncept", @@ -133,17 +179,25 @@ "error.page.delete": "Stránku \"{slug}\" nelze vymazat", "error.page.delete.confirm": "Pro potvrzení prosím zadejte titulek stránky", "error.page.delete.hasChildren": "Stránka má podstránky, nemůže být vymazána", + "error.page.delete.multiple": "Nebylo možné vymazat všechny stránky. Zkuste zbývající stránky vymazat postupně, abyste nalezli chybu, která bránila hromadnému smazání.", "error.page.delete.permission": "Nemáte dovoleno odstranit \"{slug}\"", "error.page.draft.duplicate": "Koncept stránky, který obsahuje v adrese URL \"{slug}\" již existuje ", "error.page.duplicate": "Stránka, která v adrese URL obsahuje \"{slug}\" již existuje", "error.page.duplicate.permission": "Nemáte dovoleno duplikovat \"{slug}\"", - "error.page.notFound": "Stránku se nepodařilo nalézt.", + "error.page.move.ancestor": "Stránka nemůže být přesunuta sama do sebe", + "error.page.move.directory": "Adresář stránky nelze přesunout", + "error.page.move.duplicate": "Podstránka s URL \"{slug}\" již existuje", + "error.page.move.noSections": "Stránka \"{parent}\" nemůže být žádné jiné stránce nadřazená, protože neobsahuje sekci pro podstránky", + "error.page.move.notFound": "Přesunutá stránka nebyla nalezena", + "error.page.move.permission": "Nemáte dovoleno přesunout stránku \"{slug}\"", + "error.page.move.template": "Šablonu \"{template}\" nelze použít pro podstránku \"{parent}\"", + "error.page.notFound": "Str\u00e1nku se nepoda\u0159ilo nal\u00e9zt.", "error.page.num.invalid": "Zadejte prosím platné pořadové číslo. Čísla nesmí být záporná.", "error.page.slug.invalid": "Podtržení", "error.page.slug.maxlength": "URL musí mít méně než \"{length}\" znaků", "error.page.sort.permission": "Stránce \"{slug}\" nelze změnit pořadí", "error.page.status.invalid": "Nastavte prosím platný status stránky", - "error.page.undefined": "Stránku se nepodařilo nalézt.", + "error.page.undefined": "Str\u00e1nku se nepoda\u0159ilo nal\u00e9zt.", "error.page.update.permission": "Nemáte dovoleno upravit \"{slug}\"", "error.section.files.max.plural": "Sekce \"{section}\" nesmí obsahovat více jak {max} souborů", @@ -163,6 +217,8 @@ "error.site.changeTitle.permission": "Nemáte dovoleno změnit titulek stránky", "error.site.update.permission": "Nemáte dovoleno upravit stránku", + "error.structure.validation": "Chyba v poli \"{field}\" na řádku {index}", + "error.template.default.notFound": "Výchozí šablona neexistuje", "error.unexpected": "Vyskytla se neočekávaná chyba! Pro více informací povolte debug mód, viz: https://getkirby.com/docs/reference/system/options/debug", @@ -175,17 +231,17 @@ "error.user.changeRole.permission": "Nemáte dovoleno změnit roli uživatele \"{name}\"", "error.user.changeRole.toAdmin": "Nemáte dovoleno povýšit uživatele do role administrátora.", "error.user.create.permission": "Nemáte dovoleno vytvořit tohoto uživatele", - "error.user.delete": "Uživatel nemohl být smazán", - "error.user.delete.lastAdmin": "Nemůžete smazat posledního administrátora", + "error.user.delete": "U\u017eivatel nemohl b\u00fdt smaz\u00e1n", + "error.user.delete.lastAdmin": "Nem\u016f\u017eete smazat posledn\u00edho administr\u00e1tora", "error.user.delete.lastUser": "Poslední uživatel nemůže být smazán", - "error.user.delete.permission": "Nemáte dovoleno smazat tohoto uživatele", + "error.user.delete.permission": "Nem\u00e1te dovoleno smazat tohoto u\u017eivatele", "error.user.duplicate": "Uživatel s emailovou adresou \"{email}\" již existuje", "error.user.email.invalid": "Zadejte prosím platnou emailovou adresu", "error.user.language.invalid": "Zadejte prosím platný jazyk", - "error.user.notFound": "Uživatele se nepodařilo nalézt", + "error.user.notFound": "U\u017eivatele se nepoda\u0159ilo nal\u00e9zt", "error.user.password.excessive": "Zadejte prosím platné heslo. Heslo nesmí být delší než 1000 znaků.", "error.user.password.invalid": "Zadejte prosím platné heslo. Heslo musí být dlouhé alespoň 8 znaků.", - "error.user.password.notSame": "Prosím potvrďte heslo", + "error.user.password.notSame": "Pros\u00edm potvr\u010fte heslo", "error.user.password.undefined": "Uživatel nemá nastavené heslo.", "error.user.password.wrong": "Špatné heslo", "error.user.role.invalid": "Zadejte prosím platnou roli", @@ -195,8 +251,10 @@ "error.validation.accepted": "Potvrďte prosím", "error.validation.alpha": "Zadávejte prosím pouze znaky v rozmezí a-z", "error.validation.alphanum": "Zadávejte prosím pouze znaky v rozmezí a-z nebo čísla v rozmezí 0-9", + "error.validation.anchor": "Zadejte správný název kotvy", "error.validation.between": "Zadejte prosím hodnotu mez \"{min}\" a \"{max}\"", "error.validation.boolean": "Potvrďte prosím, nebo odmítněte", + "error.validation.color": "Zadejte platnou barvu ve formátu {format}", "error.validation.contains": "Zadejte prosím hodnotu, která obsahuje \"{needle}\"", "error.validation.date": "Zadejte prosím platné datum", "error.validation.date.after": "Zadejte prosím datum po {date}", @@ -211,6 +269,7 @@ "error.validation.integer": "Zadejte prosím platné celé číslo", "error.validation.ip": "Zadejte prosím platnou IP adresu", "error.validation.less": "Zadejte prosím hodnotu menší než {max}", + "error.validation.linkType": "Typ odkazu není povolen", "error.validation.match": "Hodnota neodpovídá očekávanému vzoru", "error.validation.max": "Zadejte prosím hodnotu rovnou, nebo menší než {max}", "error.validation.maxlength": "Zadaná hodnota je příliš dlouhá. (Povoleno nejvýše {max} znaků)", @@ -227,15 +286,18 @@ "error.validation.same": "Zadejte prosím \"{other}\"", "error.validation.size": "Velikost hodnoty musí být \"{size}\"", "error.validation.startswith": "Hodnota musí začínat \"{start}\"", + "error.validation.tel": "Zadejte neformátované telefonní číslo ", "error.validation.time": "Zadejte prosím platný čas", "error.validation.time.after": "Zadejte prosím čas po {time}", "error.validation.time.before": "Zadejte prosím čas před {time}", "error.validation.time.between": "Zadejte prosím čas v rozmezí od {min} do {max}", + "error.validation.uuid": "Zadejte platné UUID", "error.validation.url": "Zadejte prosím platnou adresu URL", "expand": "Rozbalit", "expand.all": "Rozbalit vše", + "field.invalid": "Pole není platné", "field.required": "Pole musí být vyplněno.", "field.blocks.changeType": "Změnit typ", "field.blocks.code.name": "Kód", @@ -245,8 +307,9 @@ "field.blocks.delete.confirm.all": "Opravdu chcete smazat všechny bloky?", "field.blocks.delete.confirm.selected": "Opravdu chcete smazat vybrané bloky?", "field.blocks.empty": "Zatím žádné bloky", + "field.blocks.fieldsets.empty": "Zatím žádné fieldsets", "field.blocks.fieldsets.label": "Vyberte prosím typ bloku …", - "field.blocks.fieldsets.paste": "Stiskněte{{ shortcut }} pro vložení/import bloků z Vaší schránky", + "field.blocks.fieldsets.paste": "Stiskněte{{ shortcut }} pro vložení rozvržení/bloků ze schránky Vloženy budou jen ty, které jsou v aktuálním poli povolené.", "field.blocks.gallery.name": "Galerie", "field.blocks.gallery.images.empty": "Zatím žádné obrázky", "field.blocks.gallery.images.label": "Obrázky", @@ -254,11 +317,16 @@ "field.blocks.heading.name": "Nadpis", "field.blocks.heading.text": "Text", "field.blocks.heading.placeholder": "Nadpis …", + "field.blocks.figure.back.plain": "Čistý", + "field.blocks.figure.back.pattern.light": "Vzor (světlý)", + "field.blocks.figure.back.pattern.dark": "Vzor (tmavý)", "field.blocks.image.alt": "Alternativní text", "field.blocks.image.caption": "Titulek", "field.blocks.image.crop": "Oříznout", "field.blocks.image.link": "Odkaz", "field.blocks.image.location": "Umístění", + "field.blocks.image.location.internal": "Tato webová stránka", + "field.blocks.image.location.external": "Externí zdroj", "field.blocks.image.name": "Obrázek", "field.blocks.image.placeholder": "Vyberte obrázek", "field.blocks.image.ratio": "Poměr stran", @@ -275,41 +343,75 @@ "field.blocks.quote.citation.placeholder": "od …", "field.blocks.text.name": "Text", "field.blocks.text.placeholder": "Text …", + "field.blocks.video.autoplay": "Autoplay", "field.blocks.video.caption": "Titulek", + "field.blocks.video.controls": "Ovládání", + "field.blocks.video.location": "Umístění", + "field.blocks.video.loop": "Smyčka", + "field.blocks.video.muted": "Ztlumené", "field.blocks.video.name": "Video", "field.blocks.video.placeholder": "Zadejte URL adresu videa", + "field.blocks.video.poster": "Náhledový obrázek", + "field.blocks.video.preload": "Předběžně načíst", "field.blocks.video.url.label": "URL adresa videa", "field.blocks.video.url.placeholder": "https://youtube.com/?v=", - "field.files.empty": "Nebyly zatím vybrány žádné soubory", + "field.entries.delete.confirm.all": "Opravdu chcete smazat všechny záznamy?", + "field.entries.empty": "Zatím nejsou žádné záznamy.", + "field.files.empty": "Nebyly zatím vybrány žádné soubory", + "field.files.empty.single": "Nebyl zatím vybrán žádný soubor", + + "field.layout.change": "Změnit rozvržení", "field.layout.delete": "Smazat rozvržení", "field.layout.delete.confirm": "Opravdu chcete smazat toto rozvržení?", + "field.layout.delete.confirm.all": "Opravdu chcete smazat všechna rozvržení?", "field.layout.empty": "Zatím žádné řádky", "field.layout.select": "Vyberte rozvržení", "field.object.empty": "Zatím žádná informace", "field.pages.empty": "Nebyly zatím vybrány žádné stránky", + "field.pages.empty.single": "Nebyla zatím vybrána žádná stránka", - "field.structure.delete.confirm": "Opravdu chcete smazat tento záznam?", + "field.structure.delete.confirm": "Opravdu chcete smazat tento z\u00e1znam?", "field.structure.delete.confirm.all": "Opravdu chcete smazat všechny záznamy?", - "field.structure.empty": "Zatím nejsou žádné záznamy.", + "field.structure.empty": "Zat\u00edm nejsou \u017e\u00e1dn\u00e9 z\u00e1znamy.", "field.users.empty": "Nebyli zatím vybráni žádní uživatelé", + "field.users.empty.single": "Nebyl zatím vybrán žádný uživatel", + "fields.empty": "Zatím žádné pole", + + "file": "Soubor", "file.blueprint": "Tento typ souboru nemá blueprint. Blueprint můžete definovat v /site/blueprints/files/{blueprint}.yml", + "file.changeTemplate": "Změnit šablonu", + "file.changeTemplate.notice": "Změna šablony souboru změní obsah pro pole, která mají odlišný typ. Pokud má nová šablona nastavena určitá pravidla, např. rozměry obrázku, tato pravidla budou také aplikována. Používejte obezřetně.", "file.delete.confirm": "Opravdu chcete smazat tento soubor?", + "file.focus.placeholder": "Nastavit ohnisko", + "file.focus.reset": "Odstranit ohnisko", + "file.focus.title": "Zaměřit na", "file.sort": "Změnit pozici", "files": "Soubory", + "files.delete.confirm.selected": "Opravdu chcete smazat vybrané soubory? Tuto akci nelze vzít zpět!", "files.empty": "Zatím žádné soubory", + "filter": "Filtr", + + "form.discard": "Zahodit změny", + "form.discard.confirm": "Opravdu chcete zahodit všechny změny?", + "form.locked": "Obsah je momentálně zablokován, protože ho upravuje jiný uživatel", + "form.unsaved": "Změny dosud nebyly uloženy", + "form.preview": "Náhled změn", + "form.preview.draft": "Náhled konceptu", + "hide": "Skrýt", "hour": "Hodina", + "hue": "Odstín", "import": "Import", "info": "Informace", - "insert": "Vložit", + "insert": "Vlo\u017eit", "insert.after": "Vložit za", "insert.before": "Vložit před", "install": "Instalovat", @@ -317,14 +419,13 @@ "installation": "Instalace", "installation.completed": "Panel byl nainstalován", "installation.disabled": "Instalátor panelu je ve výchozím nastavení na veřejných serverech zakázán. Spusťte prosím instalátor na lokálním počítači nebo jej povolte prostřednictvím panel.install.", - "installation.issues.accounts": "/site/accounts není zapisovatelné", - "installation.issues.content": "Složka content a všechny soubory a složky v ní musí být zapisovatelné.", + "installation.issues.accounts": "\/site\/accounts nen\u00ed zapisovateln\u00e9", + "installation.issues.content": "Slo\u017eka content a v\u0161echny soubory a slo\u017eky v n\u00ed mus\u00ed b\u00fdt zapisovateln\u00e9.", "installation.issues.curl": "Je vyžadováno rozšířeníCURL", "installation.issues.headline": "Panel nelze nainstalovat", "installation.issues.mbstring": "Je vyžadováno rozšířeníMB String", "installation.issues.media": "Složka/media neexistuje, nebo nemá povolený zápis", "installation.issues.php": "Ujistěte se, že používátePHP 8+", - "installation.issues.server": "Kirby vyžadujeApache, Nginx neboCaddy", "installation.issues.sessions": "Složka/site/sessions neexistuje, nebo nemá povolený zápis", "language": "Jazyk", @@ -332,6 +433,7 @@ "language.convert": "Nastavte výchozí možnost", "language.convert.confirm": "

Opravdu chcete převést{name} na výchozí jazyk? Tuto volbu nelze vzít zpátky.

Pokud {name} obsahuje nepřeložený text, nebude již k dispozici záložní varianta a části stránky mohou zůstat prázdné.

", "language.create": "Přidat nový jazyk", + "language.default": "Výchozí jazyk", "language.delete.confirm": "Opravdu chcete smazat jazyk {name} včetně všech překladů? Tuto volbu nelze vzít zpátky!", "language.deleted": "Jazyk byl smazán", "language.direction": "Směr čtení", @@ -340,7 +442,16 @@ "language.locale": "Řetězec lokalizace PHP", "language.locale.warning": "Používáte vlastní jazykové nastavení. Upravte prosím soubor s nastavením v /site/languages", "language.name": "Jméno", + "language.secondary": "Sekundární jazyk", + "language.settings": "Nastavení jazyka", "language.updated": "Jazyk byl aktualizován", + "language.variables": "Jazykové proměnné", + "language.variables.empty": "Zatím žádné překlady", + + "language.variable.delete.confirm": "Doopravdy chcete smazat proměnou {key}?", + "language.variable.key": "Klíč", + "language.variable.notFound": "Proměnná nebyla nalezena", + "language.variable.value": "Hodnota", "languages": "Jazyky", "languages.default": "Výchozí jazyk", @@ -349,15 +460,32 @@ "languages.secondary.empty": "Neexistují zatím žádné další jazyky", "license": "Kirby licence", + "license.activate": "Aktivovat nyní", + "license.activate.label": "Prosím aktivute svoji licenci", + "license.activate.domain": "Vaše licence bude zaregistrována na {host}.", + "license.activate.local": "Chystáte se registrovat licenci na Vaší lokální doméně {host}. Pokud bude tato stránka nasazena na veřejnou doménu, registrujte prosím licenci až tam. Pokud je {host} opravdu doménou, na které si přejete licenci registrovat, pokračujte prosím dále.", + "license.activated": "Aktivováno", "license.buy": "Zakoupit licenci", - "license.register": "Registrovat", + "license.code": "Kód", + "license.code.help": "Licenční kód jste po zakoupení obdrželi na email. Vložte prosím kód a zaregistrujte Vaší kopii.", + "license.code.label": "Zadejte prosím licenční kód", + "license.status.active.info": "Platí pro nové verze až do {date}", + "license.status.active.label": "Platná licence", + "license.status.demo.info": "Tato instalace je pouze demoverze", + "license.status.demo.label": "Demoverze", + "license.status.inactive.info": "Pro update na novou hlavní verzi musíte obnovit licenci", + "license.status.inactive.label": "Žádné nové hlavní verze", + "license.status.legacy.bubble": "Vše připraveno na obnovení licence?", + "license.status.legacy.info": "Vaše licence se nevztahuje na tuto verzi", + "license.status.legacy.label": "Prosím obnovte svoji licenci", + "license.status.missing.bubble": "Vše připraveno na spuštění vaši stránky?", + "license.status.missing.info": "Žádlná platná licence", + "license.status.missing.label": "Prosím aktivute svoji licenci", + "license.status.unknown.info": "Status licence je neznámý", + "license.status.unknown.label": "Neznámý", "license.manage": "Spravovat licence", - "license.register.help": "Licenční kód jste po zakoupení obdrželi na email. Vložte prosím kód a zaregistrujte Vaší kopii.", - "license.register.label": "Zadejte prosím licenční kód", - "license.register.domain": "Vaše licence bude zaregistrována na {host}.", - "license.register.local": "Chystáte se registrovat licenci na Vaší lokální doméně {host}. Pokud bude tato stránka nasazena na veřejnou doménu, registrujte prosím licenci až tam. Pokud je {host} opravdu doménou, na které si přejete licenci registrovat, pokračujte prosím dále.", - "license.register.success": "Děkujeme Vám za podporu Kirby", - "license.unregistered": "Toto je neregistrovaná kopie Kirby", + "license.purchased": "Zakoupeno", + "license.success": "Děkujeme Vám za podporu Kirby", "license.unregistered.label": "Neregistrovaný", "link": "Odkaz", @@ -367,17 +495,21 @@ "lock.unsaved": "Neuložené změny", "lock.unsaved.empty": "Nezbývají již žádné neuložené změny.", - "lock.isLocked": "Neuložené změny provedené {email}", - "lock.file.isLocked": "Soubor nelze změnit, právě jej upravuje {email}.", - "lock.page.isLocked": "Stránku nelze změnit, právě jí upravuje {email} .", + "lock.unsaved.files": "Neuložené soubory", + "lock.unsaved.pages": "Neuložené stránky", + "lock.unsaved.users": "Neuložené účty", + "lock.isLocked": "Neuložené změny od {email}", "lock.unlock": "Odemknout", - "lock.isUnlocked": "Vaše neuložené změny byly přepsány jiným uživatelem. Můžeze si své úpravy stáhnout a zapracovat je ručně.", + "lock.unlock.submit": "Odemknout a přepsat neuložené změny od {email}", + "lock.isUnlocked": "Bylo odemknuto jiným uživatelem", "login": "Přihlásit se", "login.code.label.login": "Kód pro přihlášení", "login.code.label.password-reset": "Kód pro resetování hesla", "login.code.placeholder.email": "000 000", + "login.code.placeholder.totp": "000000", "login.code.text.email": "Vaše e-mailová adresa byla zaregistrována, kód byl odeslán do Vaší e-mailové schránky.", + "login.code.text.totp": "Prosím vložte jednorázový kód z vaší autentifikační aplikace.", "login.email.login.body": "Ahoj {user.nameOrEmail},\n\nV nedávné době jsi zažádal(a) o kód pro přihlášení do Kirby Panelu na stránce {site}.\nNásledující kód pro přihlášení je platný {timeout} minut:\n\n{code}\n\nPokud jsi o kód pro přihlášení nežádal(a), tuto zprávu prosím ignoruj a v případě dotazů prosím kontaktuj svého administrátora.\nZ bezpečnostních důvodů prosím tuto zprávu nepřeposílej nikomu dalšímu.", "login.email.login.subject": "Váš kód pro přihlášení", "login.email.password-reset.body": "Ahoj {user.nameOrEmail},\n\nV nedávné době jsi zažádal(a) o kód pro resetování hesla do Kirby Panelu na stránce {site}.\nNásledující kód pro resetování hesla je platný {timeout} minut:\n\n{code}\n\nPokud jsi o kód pro resetování hesla nežádal(a), tuto zprávu prosím ignoruj a v případě dotazů prosím kontaktuj svého administrátora.\nZ bezpečnostních důvodů prosím tuto zprávu nepřeposílej nikomu dalšímu.", @@ -388,9 +520,24 @@ "login.toggleText.code.email-password": "Přihlásit se pomocí hesla", "login.toggleText.password-reset.email": "Zapomenuté heslo?", "login.toggleText.password-reset.email-password": "← Zpět na přihlášení", + "login.totp.enable.option": "Nastavit jednorázové kódy", + "login.totp.enable.intro": "Autentifikační aplikace umí generovat jednorázové kódy, které jsou použitý jako druhý faktor při přihlášení do vašeho účtu.", + "login.totp.enable.qr.label": "1. Naskenujte tento QR kód", + "login.totp.enable.qr.help": "Nepodařilo se scanovat? Přidejte klíč pro nastavení  {secret} ručně do své autentifikační aplikace.", + "login.totp.enable.confirm.headline": "2. Potvrďte vygenerovaným kódem", + "login.totp.enable.confirm.text": "Vaše aplikace generuje každých 30 sekund nový jednorázový kód. Zadejte aktuální kód a dokončete nastavení:", + "login.totp.enable.confirm.label": "Současný kód", + "login.totp.enable.confirm.help": "Po tomto nastavení vás při každém přihlášení požádáme o jednorázový kód.", + "login.totp.enable.success": "Jednorázové kódy zapnuty", + "login.totp.disable.option": "Vypnutí jednorázových kódu", + "login.totp.disable.label": "Pro vypnutí jednorázových kódů zadejte svoje heslo", + "login.totp.disable.help": "V budoucnu bude při přihlašování vyžadován jiný druhý faktor, například přihlašovací kód zaslaný e-mailem. Jednorázové kódy můžete vždy později nastavit znovu.", + "login.totp.disable.admin": "

Tím se jednorázové kódy pro{user} zakážou.

V budoucnu bude při přihlášení vyžadován jiný druhý faktor, například přihlašovací kód zaslaný e-mailem. Jednorázové kódy si může {user} nastavit znovu po svém dalším přihlášení.", + "login.totp.disable.success": "Jednorázové kódy vypnuty", "logout": "Odhlásit se", + "merge": "Spojit", "menu": "Menu", "meridiem": "AM/PM", "mime": "Typ média", @@ -402,44 +549,51 @@ "months.december": "Prosinec", "months.february": "Únor", "months.january": "Leden", - "months.july": "Červenec", - "months.june": "Červen", - "months.march": "Březen", - "months.may": "Květen", + "months.july": "\u010cervenec", + "months.june": "\u010cerven", + "months.march": "B\u0159ezen", + "months.may": "Kv\u011bten", "months.november": "Listopad", - "months.october": "Říjen", - "months.september": "Září", + "months.october": "\u0158\u00edjen", + "months.september": "Z\u00e1\u0159\u00ed", "more": "Více", + "move": "Přesunout", "name": "Jméno", "next": "Další", + "night": "Noc", "no": "ne", "off": "vypnuto", "on": "zapnuto", "open": "Otevřít", "open.newWindow": "Otevřít v novém okně", + "option": "Možnost", "options": "Možnosti", "options.none": "Žádné možnosti", + "options.all": "Zobrazit všech {count} možností", "orientation": "Orientace", "orientation.landscape": "Na šířku", "orientation.portrait": "Na výšku", "orientation.square": "Čtverec", + "page": "Stránka", "page.blueprint": "Tento typ stránky nemá blueprint. Blueprint můžete definovat v /site/blueprints/pages/{blueprint}.yml", - "page.changeSlug": "Změnit URL", - "page.changeSlug.fromTitle": "Vytvořit z názvu", + "page.changeSlug": "Zm\u011bnit URL", + "page.changeSlug.fromTitle": "Vytvo\u0159it z n\u00e1zvu", "page.changeStatus": "Změnit status", "page.changeStatus.position": "Vyberte prosím pozici", "page.changeStatus.select": "Vybrat nový status", "page.changeTemplate": "Změnit šablonu", - "page.delete.confirm": "Opravdu chcete smazat tuto stránku?", + "page.changeTemplate.notice": "Změna šablony stránky odstraní obsah pro pole, jejichž typy se neshodují. Používejte obezřetně.", + "page.create": "Vytvořit jako {status}", + "page.delete.confirm": "Opravdu chcete smazat tuto str\u00e1nku?", "page.delete.confirm.subpages": "Tato stránka má podstránky.
Všechny podstránky budou vymazány.", "page.delete.confirm.title": "Pro potvrzení zadejte titulek stránky", - "page.draft.create": "Vytvořit koncept", "page.duplicate.appendix": "Kopírovat", "page.duplicate.files": "Kopírovat soubory", "page.duplicate.pages": "Kopírovat stránky", + "page.move": "Přesunout stránku", "page.sort": "Změnit pozici", "page.status": "Stav", "page.status.draft": "Koncept", @@ -450,24 +604,32 @@ "page.status.unlisted.description": "Tato stránka je dostupná pouze přes URL.", "pages": "Stránky", + "pages.delete.confirm.selected": "Opravdu chcete smazat vybrané stránky? Tuto akci nelze vzít zpět!", "pages.empty": "Zatím žádné stránky", "pages.status.draft": "Koncepty", "pages.status.listed": "Zveřejněno", "pages.status.unlisted": "Neveřejná", - "pagination.page": "Page", + "pagination.page": "Stránka", "password": "Heslo", "paste": "Vložit", "paste.after": "Vložit za", + "paste.success": "{count} vloženo!", "pixel": "Pixel", - "plugin": "Doplňek", + "plugin": "Doplněk", "plugins": "Doplňky", "prev": "Předchozí", "preview": "Náhled", + + "publish": "Zveřejnit", + "published": "Zveřejněno", + "remove": "Odstranit", "rename": "Přejmenovat", + "renew": "Obnovit", "replace": "Nahradit", + "replace.with": "Nahradit pomocí", "retry": "Zkusit znovu", "revert": "Zahodit", "revert.confirm": "Opravdu chcete smazat všechny provedené změny?", @@ -481,12 +643,15 @@ "role.nobody.description": "Toto je výchozí role bez jakýchkoli oprávnění", "role.nobody.title": "Nikdo", - "save": "Uložit", + "save": "Ulo\u017eit", + "saved": "Uloženo", "search": "Hledat", + "searching": "Hledání", "search.min": "Pro vyhledání zadejte alespoň {min} znaky", - "search.all": "Zobrazit vše", + "search.all": "Zobrazit všech {count} výsledků", "search.results.none": "Žádné výsledky", + "section.invalid": "Sekce je neplatná", "section.required": "Sekce musí být vyplněna", "security": "Zabezpečení", @@ -496,18 +661,27 @@ "show": "Zobrazit", "site.blueprint": "Hlavní panel nemá blueprint. Blueprint můžete definovat v /site/blueprints/site.yml", "size": "Velikost", - "slug": "Přípona URL", + "slug": "P\u0159\u00edpona URL", "sort": "Řadit", + "sort.drag": "Táhnout pro změnu řazení ...", + "split": "Rozdělit", "stats.empty": "Žádná hlášení", + "status": "Stav", + + "system.info.copy": "Kopírovat informace", + "system.info.copied": "Systémové informace zkopírovány", "system.issues.content": "Složka content je zřejmě přístupná zvenčí", "system.issues.eol.kirby": "Instalovaná verze Kirby dosáhla konce životnosti a nebude již dále dostávat bezpečnostní aktualizace", "system.issues.eol.plugin": "Instalovaná verze doplňku { plugin } dosáhla konce životnosti a nebude již dále dostávat bezpečnostní aktualizace", + "system.issues.eol.php": "Instalovaná verze PHP { release } dosálhla konce životnosti a nebude již dále dostávat bezpečností aktualizace", "system.issues.debug": "Debug mode musí být v produkci vypnutý", "system.issues.git": "Složka .git je zřejmě přístupná zvenčí", "system.issues.https": "Pro všechny stránky doporučujeme používat protokol HTTPS", "system.issues.kirby": "Složka kirby je zřejmě přístupná zvenčí", + "system.issues.local": "Stránka běží lokálně s mírnějšími bezpečnostními kontrolami", "system.issues.site": "Složka site je zřejmě přístupná zvenčí", + "system.issues.vue.compiler": "Kompilátor Vue šablon je povolen", "system.issues.vulnerability.kirby": "Vaše instalace může být ovlivněna následující zranitelností (stupeň vážnosti - { severity }): { description }", "system.issues.vulnerability.plugin": "Vaše instalace může být ovlivněna následující zranitelností v doplňku { plugin } (stupeň vážnosti - { severity }): { description }", "system.updateStatus": "Status aktualizací", @@ -520,12 +694,21 @@ "system.updateStatus.update": "Je dostupná bezplatná nová verze { version }", "system.updateStatus.upgrade": "Je dostupný upgrade na verzi { version }", + "tel": "Telefon", + "tel.placeholder": "+49123456789", + "template": "\u0160ablona", + + "theme": "Motiv", + "theme.light": "Světlý motiv", + "theme.dark": "Tmavý motiv", + "theme.automatic": "Podle nastavení systému", + "title": "Název", - "template": "Šablona", "today": "Dnes", + "toolbar.button.clear": "Odstranit formátování", "toolbar.button.code": "Kód", - "toolbar.button.bold": "Tučný text", + "toolbar.button.bold": "Tu\u010dn\u00fd text", "toolbar.button.email": "Email", "toolbar.button.headings": "Nadpisy", "toolbar.button.heading.1": "Nadpis 1", @@ -534,22 +717,26 @@ "toolbar.button.heading.4": "Nadpis 4", "toolbar.button.heading.5": "Nadpis 5", "toolbar.button.heading.6": "Nadpis 6", - "toolbar.button.italic": "Kurzíva", + "toolbar.button.italic": "Kurz\u00edva", "toolbar.button.file": "Soubor", "toolbar.button.file.select": "Vyberte soubor", "toolbar.button.file.upload": "Nahrajte soubor", "toolbar.button.link": "Odkaz", "toolbar.button.paragraph": "Odstavec", "toolbar.button.strike": "Přeškrtnutí", + "toolbar.button.sub": "Dolní index", + "toolbar.button.sup": "Horní index", "toolbar.button.ol": "Číslovaný seznam", "toolbar.button.underline": "Podtržení", "toolbar.button.ul": "Odrážkový seznam", "translation.author": "Kirby tým", "translation.direction": "ltr", - "translation.name": "Česky", + "translation.name": "\u010cesky", "translation.locale": "cs_CZ", + "type": "Typ", + "upload": "Nahrát", "upload.error.cantMove": "Nahraný soubor nemohl být přesunut", "upload.error.cantWrite": "Zápis souboru na disk se nezdařil", @@ -562,7 +749,7 @@ "upload.error.noFiles": "Nebyly nahrány žádné soubory", "upload.error.partial": "Soubor byl nahrán pouze z části", "upload.error.tmpDir": "Chybí dočasná složka", - "upload.errors": "Error", + "upload.errors": "Chyba", "upload.progress": "Nahrávání...", "url": "Url", @@ -574,28 +761,32 @@ "user.changeLanguage": "Změnit jazyk", "user.changeName": "Přejmenovat tohoto uživatele", "user.changePassword": "Změnit heslo", + "user.changePassword.current": "Vaše současné heslo", "user.changePassword.new": "Nové heslo", "user.changePassword.new.confirm": "Potvrdit nové heslo...", "user.changeRole": "Změnit roli", "user.changeRole.select": "Vybrat novou roli", "user.create": "Přidat nového uživatele", "user.delete": "Smazat tohoto uživatele", - "user.delete.confirm": "Opravdu chcete smazat tohoto uživatele?", + "user.delete.confirm": "Opravdu chcete smazat tohoto u\u017eivatele?", "users": "Uživatelé", "version": "Verze Kirby", + "version.changes": "Změnit verzi", + "version.compare": "Porovnat verze", "version.current": "Současná verze", "version.latest": "Poslední verze", "versionInformation": "Informace o verzi", - "view.account": "Váš účet", + "view": "Pohled", + "view.account": "V\u00e1\u0161 \u00fa\u010det", "view.installation": "Instalace", "view.languages": "Jazyky", "view.resetPassword": "Resetovat heslo", "view.site": "Stránka", "view.system": "Systém", - "view.users": "Uživatelé", + "view.users": "U\u017eivatel\u00e9", "welcome": "Vítejte", "year": "Rok", diff --git a/kirby/i18n/translations/da.json b/kirby/i18n/translations/da.json index 67138d5..8c05064 100644 --- a/kirby/i18n/translations/da.json +++ b/kirby/i18n/translations/da.json @@ -3,19 +3,28 @@ "account.delete": "Slet din konto", "account.delete.confirm": "Ønsker du virkelig at slette din konto? Du vil blive logget ud med det samme. Din konto kan ikke gendannes.", + "activate": "Activate", "add": "Ny", + "alpha": "Alpha", "author": "Forfatter", "avatar": "Profilbillede", "back": "Tilbage", "cancel": "Annuller", - "change": "Ændre", + "change": "\u00c6ndre", "close": "Luk", + "changes": "Changes", "confirm": "Gem", "collapse": "Fold sammen", "collapse.all": "Fold alle sammen", + "color": "Color", + "coordinates": "Coordinates", "copy": "Kopier", "copy.all": "Kopier alle", + "copy.success": "{count} copied!", + "copy.success.multiple": "{count} copied!", + "copy.url": "Copy URL", "create": "Opret", + "custom": "Custom", "date": "Dato", "date.select": "Vælg en dato", @@ -23,8 +32,8 @@ "day": "Dag", "days.fri": "Fre", "days.mon": "Man", - "days.sat": "Lør", - "days.sun": "Søn", + "days.sat": "L\u00f8r", + "days.sun": "S\u00f8n", "days.thu": "Tor", "days.tue": "Tir", "days.wed": "Ons", @@ -34,13 +43,20 @@ "delete": "Slet", "delete.all": "Slet alle", + "dialog.fields.empty": "This dialog has no fields", "dialog.files.empty": "Ingen filer kan vælges", "dialog.pages.empty": "Ingen sider kan vælges", + "dialog.text.empty": "This dialog does not define any text", "dialog.users.empty": "Ingen brugere kan vælges", "dimensions": "Dimensioner", + "disable": "Disable", "disabled": "Deaktiveret", - "discard": "Kassér", + "discard": "Kass\u00e9r", + + "drawer.fields.empty": "This drawer has no fields", + + "domain": "Domain", "download": "Download", "duplicate": "Dupliker", @@ -49,11 +65,13 @@ "email": "Email", "email.placeholder": "mail@eksempel.dk", + "enter": "Enter", "entries": "Entries", "entry": "Entry", "environment": "Miljø", + "error": "Fejl", "error.access.code": "Ugyldig kode", "error.access.login": "Ugyldigt log ind", "error.access.panel": "Du har ikke adgang til panelet", @@ -74,13 +92,31 @@ "error.cache.type.invalid": "Invalid cache type \"{type}\"", + "error.content.lock.delete": "The version is locked and cannot be deleted", + "error.content.lock.move": "The source version is locked and cannot be moved", + "error.content.lock.publish": "This version is already published", + "error.content.lock.replace": "The version is locked and cannot be replaced", + "error.content.lock.update": "The version is locked and cannot be updated", + + "error.entries.max.plural": "You must not add more than {max} entries", + "error.entries.max.singular": "You must not add more than one entry", + "error.entries.min.plural": "You must add at least {min} entries", + "error.entries.min.singular": "You must add at least one entry", + "error.entries.supports": "\"{type}\" field type is not supported for the entries field", + "error.entries.validation": "There's an error on the \"{field}\" field in row {index}", + "error.email.preset.notFound": "Email preset \"{name}\" findes ikke", "error.field.converter.invalid": "Ugyldig converter \"{converter}\"", + "error.field.link.options": "Invalid options: {options}", "error.field.type.missing": "Field \"{ name }\": The field type \"{ type }\" does not exist", "error.file.changeName.empty": "Navn kan ikke efterlades tomt", "error.file.changeName.permission": "Du har ikke tilladelse til at ændre navnet på filen \"{filename}\"", + "error.file.changeTemplate.invalid": "The template for the file \"{id}\" cannot be changed to \"{template}\" (valid: \"{blueprints}\")", + "error.file.changeTemplate.permission": "You are not allowed to change the template for the file \"{id}\"", + + "error.file.delete.multiple": "Not all files could be deleted. Try each remaining file individually to see the specific error that prevents deletion.", "error.file.duplicate": "En fil med navnet \"{filename}\" eksisterer allerede", "error.file.extension.forbidden": "Uacceptabel fil-endelse", "error.file.extension.invalid": "Ugyldig endelse: {extension}", @@ -95,9 +131,11 @@ "error.file.minheight": "Højden af billedet skal mindst være {height} pixels", "error.file.minsize": "Filen er for lille", "error.file.minwidth": "Bredden af billedet skal mindst være {width} pixels", + "error.file.name.unique": "The filename must be unique", "error.file.name.missing": "Filnavn må ikke være tomt", "error.file.notFound": "Filen kunne ikke findes", "error.file.orientation": "Formatet på billedet skal være \"{orientation}\"", + "error.file.sort.permission": "You are not allowed to change the sorting of \"{filename}\"", "error.file.type.forbidden": "Du har ikke tilladelse til at uploade {type} filer", "error.file.type.invalid": "Ugyldig filtype: {type}", "error.file.undefined": "Filen kunne ikke findes", @@ -106,22 +144,30 @@ "error.form.notSaved": "Formularen kunne ikke gemmes", "error.language.code": "Indtast venligst en gyldig kode for sproget", + "error.language.create.permission": "You are not allowed to create a language", + "error.language.delete.permission": "You are not allowed to delete the language", "error.language.duplicate": "Sproget eksisterer allerede", "error.language.name": "Indtast venligst et gyldigt navn for sproget", "error.language.notFound": "Sproget fandtes ikke", + "error.language.update.permission": "You are not allowed to update the language", "error.layout.validation.block": "There's an error on the \"{field}\" field in block {blockIndex} using the \"{fieldset}\" block type in layout {layoutIndex}", "error.layout.validation.settings": "Der er fejl i layout {index} indstillinger", - "error.license.format": "Indtast venligst en gyldig licensnøgle", + "error.license.domain": "The domain for the license is missing", "error.license.email": "Indtast venligst en gyldig email adresse", + "error.license.format": "Please enter a valid license code", "error.license.verification": "Licensen kunne ikke verificeres", + "error.login.totp.confirm.invalid": "Ugyldig kode", + "error.login.totp.confirm.missing": "Please enter the current code", + "error.object.validation": "There’s an error in the \"{label}\" field:\n{message}", "error.offline": "Panelet er i øjeblikket offline", "error.page.changeSlug.permission": "Du kan ikke ændre URL-endelse for \"{slug}\"", + "error.page.changeSlug.reserved": "The path of top-level pages must not start with \"{path}\"", "error.page.changeStatus.incomplete": "Siden indeholder fejl og kan derfor ikke udgives", "error.page.changeStatus.permission": "Status for denne side kan ikke ændres", "error.page.changeStatus.toDraft.invalid": "Siden \"{slug}\" kan ikke konverteres om til en kladde", @@ -133,10 +179,18 @@ "error.page.delete": "Siden \"{slug}\" kan ikke slettes", "error.page.delete.confirm": "Indtast venligst sidens titel for at bekræfte", "error.page.delete.hasChildren": "Siden har unsersider og kan derfor ikke slettes", + "error.page.delete.multiple": "Not all pages could be deleted. Try each remaining page individually to see the specific error that prevents deletion.", "error.page.delete.permission": "Du har ikke tilladelse til at slette \"{slug}\"", "error.page.draft.duplicate": "En sidekladde med URL-endelsen \"{slug}\" eksisterer allerede", "error.page.duplicate": "En side med URL-endelsen \"{slug}\" eksisterer allerede", "error.page.duplicate.permission": "Du har ikke mulighed for at duplikere \"{slug}\"", + "error.page.move.ancestor": "The page cannot be moved into itself", + "error.page.move.directory": "The page directory cannot be moved", + "error.page.move.duplicate": "A sub page with the URL appendix \"{slug}\" already exists", + "error.page.move.noSections": "The page \"{parent}\" cannot be a parent of any page because it lacks any pages sections in its blueprint", + "error.page.move.notFound": "The moved page could not be found", + "error.page.move.permission": "You are not allowed to move \"{slug}\"", + "error.page.move.template": "The \"{template}\" template is not accepted as a subpage of \"{parent}\"", "error.page.notFound": "Siden kunne ikke findes", "error.page.num.invalid": "Indtast venligst et gyldigt sorteringsnummer. Nummeret kan ikke være negativt.", "error.page.slug.invalid": "Indtast venligst et gyldigt URL appendix", @@ -163,6 +217,8 @@ "error.site.changeTitle.permission": "Du har ikke tilladelse til at ændre titlen på sitet", "error.site.update.permission": "Du har ikke tilladelse til at opdatere sitet", + "error.structure.validation": "There's an error on the \"{field}\" field in row {index}", + "error.template.default.notFound": "Standardskabelonen eksisterer ikke", "error.unexpected": "En uventet fejl opstod! Aktiver debug mode for mere info: https://getkirby.com/docs/reference/system/options/debug", @@ -185,7 +241,7 @@ "error.user.notFound": "Brugeren kunne ikke findes", "error.user.password.excessive": "Please enter a valid password. Passwords must not be longer than 1000 characters.", "error.user.password.invalid": "Indtast venligst en gyldig adgangskode. Adgangskoder skal minimum være 8 tegn lange.", - "error.user.password.notSame": "Bekræft venligst adgangskoden", + "error.user.password.notSame": "Bekr\u00e6ft venligst adgangskoden", "error.user.password.undefined": "Brugeren har ikke en adgangskode", "error.user.password.wrong": "Forkert adgangskode", "error.user.role.invalid": "Indtast venligst en gyldig rolle", @@ -195,8 +251,10 @@ "error.validation.accepted": "Bekræft venligst", "error.validation.alpha": "Indtast venligst kun bogstaver imellem a-z", "error.validation.alphanum": "Indtast venligst kun bogstaver og tal imellem a-z eller 0-9", + "error.validation.anchor": "Please enter a correct link anchor", "error.validation.between": "Indtast venligst en værdi imellem \"{min}\" og \"{max}\"", "error.validation.boolean": "Venligst bekræft eller afvis", + "error.validation.color": "Please enter a valid color in the {format} format", "error.validation.contains": "Indtast venligst en værdi der indeholder \"{needle}\"", "error.validation.date": "Indtast venligst en gyldig dato", "error.validation.date.after": "Indtast venligst en dato efter {date}", @@ -211,6 +269,7 @@ "error.validation.integer": "Indtast et gyldigt tal", "error.validation.ip": "Indtast en gyldig IP adresse", "error.validation.less": "Indtast venligst en værdi mindre end {max}", + "error.validation.linkType": "The link type is not allowed", "error.validation.match": "Værdien matcher ikke det forventede mønster", "error.validation.max": "Indtast venligst en værdi lig med eller lavere end {max}", "error.validation.maxlength": "Indtast venligst en kortere værdi. (maks. {max} karakterer)", @@ -227,15 +286,18 @@ "error.validation.same": "Indtast venligst \"{other}\"", "error.validation.size": "Størrelsen på værdien skal være \"{size}\"", "error.validation.startswith": "Værdien skal starte med \"{start}\"", + "error.validation.tel": "Please enter an unformatted phone number", "error.validation.time": "Indtast venligst et gyldigt tidspunkt", "error.validation.time.after": "Indtast venligst et tidspunkt efter {time}", "error.validation.time.before": "Indtast venligst et tidspunkt inden {time}", "error.validation.time.between": "Indtast venligst et tidspunkt imellem {min} og {max}", + "error.validation.uuid": "Please enter a valid UUID", "error.validation.url": "Indtast venligst en gyldig URL", "expand": "Fold ud", "expand.all": "Fold alle ud", + "field.invalid": "The field is invalid", "field.required": "Feltet er påkrævet", "field.blocks.changeType": "Skift type", "field.blocks.code.name": "Kode", @@ -245,8 +307,9 @@ "field.blocks.delete.confirm.all": "Ønsker du virkelig at slette alle blokke?", "field.blocks.delete.confirm.selected": "Ønsker du virkelig at slette de valgte blokke?", "field.blocks.empty": "Ingen blokke endnu", + "field.blocks.fieldsets.empty": "No fieldsets yet", "field.blocks.fieldsets.label": "Vælg venligst en blok type", - "field.blocks.fieldsets.paste": "Tryk {{ shortcut }} for at indsætte/importere blokke fra dit udklipsholder", + "field.blocks.fieldsets.paste": "Press {{ shortcut }} to import layouts/blocks from your clipboard Only those allowed in the current field will get inserted.", "field.blocks.gallery.name": "Galleri", "field.blocks.gallery.images.empty": "Ingen billeder endnu", "field.blocks.gallery.images.label": "Billeder", @@ -254,11 +317,16 @@ "field.blocks.heading.name": "Overskrift", "field.blocks.heading.text": "Tekst", "field.blocks.heading.placeholder": "Overskrift …", + "field.blocks.figure.back.plain": "Plain", + "field.blocks.figure.back.pattern.light": "Pattern (light)", + "field.blocks.figure.back.pattern.dark": "Pattern (dark)", "field.blocks.image.alt": "Alternativ tekst", "field.blocks.image.caption": "Billedtekst", "field.blocks.image.crop": "Beskær", "field.blocks.image.link": "Link", "field.blocks.image.location": "Placering", + "field.blocks.image.location.internal": "This website", + "field.blocks.image.location.external": "External source", "field.blocks.image.name": "Billede", "field.blocks.image.placeholder": "Vælg et billede", "field.blocks.image.ratio": "Størrelsesforhold", @@ -275,41 +343,75 @@ "field.blocks.quote.citation.placeholder": "af …", "field.blocks.text.name": "Tekst", "field.blocks.text.placeholder": "Tekst …", + "field.blocks.video.autoplay": "Autoplay", "field.blocks.video.caption": "Billedtekst", + "field.blocks.video.controls": "Controls", + "field.blocks.video.location": "Placering", + "field.blocks.video.loop": "Loop", + "field.blocks.video.muted": "Muted", "field.blocks.video.name": "Video", "field.blocks.video.placeholder": "Indtast URL til en video", + "field.blocks.video.poster": "Poster", + "field.blocks.video.preload": "Preload", "field.blocks.video.url.label": "Video-URL", "field.blocks.video.url.placeholder": "https://youtube.com/?v=", - "field.files.empty": "Ingen filer valgt endnu", + "field.entries.delete.confirm.all": "Do you really want to delete all entries?", + "field.entries.empty": "Ingen indtastninger endnu.", + "field.files.empty": "Ingen filer valgt endnu", + "field.files.empty.single": "No file selected yet", + + "field.layout.change": "Change layout", "field.layout.delete": "Slet layout", "field.layout.delete.confirm": "Ønsker du virkelig at slette dette layout", + "field.layout.delete.confirm.all": "Do you really want to delete all layouts?", "field.layout.empty": "Ingen rækker endnu", "field.layout.select": "Vælg et layout", "field.object.empty": "No information yet", "field.pages.empty": "Ingen sider valgt endnu", + "field.pages.empty.single": "No page selected yet", - "field.structure.delete.confirm": "Ønsker du virkelig at slette denne indtastning?", + "field.structure.delete.confirm": "\u00d8nsker du virkelig at slette denne indtastning?", "field.structure.delete.confirm.all": "Do you really want to delete all entries?", "field.structure.empty": "Ingen indtastninger endnu.", "field.users.empty": "Ingen brugere er valgt", + "field.users.empty.single": "No user selected yet", + "fields.empty": "No fields yet", + + "file": "File", "file.blueprint": "Denne fil har intet blueprint endnu. Du kan definere opsætningen i /site/blueprints/files/{blueprint}.yml", - "file.delete.confirm": "Ønsker du virkelig at slette denne fil?", + "file.changeTemplate": "Skift skabelon", + "file.changeTemplate.notice": "Changing the file's template will remove content for fields that don't match in type. If the new template defines certain rules, e.g. image dimensions, those will also be applied irreversibly. Use with caution.", + "file.delete.confirm": "\u00d8nsker du virkelig at slette denne fil?", + "file.focus.placeholder": "Set focal point", + "file.focus.reset": "Remove focal point", + "file.focus.title": "Focus", "file.sort": "Skift position", "files": "Filer", + "files.delete.confirm.selected": "Do you really want to delete the selected files? This action cannot be undone.", "files.empty": "Ingen filer endnu", + "filter": "Filter", + + "form.discard": "Discard changes", + "form.discard.confirm": "Do you really want to discard all your changes?", + "form.locked": "This content is disabled for you as it is currently edited by another user", + "form.unsaved": "The current changes have not yet been saved", + "form.preview": "Preview changes", + "form.preview.draft": "Preview draft", + "hide": "Skjul", "hour": "Time", + "hue": "Hue", "import": "Importer", "info": "Info", - "insert": "Indsæt", + "insert": "Inds\u00e6t", "insert.after": "Indsæt efter", "insert.before": "Indsæt før", "install": "Installer", @@ -317,14 +419,13 @@ "installation": "Installation", "installation.completed": "Panelet er blevet installeret", "installation.disabled": "Panel installationen er deaktiveret på offentlige servere som standard. Kør venligst installationen på en lokal maskine eller aktiver det med panel.install panel.install muligheden.", - "installation.issues.accounts": "/site/accounts er ikke skrivbar", - "installation.issues.content": "Content mappen samt alle underliggende filer og mapper skal være skrivbare.", + "installation.issues.accounts": "\/site\/accounts er ikke skrivbar", + "installation.issues.content": "Content mappen samt alle underliggende filer og mapper skal v\u00e6re skrivbare.", "installation.issues.curl": "CURL extension er påkrævet", "installation.issues.headline": "Panelet kan ikke installeres", "installation.issues.mbstring": "MB String extension er påkrævet", "installation.issues.media": "/media mappen eksisterer ikke eller er ikke skrivbar", "installation.issues.php": "Sikre dig at der benyttes PHP 8+", - "installation.issues.server": "Kirby kræver Apache, Nginx eller Caddy", "installation.issues.sessions": "/site/sessions mappen eksisterer ikke eller er ikke skrivbar", "language": "Sprog", @@ -332,6 +433,7 @@ "language.convert": "Gør standard", "language.convert.confirm": "

Ønsker du virkelig at konvertere {name} til standardsproget? Dette kan ikke fortrydes.

Hvis {name} har uoversat indhold, vil der ikke længere være et gyldigt tilbagefald og dele af dit website vil måske fremstå tomt.

", "language.create": "Tilføj nyt sprog", + "language.default": "Standardsprog", "language.delete.confirm": "Ønsker du virkelig at slette sproget {name} inklusiv alle oversættelser? Kan ikke fortrydes!", "language.deleted": "Sproget er blevet slettet", "language.direction": "Læseretning", @@ -340,7 +442,16 @@ "language.locale": "PHP locale string", "language.locale.warning": "Du benytter en brugerdefineret sprogopsætning. Rediger venligst dette i sprogfilen i /site/languages", "language.name": "Navn", + "language.secondary": "Secondary language", + "language.settings": "Language settings", "language.updated": "Sproget er blevet opdateret", + "language.variables": "Language variables", + "language.variables.empty": "No translations yet", + + "language.variable.delete.confirm": "Do you really want to delete the variable for {key}?", + "language.variable.key": "Key", + "language.variable.notFound": "The variable could not be found", + "language.variable.value": "Value", "languages": "Sprog", "languages.default": "Standardsprog", @@ -349,15 +460,32 @@ "languages.secondary.empty": "Der er ingen sekundære sprog endnu", "license": "Kirby licens", + "license.activate": "Activate it now", + "license.activate.label": "Please activate your license", + "license.activate.domain": "Your license will be activated for {host}.", + "license.activate.local": "You are about to activate your Kirby license for your local domain {host}. If this site will be deployed to a public domain, please activate it there instead. If {host} is the domain you want to use your license for, please continue.", + "license.activated": "Activated", "license.buy": "Køb en licens", - "license.register": "Registrer", + "license.code": "Kode", + "license.code.help": "You received your license code after the purchase via email. Please copy and paste it here.", + "license.code.label": "Indtast venligst din licenskode", + "license.status.active.info": "Includes new major versions until {date}", + "license.status.active.label": "Valid license", + "license.status.demo.info": "This is a demo installation", + "license.status.demo.label": "Demo", + "license.status.inactive.info": "Renew license to update to new major versions", + "license.status.inactive.label": "No new major versions", + "license.status.legacy.bubble": "Ready to renew your license?", + "license.status.legacy.info": "Your license does not cover this version", + "license.status.legacy.label": "Please renew your license", + "license.status.missing.bubble": "Ready to launch your site?", + "license.status.missing.info": "No valid license", + "license.status.missing.label": "Please activate your license", + "license.status.unknown.info": "The license status is unknown", + "license.status.unknown.label": "Unknown", "license.manage": "Manage your licenses", - "license.register.help": "Du modtog din licenskode efter købet via email. Venligst kopier og indsæt den for at registrere.", - "license.register.label": "Indtast venligst din licenskode", - "license.register.domain": "Your license will be registered to {host}.", - "license.register.local": "You are about to register your license for your local domain {host}. If this site will be deployed to a public domain, please register it there instead. If {host} is the domain you want to license Kirby to, please continue.", - "license.register.success": "Tak for din støtte af Kirby", - "license.unregistered": "Dette er en uregistreret demo af Kirby", + "license.purchased": "Purchased", + "license.success": "Tak for din støtte af Kirby", "license.unregistered.label": "Unregistered", "link": "Link", @@ -367,17 +495,21 @@ "lock.unsaved": "Ugemte ændringer", "lock.unsaved.empty": "Der er ikke flere ændringer der ikke er gamt", - "lock.isLocked": "Ugemte ændringer af {email}", - "lock.file.isLocked": "Filen redigeres på nuværende af {email} og kan derfor ikke ændres.", - "lock.page.isLocked": "Siden redigeres på nuværende af {email} og kan derfor ikke ændres.", + "lock.unsaved.files": "Unsaved files", + "lock.unsaved.pages": "Unsaved pages", + "lock.unsaved.users": "Unsaved accounts", + "lock.isLocked": "Unsaved changes by {email}", "lock.unlock": "Lås op", - "lock.isUnlocked": "Dine ugemte ændringer er blevet overskrevet af en anden bruger. Du kan downloade dine ændringer for at flette dem ind manuelt.", + "lock.unlock.submit": "Unlock and overwrite unsaved changes by {email}", + "lock.isUnlocked": "Was unlocked by another user", "login": "Log ind", "login.code.label.login": "Log ind kode", "login.code.label.password-reset": "Sikkerhedskode", "login.code.placeholder.email": "000 000", + "login.code.placeholder.totp": "000000", "login.code.text.email": "Hvis din email adresse er registreret er en sikkerhedskode blevet sendt via email.", + "login.code.text.totp": "Please enter the one‑time code from your authenticator app.", "login.email.login.body": "Hej {user.nameOrEmail},\n\nDu har for nyligt anmodet om en log ind kode til panelet af {site}.\nFølgende log ind kode vil være gyldig i {timeout} minutter:\n\n{code}\n\nHvis du ikke har anmodet om en log ind kode, kan du blot ignorere denne email eller kontakte din administrator hvis du har spørgsmål.\nAf sikkerhedsmæssige årsager, bør du IKKE videresende denne email.", "login.email.login.subject": "Din log ind kode", "login.email.password-reset.body": "Hej {user.nameOrEmail},\n\nDu har for nyligt anmodet om kode til nulstilling af adgangskode til panelet af {site}.\nFølgende kode til nulstilling af adgangskode vil være gyldig i {timeout} minutter:\n\n{code}\n\nHvis du ikke har anmodet om kode til nulstilling af adgangskode, kan du blot ignorere denne email eller kontakte din administrator hvis du har spørgsmål.\nAf sikkerhedsmæssige årsager, bør du IKKE videresende denne email.", @@ -388,9 +520,24 @@ "login.toggleText.code.email-password": "Log ind med adgangskode", "login.toggleText.password-reset.email": "Glemt din adgangskode?", "login.toggleText.password-reset.email-password": "← Tilbage til log ind", + "login.totp.enable.option": "Set up one‑time codes", + "login.totp.enable.intro": "Authenticator apps can generate one‑time codes that are used as a second factor when signing into your account.", + "login.totp.enable.qr.label": "1. Scan this QR code", + "login.totp.enable.qr.help": "Unable to scan? Add the setup key {secret} manually to your authenticator app.", + "login.totp.enable.confirm.headline": "2. Confirm with generated code", + "login.totp.enable.confirm.text": "Your app generates a new one‑time code every 30 seconds. Enter the current code to complete the setup:", + "login.totp.enable.confirm.label": "Current code", + "login.totp.enable.confirm.help": "After this setup, we will ask you for a one‑time code every time you log in.", + "login.totp.enable.success": "One‑time codes enabled", + "login.totp.disable.option": "Disable one‑time codes", + "login.totp.disable.label": "Enter your password to disable one‑time codes", + "login.totp.disable.help": "In the future, a different second factor like a login code sent via email will be requested when you log in. You can always set up one‑time codes again later.", + "login.totp.disable.admin": "

This will disable one‑time codes for {user}.

In the future, a different second factor like a login code sent via email will be requested when they log in. {user} can set up one‑time codes again after their next login.

", + "login.totp.disable.success": "One‑time codes disabled", "logout": "Log ud", + "merge": "Merge", "menu": "Menu", "meridiem": "AM/PM", "mime": "Medie Type", @@ -411,35 +558,42 @@ "months.september": "September", "more": "Mere", + "move": "Move", "name": "Navn", "next": "Næste", + "night": "Night", "no": "nej", "off": "Sluk", "on": "Aktiveret", "open": "Åben", "open.newWindow": "Åben i et nyt vindue", + "option": "Option", "options": "Indstillinger", "options.none": "Ingen muligheder", + "options.all": "Show all {count} options", "orientation": "Orientering", "orientation.landscape": "Landskab", "orientation.portrait": "Portræt", "orientation.square": "Kvadrat", + "page": "Page", "page.blueprint": "Denne side har intet blueprint endnu. Du kan definere opsætningen i /site/blueprints/pages/{blueprint}.yml", - "page.changeSlug": "Ændre URL", + "page.changeSlug": "\u00c6ndre URL", "page.changeSlug.fromTitle": "Generer udfra titel", "page.changeStatus": "Skift status", "page.changeStatus.position": "Vælg venligst position", "page.changeStatus.select": "Vælg en ny status", "page.changeTemplate": "Skift skabelon", - "page.delete.confirm": "Ønsker du virkelig at slette denne side?", + "page.changeTemplate.notice": "Changing the page's template will remove content for fields that don't match in type. Use with caution.", + "page.create": "Create as {status}", + "page.delete.confirm": "\u00d8nsker du virkelig at slette denne side?", "page.delete.confirm.subpages": "Denne side har undersider.
Alle undersider vil også blive slettet.", "page.delete.confirm.title": "Indtast sidens titel for at bekræfte", - "page.draft.create": "Opret kladde", "page.duplicate.appendix": "Kopier", "page.duplicate.files": "Kopier filer", "page.duplicate.pages": "Kopier sider", + "page.move": "Move page", "page.sort": "Skift position", "page.status": "Status", "page.status.draft": "Kladde", @@ -450,26 +604,34 @@ "page.status.unlisted.description": "Siden er kun tilgængelig via URL", "pages": "Sider", + "pages.delete.confirm.selected": "Do you really want to delete the selected pages? This action cannot be undone.", "pages.empty": "Ingen sider endnu", "pages.status.draft": "Kladder", "pages.status.listed": "Udgivede", "pages.status.unlisted": "Ulistede", - "pagination.page": "Page", + "pagination.page": "Side", "password": "Adgangskode", "paste": "Indsæt", "paste.after": "Indsæt efter", + "paste.success": "{count} pasted!", "pixel": "Pixel", "plugin": "Plugin", "plugins": "Plugins", "prev": "Forrige", "preview": "Forhåndsvisning", + + "publish": "Publish", + "published": "Udgivede", + "remove": "Fjern", "rename": "Omdøb", + "renew": "Renew", "replace": "Erstat", - "retry": "Prøv igen", - "revert": "Kassér", + "replace.with": "Replace with", + "retry": "Pr\u00f8v igen", + "revert": "Kass\u00e9r", "revert.confirm": "Ønsker du virkelig at slette all ændringer der ikke er gemt?", "role": "Rolle", @@ -482,11 +644,14 @@ "role.nobody.title": "Ingen", "save": "Gem", + "saved": "Saved", "search": "Søg", + "searching": "Searching", "search.min": "Indtast {min} tegn for at søge", - "search.all": "Vis alle", + "search.all": "Show all {count} results", "search.results.none": "Ingen resultater", + "section.invalid": "The section is invalid", "section.required": "Sektionen er påkrævet", "security": "Security", @@ -498,16 +663,25 @@ "size": "Størrelse", "slug": "URL-appendiks", "sort": "Sorter", + "sort.drag": "Drag to sort …", + "split": "Split", "stats.empty": "No reports", + "status": "Status", + + "system.info.copy": "Copy info", + "system.info.copied": "System info copied", "system.issues.content": "The content folder seems to be exposed", "system.issues.eol.kirby": "Your installed Kirby version has reached end-of-life and will not receive further security updates", "system.issues.eol.plugin": "Your installed version of the { plugin } plugin is has reached end-of-life and will not receive further security updates", + "system.issues.eol.php": "Your installed PHP release { release } has reached end-of-life and will not receive further security updates", "system.issues.debug": "Debugging must be turned off in production", "system.issues.git": "The .git folder seems to be exposed", "system.issues.https": "We recommend HTTPS for all your sites", "system.issues.kirby": "The kirby folder seems to be exposed", + "system.issues.local": "The site is running locally with relaxed security checks", "system.issues.site": "The site folder seems to be exposed", + "system.issues.vue.compiler": "The Vue template compiler is enabled", "system.issues.vulnerability.kirby": "Your installation might be affected by the following vulnerability ({ severity } severity): { description }", "system.issues.vulnerability.plugin": "Your installation might be affected by the following vulnerability in the { plugin } plugin ({ severity } severity): { description }", "system.updateStatus": "Update status", @@ -520,10 +694,19 @@ "system.updateStatus.update": "Free update { version } available", "system.updateStatus.upgrade": "Upgrade { version } available", - "title": "Titel", + "tel": "Phone", + "tel.placeholder": "+49123456789", "template": "Skabelon", + + "theme": "Theme", + "theme.light": "Lights on", + "theme.dark": "Lights off", + "theme.automatic": "Match system default", + + "title": "Titel", "today": "Idag", + "toolbar.button.clear": "Clear formatting", "toolbar.button.code": "Kode", "toolbar.button.bold": "Fed tekst", "toolbar.button.email": "Email", @@ -535,12 +718,14 @@ "toolbar.button.heading.5": "Overskrift 5", "toolbar.button.heading.6": "Overskrift 6", "toolbar.button.italic": "Kursiv tekst", - "toolbar.button.file": "File", + "toolbar.button.file": "Fil", "toolbar.button.file.select": "Vælg en fil", "toolbar.button.file.upload": "Upload en fil", "toolbar.button.link": "Link", "toolbar.button.paragraph": "Afsnit", "toolbar.button.strike": "Gennemstreg", + "toolbar.button.sub": "Subscript", + "toolbar.button.sup": "Superscript", "toolbar.button.ol": "Ordnet liste", "toolbar.button.underline": "Understreg", "toolbar.button.ul": "Punktliste", @@ -550,6 +735,8 @@ "translation.name": "Dansk", "translation.locale": "da_DK", + "type": "Type", + "upload": "Upload", "upload.error.cantMove": "Den uploadede fil kunne ikke flyttes", "upload.error.cantWrite": "Kunne ikke skrive fil til disk", @@ -574,21 +761,25 @@ "user.changeLanguage": "Skift sprog", "user.changeName": "Omdøb denne bruger", "user.changePassword": "Skift adgangskode", + "user.changePassword.current": "Your current password", "user.changePassword.new": "Ny adgangskode", "user.changePassword.new.confirm": "Bekræft den nye adgangskode...", "user.changeRole": "Skift rolle", "user.changeRole.select": "Vælg en ny rolle", "user.create": "Tilføj en ny bruger", "user.delete": "Slet denne bruger", - "user.delete.confirm": "Ønsker du virkelig at slette denne bruger?", + "user.delete.confirm": "\u00d8nsker du virkelig at slette denne bruger?", "users": "Brugere", "version": "Kirby version", + "version.changes": "Changed version", + "version.compare": "Compare versions", "version.current": "Current version", "version.latest": "Latest version", "versionInformation": "Version information", + "view": "View", "view.account": "Din konto", "view.installation": "Installation", "view.languages": "Sprog", diff --git a/kirby/i18n/translations/de.json b/kirby/i18n/translations/de.json index caec627..f5f19ba 100644 --- a/kirby/i18n/translations/de.json +++ b/kirby/i18n/translations/de.json @@ -3,19 +3,28 @@ "account.delete": "Deinen Account löschen", "account.delete.confirm": "Willst du deinen Account wirklich löschen? Du wirst sofort danach abgemeldet. Dein Account kann nicht wieder hergestellt werden.", - "add": "Hinzufügen", + "activate": "Aktivieren", + "add": "Hinzuf\u00fcgen", + "alpha": "Alpha", "author": "Autor", "avatar": "Profilbild", "back": "Zurück", "cancel": "Abbrechen", - "change": "Ändern", - "close": "Schließen", + "change": "\u00c4ndern", + "close": "Schlie\u00dfen", + "changes": "Änderungen", "confirm": "OK", "collapse": "Zusammenklappen", "collapse.all": "Alle zusammenklappen", + "color": "Farbe", + "coordinates": "Koordinaten", "copy": "Kopieren", "copy.all": "Alle kopieren", + "copy.success": "Kopiert", + "copy.success.multiple": "{count} kopiert!", + "copy.url": "URL kopieren", "create": "Erstellen", + "custom": "Benutzerdefiniert", "date": "Datum", "date.select": "Datum auswählen", @@ -31,16 +40,23 @@ "debugging": "Debugging", - "delete": "Löschen", + "delete": "L\u00f6schen", "delete.all": "Alle löschen", + "dialog.fields.empty": "Der Dialog hat keine Felder", "dialog.files.empty": "Keine verfügbaren Dateien", "dialog.pages.empty": "Keine verfügbaren Seiten", + "dialog.text.empty": "Dieser Dialog definiert keinen Text", "dialog.users.empty": "Keine verfügbaren Accounts", "dimensions": "Maße", + "disable": "Deaktivieren", "disabled": "Gesperrt", "discard": "Verwerfen", + + "drawer.fields.empty": "Die Schublade hat keine Felder", + + "domain": "Domain", "download": "Download", "duplicate": "Duplizieren", @@ -49,18 +65,20 @@ "email": "E-Mail", "email.placeholder": "mail@beispiel.de", + "enter": "Enter", "entries": "Einträge", "entry": "Eintrag", "environment": "Umgebung", + "error": "Fehler", "error.access.code": "Ungültiger Code", "error.access.login": "Ungültige Zugangsdaten", "error.access.panel": "Du hast keinen Zugang zum Panel", "error.access.view": "Du hast keinen Zugriff auf diesen Teil des Panels", "error.avatar.create.fail": "Das Profilbild konnte nicht hochgeladen werden", - "error.avatar.delete.fail": "Das Profilbild konnte nicht gelöscht werden", + "error.avatar.delete.fail": "Das Profilbild konnte nicht gel\u00f6scht werden", "error.avatar.dimensions.invalid": "Bitte lade ein Profilbild hoch, das nicht breiter oder höher als 3000 Pixel ist.", "error.avatar.mime.forbidden": "Das Profilbild muss vom Format JPEG oder PNG sein", @@ -74,13 +92,31 @@ "error.cache.type.invalid": "Ungültiger Cachetyp: \"{type}\"", + "error.content.lock.delete": "Die Version ist blockiert und kann daher nicht gelöscht werden.", + "error.content.lock.move": "Die Ursprungsversion ist blockiert und kann daher nicht gelöscht werden", + "error.content.lock.publish": "Die Version wurde bereits veröffentlicht", + "error.content.lock.replace": "Die Version ist blockiert und kann daher nicht ersetzt werden", + "error.content.lock.update": "Die Version ist blockiert und kann daher nicht geändert werden", + + "error.entries.max.plural": "Bitte füge nicht mehr als {max} Einträge hinzu", + "error.entries.max.singular": "Bitte füge nicht mehr als einen Eintrag hinzu", + "error.entries.min.plural": "Bitte füge mindestens {min} Einträge hinzu", + "error.entries.min.singular": "Bitte füge mindestens einen Eintrag hinzu", + "error.entries.supports": "Der Feldtyp \"{type}\" wird im Entries Feld nicht unterstützt.", + "error.entries.validation": "Fehler im Feld \"{field}\" in Zeile {index}", + "error.email.preset.notFound": "Die E-Mailvorlage \"{name}\" wurde nicht gefunden", "error.field.converter.invalid": "Ungültiger Konverter: \"{converter}\"", + "error.field.link.options": "Ungültige Optionen: {options}", "error.field.type.missing": "Feld \"{ name }\": Der Feldtyp \"{ type }\" existiert nicht", "error.file.changeName.empty": "Bitte gib einen Namen an", "error.file.changeName.permission": "Du darfst den Dateinamen von \"{filename}\" nicht ändern", + "error.file.changeTemplate.invalid": "Die Vorlage für die Datei \"{id}\" kann nicht zu \"{template}\" geändert werden (gültig: \"{blueprints}\")", + "error.file.changeTemplate.permission": "Du kannst die Vorlage für die Datei \"{id}\" nicht ändern", + + "error.file.delete.multiple": "Es konnten nicht alle Dateien gelöscht werden. Versuche verbleibende Dateien einzeln zu löschen, um die Ursachen festzustellen.", "error.file.duplicate": "Eine Datei mit dem Dateinamen \"{filename}\" besteht bereits", "error.file.extension.forbidden": "Verbotene Dateiendung \"{extension}\"", "error.file.extension.invalid": "Verbotene Dateiendung \"{extension}\"", @@ -95,9 +131,11 @@ "error.file.minheight": "Die Bildhöhe muss mindestens {height} Pixel betragen", "error.file.minsize": "Die Datei ist zu klein", "error.file.minwidth": "Die Bildbreite muss mindestens {width} Pixel betragen", + "error.file.name.unique": "Der Dateiname besteht bereits", "error.file.name.missing": "Bitte gib einen Dateinamen an", "error.file.notFound": "Die Datei \"{filename}\" konnte nicht gefunden werden", "error.file.orientation": "Das Bildformat ist ungültig. Erwartetes Format: \"{orientation}\"", + "error.file.sort.permission": "Du darfst die Sortierung für \"{filename}\" nicht ändern.", "error.file.type.forbidden": "Du kannst keinen {type}-Dateien hochladen", "error.file.type.invalid": "Ungültiger Dateityp: {mime}", "error.file.undefined": "Die Datei konnte nicht gefunden werden", @@ -106,22 +144,30 @@ "error.form.notSaved": "Das Formular konnte nicht gespeichert werden", "error.language.code": "Bitte gib einen gültigen Code für die Sprache an", + "error.language.create.permission": "Du darfst keine Sprache anlegen", + "error.language.delete.permission": "Du darfst diese Sprache nicht löschen", "error.language.duplicate": "Die Sprache besteht bereits", "error.language.name": "Bitte gib einen gültigen Namen für die Sprache an", "error.language.notFound": "Die Sprache konnte nicht gefunden werden", + "error.language.update.permission": "Du darfst diese Sprache nicht bearbeiten", "error.layout.validation.block": "Fehler im \"{field}\" Feld in Block {blockIndex} mit dem Blocktyp \"{fieldset}\" in Layout {layoutIndex}", "error.layout.validation.settings": "Fehler in den Einstellungen von Layout {index}", - "error.license.format": "Bitte gib einen gültigen Lizenzschlüssel ein", + "error.license.domain": "Die Domain für die Lizenz fehlt", "error.license.email": "Bitte gib eine gültige E-Mailadresse an", + "error.license.format": "Bitte gib einen gültigen Lizenzschlüssel ein", "error.license.verification": "Die Lizenz konnte nicht verifiziert werden", + "error.login.totp.confirm.invalid": "Ungültiger Code", + "error.login.totp.confirm.missing": "Bitte gib den aktuellen Code ein", + "error.object.validation": "Fehler im \"{label}\" Feld:\n{message}", "error.offline": "Das Panel ist zur Zeit offline", "error.page.changeSlug.permission": "Du darfst die URL der Seite \"{slug}\" nicht ändern", + "error.page.changeSlug.reserved": "Der Pfad für Top-Level Seiten darf nicht mit \"{path}\" beginnen.", "error.page.changeStatus.incomplete": "Die Seite ist nicht vollständig und kann daher nicht veröffentlicht werden", "error.page.changeStatus.permission": "Der Status der Seite kann nicht geändert werden", "error.page.changeStatus.toDraft.invalid": "Die Seite \"{slug}\" kann nicht in einen Entwurf umgewandelt werden", @@ -133,10 +179,18 @@ "error.page.delete": "Die Seite \"{slug}\" kann nicht gelöscht werden", "error.page.delete.confirm": "Bitte gib zur Bestätigung den Seitentitel ein", "error.page.delete.hasChildren": "Die Seite hat Unterseiten und kann nicht gelöscht werden", + "error.page.delete.multiple": "Es konnten nicht alle Seiten gelöscht werden. Versuche verbleibende Seiten einzeln zu löschen, um die Ursachen festzustellen.", "error.page.delete.permission": "Du kannst die Seite \"{slug}\" nicht löschen", "error.page.draft.duplicate": "Ein Entwurf mit dem URL-Kürzel \"{slug}\" besteht bereits", "error.page.duplicate": "Eine Seite mit dem URL-Kürzel \"{slug}\" besteht bereits", "error.page.duplicate.permission": "Du kannst die Seite \"{slug}\" nicht duplizieren", + "error.page.move.ancestor": "Die Seite kann nicht in sich selbst verschoben werden", + "error.page.move.directory": "Der Ordner der Seite kann nicht verschoben werden", + "error.page.move.duplicate": "Eine Seite mit dem URL-Kürzel \"{slug}\" besteht bereits", + "error.page.move.noSections": "Die Seite \"{parent}\" kann nicht ausgewählt werden, weil sie keine Unterseiten haben kann. ", + "error.page.move.notFound": "Die verschobene Seite kann nicht gefunden werden", + "error.page.move.permission": "Du kannst die Seite \"{slug}\" nicht verschieben", + "error.page.move.template": "Die Vorlage \"{template}\" wird nicht als Unterseite von \"{parent}\" akzeptiert", "error.page.notFound": "Die Seite \"{slug}\" konnte nicht gefunden werden", "error.page.num.invalid": "Bitte gib eine gültige Sortierungszahl an. Negative Zahlen sind nicht erlaubt.", "error.page.slug.invalid": "Bitte gib ein gültiges URL-Kürzel an", @@ -163,6 +217,8 @@ "error.site.changeTitle.permission": "Du kannst den Titel der Seite nicht ändern", "error.site.update.permission": "Du darfst die Seite nicht bearbeiten", + "error.structure.validation": "Fehler im Feld \"{field}\" in Zeile {index}", + "error.template.default.notFound": "Die \"Default\"-Vorlage existiert nicht", "error.unexpected": "Ein unerwarteter Fehler ist aufgetreten. Aktiviere den Debug-Modus für weitere Informationen: https://getkirby.com/docs/reference/system/options/debug", @@ -195,8 +251,10 @@ "error.validation.accepted": "Bitte bestätige", "error.validation.alpha": "Bitte gib nur Zeichen zwischen A und Z ein", "error.validation.alphanum": "Bitte gib nur Zeichen zwischen A und Z und Zahlen zwischen 0 und 9 ein", + "error.validation.anchor": "Bitte gib einen korrekten Anker an", "error.validation.between": "Bitte gib einen Wert zwischen \"{min}\" und \"{max}\" ein", "error.validation.boolean": "Bitte bestätige oder lehne ab", + "error.validation.color": "Bitte gib eine gültige Farbe im Format {format} ein", "error.validation.contains": "Bitte gib einen Wert ein, der \"{needle}\" enthält", "error.validation.date": "Bitte gib ein gültiges Datum ein", "error.validation.date.after": "Bitte gib ein Datum nach dem {date} ein", @@ -211,6 +269,7 @@ "error.validation.integer": "Bitte gib eine ganze Zahl ein", "error.validation.ip": "Bitte gib eine gültige IP Adresse ein", "error.validation.less": "Bitte gib einen Wert kleiner als {max} ein", + "error.validation.linkType": "Der Linktyp ist nicht erlaubt", "error.validation.match": "Der Wert entspricht nicht dem erwarteten Muster", "error.validation.max": "Bitte gib einen Wert ein, der nicht größer als {max} ist", "error.validation.maxlength": "Bitte gib einen kürzeren Text ein (max. {max} Zeichen)", @@ -227,15 +286,18 @@ "error.validation.same": "Bitte gib \"{other}\" ein", "error.validation.size": "Die Größe des Wertes muss \"{size}\" sein", "error.validation.startswith": "Der Wert muss mit \"{start}\" beginnen", + "error.validation.tel": "Bitte gib eine unformatierte Telefonnummer an", "error.validation.time": "Bitte gib eine gültige Uhrzeit ein", "error.validation.time.after": "Bitte gib eine Zeit nach {time} ein", "error.validation.time.before": "Bitte gib eine Zeit vor {time} ein", "error.validation.time.between": "Bitte gib eine Zeit zwischen {min} und {max} ein", + "error.validation.uuid": "Bitte gib eine gültige UUID an", "error.validation.url": "Bitte gib eine gültige URL ein", "expand": "Aufklappen", "expand.all": "Alle aufklappen", + "field.invalid": "Das Feld ist ungültig", "field.required": "Das Feld ist Pflicht", "field.blocks.changeType": "Blocktyp ändern", "field.blocks.code.name": "Code", @@ -245,8 +307,9 @@ "field.blocks.delete.confirm.all": "Willst du wirklich alle Blöcke löschen?", "field.blocks.delete.confirm.selected": "Willst du wirklich die ausgewählten Blöcke löschen?", "field.blocks.empty": "Keine Blöcke", + "field.blocks.fieldsets.empty": "Keine Block Definition", "field.blocks.fieldsets.label": "Bitte wähle einen Blocktyp aus …", - "field.blocks.fieldsets.paste": "Drücke {{ shortcut }} um Blöcke aus der Zwischenablage zu importieren", + "field.blocks.fieldsets.paste": "Drücke {{ shortcut }} um Layouts/Blocks von deinem Clipboard zu importieren. Nur die, die im aktuellen Feld erlaubt sind werden eingefügt.", "field.blocks.gallery.name": "Galerie", "field.blocks.gallery.images.empty": "Keine Bilder", "field.blocks.gallery.images.label": "Bilder", @@ -254,11 +317,16 @@ "field.blocks.heading.name": "Überschrift", "field.blocks.heading.text": "Text", "field.blocks.heading.placeholder": "Überschrift …", + "field.blocks.figure.back.plain": "Ohne", + "field.blocks.figure.back.pattern.light": "Muster (hell)", + "field.blocks.figure.back.pattern.dark": "Muster (dunkel)", "field.blocks.image.alt": "Alternativer Text", "field.blocks.image.caption": "Bildunterschrift", "field.blocks.image.crop": "Beschneiden", "field.blocks.image.link": "Link", "field.blocks.image.location": "Ort", + "field.blocks.image.location.internal": "Diese Webseite", + "field.blocks.image.location.external": "Externe Quelle", "field.blocks.image.name": "Bild", "field.blocks.image.placeholder": "Bild auswählen", "field.blocks.image.ratio": "Seitenverhältnis", @@ -275,41 +343,75 @@ "field.blocks.quote.citation.placeholder": "Quelle …", "field.blocks.text.name": "Text", "field.blocks.text.placeholder": "Text …", + "field.blocks.video.autoplay": "Autoplay", "field.blocks.video.caption": "Bildunterschrift", + "field.blocks.video.controls": "Steuerung", + "field.blocks.video.location": "Ort", + "field.blocks.video.loop": "Schleife", + "field.blocks.video.muted": "Stumm", "field.blocks.video.name": "Video", "field.blocks.video.placeholder": "Video-URL eingeben", + "field.blocks.video.poster": "Poster", + "field.blocks.video.preload": "Vorladen", "field.blocks.video.url.label": "Video-URL", "field.blocks.video.url.placeholder": "https://youtube.com/?v=", - "field.files.empty": "Keine Dateien ausgewählt", + "field.entries.delete.confirm.all": "Möchtest du wirklich alle Einträge löschen?", + "field.entries.empty": "Es bestehen keine Einträge.", + "field.files.empty": "Keine Dateien ausgewählt", + "field.files.empty.single": "Keine Dateien ausgewählt", + + "field.layout.change": "Layout ändern", "field.layout.delete": "Layout löschen", "field.layout.delete.confirm": "Willst du dieses Layout wirklich löschen?", + "field.layout.delete.confirm.all": "Willst du wirklich alle Layouts löschen?", "field.layout.empty": "Keine Layouts", "field.layout.select": "Layout auswählen", "field.object.empty": "Noch keine Information", "field.pages.empty": "Keine Seiten ausgewählt", + "field.pages.empty.single": "Keine Seiten ausgewählt", - "field.structure.delete.confirm": "Willst du diesen Eintrag wirklich löschen?", + "field.structure.delete.confirm": "Willst du diesen Eintrag wirklich l\u00f6schen?", "field.structure.delete.confirm.all": "Möchtest du wirklich alle Einträge löschen?", - "field.structure.empty": "Es bestehen keine Einträge.", + "field.structure.empty": "Es bestehen keine Eintr\u00e4ge.", "field.users.empty": "Keine Accounts ausgewählt", + "field.users.empty.single": "Keine Accounts ausgewählt", + "fields.empty": "Keine Felder", + + "file": "Datei", "file.blueprint": "Du kannst zusätzliche Felder und Bereiche für diese Datei in /site/blueprints/files/{blueprint}.yml anlegen", + "file.changeTemplate": "Vorlage ändern", + "file.changeTemplate.notice": "Das Ändern der Dateivorlage wird alle Inhalte von Feldern entfernen, deren Feldtypen nicht übereinstimmen. Wenn die neue Vorlage bestimmte Regeln definiert, z.B. Bildabmessungen, werden diese unwiderruflich angewandt. Benutze diese Funktion mit Vorsicht.", "file.delete.confirm": "Willst du die Datei {filename}
wirklich löschen?", + "file.focus.placeholder": "Fokuspunkt setzen", + "file.focus.reset": "Fokuspunkt entfernen", + "file.focus.title": "Fokus", "file.sort": "Position ändern", "files": "Dateien", + "files.delete.confirm.selected": "Willst du wirklich die ausgewählten Dateien löschen? Diese Aktion kann nicht rückgängig gemacht werden.", "files.empty": "Keine Dateien", + "filter": "Filter", + + "form.discard": "Änderungen verwerfen", + "form.discard.confirm": "Willst du wirklich alle ungespeicherten Änderungen verwerfen? ", + "form.locked": "Dieser Inhalt ist gesperrt, weil er aktuell von einem anderen Account bearbeitet wird", + "form.unsaved": "Die aktuellen Änderungen wurden noch nicht gespeichert", + "form.preview": "Änderungsvorschau", + "form.preview.draft": "Entwurfsvorschau", + "hide": "Verbergen", "hour": "Stunde", + "hue": "Farbton", "import": "Importieren", "info": "Info", - "insert": "Einfügen", + "insert": "Einf\u00fcgen", "insert.after": "Danach einfügen", "insert.before": "Davor einfügen", "install": "Installieren", @@ -324,7 +426,6 @@ "installation.issues.mbstring": "Die MB String Erweiterung wird benötigt", "installation.issues.media": "Der /media Ordner ist nicht beschreibbar", "installation.issues.php": "Bitte verwende PHP 8+", - "installation.issues.server": "Kirby benötigt Apache, Nginx or Caddy", "installation.issues.sessions": "/site/sessions ist nicht beschreibbar", "language": "Sprache", @@ -332,6 +433,7 @@ "language.convert": "Als Standard auswählen", "language.convert.confirm": "

Willst du {name} wirklich in die Standardsprache umwandeln? Dieser Schritt kann nicht rückgängig gemacht werden.

Wenn {name} unübersetzte Felder hat, gibt es keine gültigen Standardwerte für diese Felder und Inhalte könnten verloren gehen.

", "language.create": "Neue Sprache anlegen", + "language.default": "Standardsprache", "language.delete.confirm": "Willst du {name} inklusive aller Übersetzungen wirklich löschen? Dieser Schritt kann nicht rückgängig gemacht werden!", "language.deleted": "Die Sprache wurde gelöscht", "language.direction": "Leserichtung", @@ -340,7 +442,16 @@ "language.locale": "PHP locale string", "language.locale.warning": "Du nutzt ein angepasstes Setup for PHP Locales. Bitte bearbeite dieses direkt in der entsprechenden Sprachdatei in /site/languages", "language.name": "Name", + "language.secondary": "Sekundäre Sprachen", + "language.settings": "Spracheinstellungen", "language.updated": "Die Sprache wurde gespeichert", + "language.variables": "Sprachvariablen", + "language.variables.empty": "Keine Übersetzung", + + "language.variable.delete.confirm": "Willst du wirklich die Variable \"{key}\" entfernen?", + "language.variable.key": "Schlüssel", + "language.variable.notFound": "Die Variable konnte nicht gefunden werden", + "language.variable.value": "Wert", "languages": "Sprachen", "languages.default": "Standardsprache", @@ -349,15 +460,32 @@ "languages.secondary.empty": "Noch keine sekundären Sprachen", "license": "Lizenz", + "license.activate": "Aktiviere Kirby jetzt", + "license.activate.label": "Bitte aktiviere deine Lizenz", + "license.activate.domain": "Deine Lizenz wird für die Domain {host} aktiviert.", + "license.activate.local": "Du bist dabei, deine Kirby Lizenz für die lokale Domain {host} zu aktivieren. Falls diese Seite später unter einer anderen Domain veröffentlicht wird, solltest du sie erst dort aktivieren. Falls {host} die Domain ist, die du für deine Lizenz nutzen möchtest, fahre bitte fort. ", + "license.activated": "Aktiviert", "license.buy": "Kaufe eine Lizenz", - "license.register": "Registrieren", + "license.code": "Code", + "license.code.help": "Du hast deinen Lizenz Code nach dem Kauf per Email bekommen. Bitte kopiere sie aus der Email und füge sie hier ein. ", + "license.code.label": "Bitte gib deinen Lizenzcode ein", + "license.status.active.info": "Beinhaltet neue Major Versionen bis {date}", + "license.status.active.label": "Gültige Lizenz", + "license.status.demo.info": "Dies ist eine Demo Installation", + "license.status.demo.label": "Demo", + "license.status.inactive.info": "Lizenz erneuern, um auf neue Major Versionen upzudaten", + "license.status.inactive.label": "Keine neuen Major Versionen", + "license.status.legacy.bubble": "Bereit, die Lizenz zu erneuern? ", + "license.status.legacy.info": "Deine Lizenz deckt diese Version nicht ab", + "license.status.legacy.label": "Bitte erneuere deine Lizenz", + "license.status.missing.bubble": "Bereit, deine Seite zu veröffentlichen?", + "license.status.missing.info": "Keine gültige Lizenz", + "license.status.missing.label": "Bitte aktiviere deine Lizenz", + "license.status.unknown.info": "Der Lizenzstatus ist unbekannt", + "license.status.unknown.label": "Unbekannt", "license.manage": "Verwalte deine Lizenzen", - "license.register.help": "Den Lizenzcode findest du in der Bestätigungsmail zu deinem Kauf. Bitte kopiere und füge ihn ein, um Kirby zu registrieren.", - "license.register.label": "Bitte gib deinen Lizenzcode ein", - "license.register.domain": "Deine Lizenz wird unter der Domain {host} registriert", - "license.register.local": "Du bist dabei, deine Lizenz unter der lokalen Domain {host} zu registrieren. Wenn diese Seite unter einer anderen Domain veröffentlicht werden soll, registriere die Lizenz statt dessen bitte dort. Wenn du {host} als Domain verwenden möchtest, fahre bitte fort. ", - "license.register.success": "Vielen Dank für deine Unterstützung", - "license.unregistered": "Dies ist eine unregistrierte Kirby-Demo", + "license.purchased": "Gekauft", + "license.success": "Vielen Dank für deine Unterstützung", "license.unregistered.label": "Unregistriert", "link": "Link", @@ -367,17 +495,21 @@ "lock.unsaved": "Ungespeicherte Änderungen", "lock.unsaved.empty": "Keine ungespeicherten Änderungen", - "lock.isLocked": "Ungespeicherte Änderungen von {email}", - "lock.file.isLocked": "Die Datei wird von {email} bearbeitet und kann nicht geändert werden.", - "lock.page.isLocked": "Die Seite wird von {email} bearbeitet und kann nicht geändert werden.", + "lock.unsaved.files": "Geänderte Dateien", + "lock.unsaved.pages": "Geänderte Seiten", + "lock.unsaved.users": "Geänderte Accounts", + "lock.isLocked": "Ungespeicherte Änderungen von {email}", "lock.unlock": "Entsperren", - "lock.isUnlocked": "Deine ungespeicherten Änderungen wurden von einem anderen Account überschrieben. Du kannst sie herunterladen, um sie manuell einzufügen. ", + "lock.unlock.submit": "Entsperre und überschreibe ungespeicherte Änderungen von {email}", + "lock.isUnlocked": "Wurde von einem*r anderen Benutzer*in überschrieben", "login": "Anmelden", "login.code.label.login": "Anmeldecode", "login.code.label.password-reset": "Anmeldecode", "login.code.placeholder.email": "000 000", + "login.code.placeholder.totp": "000000", "login.code.text.email": "Wenn deine E-Mail-Adresse registriert ist, wurde der angeforderte Code per E-Mail versendet.", + "login.code.text.totp": "Bitte gib den Einmal-Code von deiner Authentifizierungs-App ein. ", "login.email.login.body": "Hallo {user.nameOrEmail},\n\ndu hast gerade einen Anmeldecode für das Kirby Panel von {site} angefordert.\n\nDer folgende Anmeldecode ist für die nächsten {timeout} Minuten gültig:\n\n{code}\n\nWenn du keinen Anmeldecode angefordert hast, ignoriere bitte diese E-Mail oder kontaktiere bei Fragen deinen Administrator.\nBitte leite diese E-Mail aus Sicherheitsgründen NICHT weiter.", "login.email.login.subject": "Dein Anmeldecode", "login.email.password-reset.body": "Hallo {user.nameOrEmail},\n\ndu hast gerade einen Anmeldecode für das Kirby Panel von {site} angefordert.\n\nDer folgende Anmeldecode ist für die nächsten {timeout} Minuten gültig:\n\n{code}\n\nWenn du keinen Anmeldecode angefordert hast, ignoriere bitte diese E-Mail oder kontaktiere bei Fragen deinen Administrator.\nBitte leite diese E-Mail aus Sicherheitsgründen NICHT weiter.", @@ -388,9 +520,24 @@ "login.toggleText.code.email-password": "Anmelden mit Passwort", "login.toggleText.password-reset.email": "Passwort vergessen?", "login.toggleText.password-reset.email-password": "← Zurück zur Anmeldung", + "login.totp.enable.option": "Einmal-Codes einrichten", + "login.totp.enable.intro": "Authentifizierungs-Apps können Einmal-Codes erstellen, die als zweiter Faktor für die Anmeldung dienen. ", + "login.totp.enable.qr.label": "1. Scanne diesen QR Code", + "login.totp.enable.qr.help": "Scannen funktioniert nicht? Gib den Setup-Schlüssel {secret} manuell in deiner Authentifizierungs-App ein. ", + "login.totp.enable.confirm.headline": "2. Bestätige den erstellten Code.", + "login.totp.enable.confirm.text": "Deine App erstellt alle 30 Sekunden einen neuen Einmal-Code. Gib den aktuellen Code ein, um das Setup abzuschliessen. ", + "login.totp.enable.confirm.label": "Aktueller Code", + "login.totp.enable.confirm.help": "Nach dem Setup werden wir dich bei jeder Anmeldung nach einem Einmal-Code fragen. ", + "login.totp.enable.success": "Einmal-Codes aktiviert", + "login.totp.disable.option": "Einmal-Codes deaktivieren", + "login.totp.disable.label": "Gib dein Passwort ein, um die Einmal-Codes zu deaktivieren. ", + "login.totp.disable.help": "In Zukunft wird bei der Anmeldung ein anderer zweiter Faktor abgefragt. Z.B. ein Login-Code der per Email zugeschickt wird. Du kannst die Einmal-Codes jeder Zeit später wieder neu einrichten. ", + "login.totp.disable.admin": "

Einmal-Codes für {user} werden hiermit deaktiviert.

In Zukunft wird für die Anmeldung ein anderer zweiter Faktor abgefragt. Z.B. ein Login-Code, der per Email zugeschickt wird. {user} kann nach der nächsten Anmeldung jeder Zeit wieder Einmal-Codes für den Account aktivieren.

", + "login.totp.disable.success": "Einmal-Codes deaktiviert", "logout": "Abmelden", + "merge": "Zusammenfügen", "menu": "Menü", "meridiem": "AM/PM", "mime": "Medientyp", @@ -404,42 +551,49 @@ "months.january": "Januar", "months.july": "Juli", "months.june": "Juni", - "months.march": "März", + "months.march": "M\u00e4rz", "months.may": "Mai", "months.november": "November", "months.october": "Oktober", "months.september": "September", "more": "Mehr", + "move": "Bewegen", "name": "Name", "next": "Nächster Eintrag", + "night": "Nacht", "no": "nein", "off": "aus", "on": "an", "open": "Öffnen", "open.newWindow": "In neuem Fenster öffnen", + "option": "Option", "options": "Optionen", "options.none": "Keine Optionen", + "options.all": "Zeige alle {count} Optionen", "orientation": "Ausrichtung", "orientation.landscape": "Querformat", "orientation.portrait": "Hochformat", "orientation.square": "Quadratisch", + "page": "Seite", "page.blueprint": "Du kannst zusätzliche Felder und Bereiche für diese Seite in /site/blueprints/pages/{blueprint}.yml anlegen", - "page.changeSlug": "URL ändern", + "page.changeSlug": "URL \u00e4ndern", "page.changeSlug.fromTitle": "Aus Titel erzeugen", "page.changeStatus": "Status ändern", "page.changeStatus.position": "Bitte wähle eine Position aus", "page.changeStatus.select": "Wähle einen neuen Status aus", "page.changeTemplate": "Vorlage ändern", + "page.changeTemplate.notice": "Das Ändern der Vorlage wird Inhalte entfernen, deren Feldtypen nicht übereinstimmen. Verwende diese Funktion mit Vorsicht.", + "page.create": "Anlegen als \"{status}\"", "page.delete.confirm": "Willst du die Seite {title} wirklich löschen?", "page.delete.confirm.subpages": "Diese Seite hat Unterseiten.
Alle Unterseiten werden ebenfalls gelöscht.", "page.delete.confirm.title": "Gib zur Bestätigung den Seitentitel ein", - "page.draft.create": "Entwurf anlegen", - "page.duplicate.appendix": "Kopieren", + "page.duplicate.appendix": "Kopie", "page.duplicate.files": "Dateien kopieren", "page.duplicate.pages": "Seiten kopieren", + "page.move": "Seite bewegen", "page.sort": "Position ändern", "page.status": "Status", "page.status.draft": "Entwurf", @@ -450,6 +604,7 @@ "page.status.unlisted.description": "Die Seite kann nur über die URL aufgerufen werden", "pages": "Seiten", + "pages.delete.confirm.selected": "Willst du wirklich die ausgewählten Seiten löschen? Diese Aktion kann nicht rückgängig gemacht werden.", "pages.empty": "Keine Seiten", "pages.status.draft": "Entwürfe", "pages.status.listed": "Veröffentlicht", @@ -460,14 +615,21 @@ "password": "Passwort", "paste": "Einfügen", "paste.after": "Danach einfügen", + "paste.success": "{count} eingefügt!", "pixel": "Pixel", "plugin": "Plugin", "plugins": "Plugins", "prev": "Vorheriger Eintrag", "preview": "Vorschau", + + "publish": "Veröffentlichen", + "published": "Veröffentlicht", + "remove": "Entfernen", "rename": "Umbenennen", + "renew": "Erneuern", "replace": "Ersetzen", + "replace.with": "Ersetzen mit", "retry": "Wiederholen", "revert": "Verwerfen", "revert.confirm": "Willst du wirklich alle ungespeicherten Änderungen verwerfen? ", @@ -482,11 +644,14 @@ "role.nobody.title": "Niemand", "save": "Speichern", + "saved": "Gespeichert", "search": "Suchen", + "searching": "Suchen", "search.min": "Gib mindestens {min}  Zeichen ein, um zu suchen", - "search.all": "Alles zeigen", + "search.all": "Zeige alle {count} Ergebnisse", "search.results.none": "Keine Ergebnisse", + "section.invalid": "Der Bereich ist ungültig", "section.required": "Der Bereich ist Pflicht", "security": "Sicherheit", @@ -498,16 +663,25 @@ "size": "Größe", "slug": "URL-Anhang", "sort": "Sortieren", + "sort.drag": "Bewegen um zu sortieren …", + "split": "Teilen", "stats.empty": "Keine Daten", + "status": "Status", + + "system.info.copy": "Info kopieren", + "system.info.copied": "System Info wurde kopiert", "system.issues.content": "Der content Ordner scheint öffentlich zugänglich zu sein", "system.issues.eol.kirby": "Deine Kirby Installation ist veraltet und erhält keine weiteren Sicherheitsupdates", "system.issues.eol.plugin": "Deine Version des { plugin } Plugins ist veraltet und erhält keine weiteren Sicherheitsupdates", + "system.issues.eol.php": "Deine installierte PHP-Version { release } ist veraltet und erhält keinen Sicherheits-Updates mehr", "system.issues.debug": "Debugging muss im öffentlichen Betrieb ausgeschaltet sein", "system.issues.git": "Der .git Ordner scheint öffentlich zugänglich zu sein", "system.issues.https": "Wir empfehlen HTTPS für alle deine Seiten", "system.issues.kirby": "Der kirby Ordner scheint öffentlich zugänglich zu sein", + "system.issues.local": "Die Seite läuft lokal mit abgeschwächten Sicherheitschecks", "system.issues.site": "Der site Ordner scheint öffentlich zugänglich zu sein", + "system.issues.vue.compiler": "Der Vue Template Compiler ist aktiviert", "system.issues.vulnerability.kirby": "Deine Installation könnte von folgender Sicherheitslücke betroffen sein ({ severity } severity): { description }", "system.issues.vulnerability.plugin": "Deine Installation könnte von folgender Sicherheitslücke im { plugin } Plugin betroffen sein ({ severity } severity): { description }", "system.updateStatus": "Update Status", @@ -520,10 +694,19 @@ "system.updateStatus.update": "Kostenloses Update { version } verfügbar", "system.updateStatus.upgrade": "Upgrade { version } verfügbar", - "title": "Titel", + "tel": "Telefon", + "tel.placeholder": "+49123456789", "template": "Vorlage", + + "theme": "Thema", + "theme.light": "Licht an", + "theme.dark": "Licht aus", + "theme.automatic": "Systemeinstellung übernehmen", + + "title": "Titel", "today": "Heute", + "toolbar.button.clear": "Formatierung entfernen", "toolbar.button.code": "Code", "toolbar.button.bold": "Fetter Text", "toolbar.button.email": "E-Mail", @@ -541,6 +724,8 @@ "toolbar.button.link": "Link", "toolbar.button.paragraph": "Absatz", "toolbar.button.strike": "Durchgestrichen", + "toolbar.button.sub": "Tiefgestellt", + "toolbar.button.sup": "Hochgestellt", "toolbar.button.ol": "Geordnete Liste", "toolbar.button.underline": "Unterstrichen", "toolbar.button.ul": "Ungeordnete Liste", @@ -550,6 +735,8 @@ "translation.name": "Deutsch", "translation.locale": "de_DE", + "type": "Typ", + "upload": "Hochladen", "upload.error.cantMove": "Die Datei konnte nicht an ihren Zielort bewegt werden", "upload.error.cantWrite": "Die Datei konnte nicht auf der Festplatte gespeichert werden", @@ -574,6 +761,7 @@ "user.changeLanguage": "Sprache ändern", "user.changeName": "Account umbenennen", "user.changePassword": "Passwort ändern", + "user.changePassword.current": "Dein aktuelles Passwort", "user.changePassword.new": "Neues Passwort", "user.changePassword.new.confirm": "Wiederhole das Passwort …", "user.changeRole": "Rolle ändern", @@ -585,10 +773,13 @@ "users": "Accounts", "version": "Version", + "version.changes": "Geänderte Version", + "version.compare": "Versionen vergleichen", "version.current": "Aktuelle Version", "version.latest": "Neueste Version", "versionInformation": "Informationen zur Version", + "view": "Ansicht", "view.account": "Dein Account", "view.installation": "Installation", "view.languages": "Sprachen", diff --git a/kirby/i18n/translations/el.json b/kirby/i18n/translations/el.json index 8d37a84..1679c6d 100644 --- a/kirby/i18n/translations/el.json +++ b/kirby/i18n/translations/el.json @@ -3,57 +3,75 @@ "account.delete": "Delete your account", "account.delete.confirm": "Do you really want to delete your account? You will be logged out immediately. Your account cannot be recovered.", - "add": "Add", + "activate": "Activate", + "add": "\u03a0\u03c1\u03bf\u03c3\u03b8\u03ae\u03ba\u03b7", + "alpha": "Alpha", "author": "Author", - "avatar": "Profile picture", - "back": "Back", - "cancel": "Cancel", - "change": "Change", - "close": "Close", - "confirm": "Ok", + "avatar": "\u0395\u03b9\u03ba\u03cc\u03bd\u03b1 \u03c0\u03c1\u03bf\u03c6\u03af\u03bb", + "back": "Πίσω", + "cancel": "\u0391\u03ba\u03cd\u03c1\u03c9\u03c3\u03b7", + "change": "\u0391\u03bb\u03bb\u03b1\u03b3\u03ae", + "close": "\u039a\u03bb\u03b5\u03af\u03c3\u03b9\u03bc\u03bf", + "changes": "Changes", + "confirm": "Εντάξει", "collapse": "Collapse", "collapse.all": "Collapse All", + "color": "Color", + "coordinates": "Coordinates", "copy": "Αντιγραφή", "copy.all": "Copy all", + "copy.success": "Copied", + "copy.success.multiple": "{count} copied!", + "copy.url": "Copy URL", "create": "Δημιουργία", + "custom": "Custom", - "date": "Date", + "date": "Ημερομηνία", "date.select": "Επιλογή ημερομηνίας", "day": "Ημέρα", - "days.fri": "Fri", - "days.mon": "Mon", - "days.sat": "Sat", - "days.sun": "Κυρ", - "days.thu": "Thu", - "days.tue": "Tue", - "days.wed": "Wed", + "days.fri": "\u03a0\u03b1\u03c1", + "days.mon": "\u0394\u03b5\u03c5", + "days.sat": "\u03a3\u03ac\u03b2", + "days.sun": "\u039a\u03c5\u03c1", + "days.thu": "\u03a0\u03ad\u03bc", + "days.tue": "\u03a4\u03c1\u03af", + "days.wed": "\u03a4\u03b5\u03c4", "debugging": "Debugging", - "delete": "Delete", + "delete": "\u0394\u03b9\u03b1\u03b3\u03c1\u03b1\u03c6\u03ae", "delete.all": "Delete all", + "dialog.fields.empty": "This dialog has no fields", "dialog.files.empty": "No files to select", "dialog.pages.empty": "No pages to select", + "dialog.text.empty": "This dialog does not define any text", "dialog.users.empty": "No users to select", "dimensions": "Διαστάσεις", + "disable": "Disable", "disabled": "Disabled", "discard": "Απόρριψη", + + "drawer.fields.empty": "This drawer has no fields", + + "domain": "Domain", "download": "Λήψη", "duplicate": "Αντίγραφο", - "edit": "Edit", + "edit": "\u0395\u03c0\u03b5\u03be\u03b5\u03c1\u03b3\u03b1\u03c3\u03af\u03b1", "email": "Διεύθυνση ηλεκτρονικού ταχυδρομείου", "email.placeholder": "mail@example.com", + "enter": "Enter", "entries": "Entries", "entry": "Entry", "environment": "Environment", + "error": "Error", "error.access.code": "Mη έγκυρος κωδικός", "error.access.login": "Mη έγκυρη σύνδεση", "error.access.panel": "Δεν επιτρέπεται η πρόσβαση στον πίνακα ελέγχου", @@ -62,7 +80,7 @@ "error.avatar.create.fail": "Δεν ήταν δυνατή η μεταφόρτωση της εικόνας προφίλ", "error.avatar.delete.fail": "Δεν ήταν δυνατή η διαγραφή της εικόνας προφίλ", "error.avatar.dimensions.invalid": "Διατηρήστε το πλάτος και το ύψος της εικόνας προφίλ κάτω από 3000 εικονοστοιχεία", - "error.avatar.mime.forbidden": "Μη αποδεκτός τύπος αρχείου", + "error.avatar.mime.forbidden": "\u039c\u03b7 \u03b1\u03c0\u03bf\u03b4\u03b5\u03ba\u03c4\u03cc\u03c2 \u03c4\u03cd\u03c0\u03bf\u03c2 \u03b1\u03c1\u03c7\u03b5\u03af\u03bf\u03c5", "error.blueprint.notFound": "Δεν ήταν δυνατή η φόρτωση του προσχεδίου \"{name}\"", @@ -74,15 +92,33 @@ "error.cache.type.invalid": "Invalid cache type \"{type}\"", + "error.content.lock.delete": "The version is locked and cannot be deleted", + "error.content.lock.move": "The source version is locked and cannot be moved", + "error.content.lock.publish": "This version is already published", + "error.content.lock.replace": "The version is locked and cannot be replaced", + "error.content.lock.update": "The version is locked and cannot be updated", + + "error.entries.max.plural": "You must not add more than {max} entries", + "error.entries.max.singular": "You must not add more than one entry", + "error.entries.min.plural": "You must add at least {min} entries", + "error.entries.min.singular": "You must add at least one entry", + "error.entries.supports": "\"{type}\" field type is not supported for the entries field", + "error.entries.validation": "There's an error on the \"{field}\" field in row {index}", + "error.email.preset.notFound": "Δεν είναι δυνατή η εύρεση της προεπιλογής διεύθινσης ηλεκτρονικού ταχυδρομείου \"{name}\"", "error.field.converter.invalid": "Μη έγκυρος μετατροπέας \"{converter}\"", + "error.field.link.options": "Invalid options: {options}", "error.field.type.missing": "Field \"{ name }\": The field type \"{ type }\" does not exist", "error.file.changeName.empty": "The name must not be empty", "error.file.changeName.permission": "Δεν επιτρέπεται να αλλάξετε το όνομα του \"{filename}\"", + "error.file.changeTemplate.invalid": "The template for the file \"{id}\" cannot be changed to \"{template}\" (valid: \"{blueprints}\")", + "error.file.changeTemplate.permission": "You are not allowed to change the template for the file \"{id}\"", + + "error.file.delete.multiple": "Not all files could be deleted. Try each remaining file individually to see the specific error that prevents deletion.", "error.file.duplicate": "Ένα αρχείο με το όνομα \"{filename}\" υπάρχει ήδη", - "error.file.extension.forbidden": "Μη αποδεκτή επέκταση αρχείου", + "error.file.extension.forbidden": "\u039c\u03b7 \u03b1\u03c0\u03bf\u03b4\u03b5\u03ba\u03c4\u03ae \u03b5\u03c0\u03ad\u03ba\u03c4\u03b1\u03c3\u03b7 \u03b1\u03c1\u03c7\u03b5\u03af\u03bf\u03c5", "error.file.extension.invalid": "Invalid extension: {extension}", "error.file.extension.missing": "Λείπει η επέκταση για το \"{filename}\"", "error.file.maxheight": "The height of the image must not exceed {height} pixels", @@ -95,9 +131,11 @@ "error.file.minheight": "The height of the image must be at least {height} pixels", "error.file.minsize": "The file is too small", "error.file.minwidth": "The width of the image must be at least {width} pixels", + "error.file.name.unique": "The filename must be unique", "error.file.name.missing": "Το όνομα αρχείου δεν μπορεί να είναι άδειο", "error.file.notFound": "Δεν είναι δυνατό να βρεθεί το αρχείο \"{filename}\"", "error.file.orientation": "The orientation of the image must be \"{orientation}\"", + "error.file.sort.permission": "You are not allowed to change the sorting of \"{filename}\"", "error.file.type.forbidden": "Δεν επιτρέπεται η μεταφόρτωση αρχείων {type}", "error.file.type.invalid": "Invalid file type: {type}", "error.file.undefined": "Δεν ήταν δυνατή η εύρεση του αρχείου", @@ -106,22 +144,30 @@ "error.form.notSaved": "Δεν ήταν δυνατή η αποθήκευση της φόρμας", "error.language.code": "Please enter a valid code for the language", + "error.language.create.permission": "You are not allowed to create a language", + "error.language.delete.permission": "You are not allowed to delete the language", "error.language.duplicate": "The language already exists", "error.language.name": "Please enter a valid name for the language", "error.language.notFound": "The language could not be found", + "error.language.update.permission": "You are not allowed to update the language", "error.layout.validation.block": "There's an error on the \"{field}\" field in block {blockIndex} using the \"{fieldset}\" block type in layout {layoutIndex}", "error.layout.validation.settings": "There's an error in layout {index} settings", - "error.license.format": "Please enter a valid license key", + "error.license.domain": "The domain for the license is missing", "error.license.email": "Παρακαλώ εισάγετε μια έγκυρη διεύθυνση ηλεκτρονικού ταχυδρομείου", + "error.license.format": "Please enter a valid license code", "error.license.verification": "The license could not be verified", + "error.login.totp.confirm.invalid": "Mη έγκυρος κωδικός", + "error.login.totp.confirm.missing": "Please enter the current code", + "error.object.validation": "There’s an error in the \"{label}\" field:\n{message}", "error.offline": "The Panel is currently offline", "error.page.changeSlug.permission": "Δεν επιτρέπεται να αλλάξετε το URL της σελίδας \"{slug}\"", + "error.page.changeSlug.reserved": "The path of top-level pages must not start with \"{path}\"", "error.page.changeStatus.incomplete": "Δεν ήταν δυνατή η δημοσίευση της σελίδας καθώς περιέχει σφάλματα", "error.page.changeStatus.permission": "Δεν είναι δυνατή η αλλαγή κατάστασης για αυτή τη σελίδα", "error.page.changeStatus.toDraft.invalid": "Δεν είναι δυνατή η μετατροπή της σελίδας \"{slug}\" σε προσχέδιο", @@ -133,10 +179,18 @@ "error.page.delete": "Δεν είναι δυνατή η διαγραφή της σελίδας \"{slug}\"", "error.page.delete.confirm": "Παρακαλώ εισάγετε τον τίτλο της σελίδας για επιβεβαίωση", "error.page.delete.hasChildren": "Δεν είναι δυνατή η διαγραφή της σελίδας καθώς περιέχει υποσελίδες", + "error.page.delete.multiple": "Not all pages could be deleted. Try each remaining page individually to see the specific error that prevents deletion.", "error.page.delete.permission": "Δεν επιτρέπεται η διαγραφή της σελίδας \"{slug}\"", "error.page.draft.duplicate": "Υπάρχει ήδη ένα προσχέδιο σελίδας με την διεύθυνση URL \"{slug}\"", "error.page.duplicate": "Υπάρχει ήδη μια σελίδα με την διεύθυνση URL \"{slug}\"", "error.page.duplicate.permission": "You are not allowed to duplicate \"{slug}\"", + "error.page.move.ancestor": "The page cannot be moved into itself", + "error.page.move.directory": "The page directory cannot be moved", + "error.page.move.duplicate": "A sub page with the URL appendix \"{slug}\" already exists", + "error.page.move.noSections": "The page \"{parent}\" cannot be a parent of any page because it lacks any pages sections in its blueprint", + "error.page.move.notFound": "The moved page could not be found", + "error.page.move.permission": "You are not allowed to move \"{slug}\"", + "error.page.move.template": "The \"{template}\" template is not accepted as a subpage of \"{parent}\"", "error.page.notFound": "Δεν ήταν δυνατή η εύρεση της σελίδας \"{slug}\"", "error.page.num.invalid": "Παρακαλώ εισάγετε έναν έγκυρο αριθμό ταξινόμησης. Οι αριθμοί δεν μπορεί να είναι αρνητικοί.", "error.page.slug.invalid": "Please enter a valid URL appendix", @@ -163,6 +217,8 @@ "error.site.changeTitle.permission": "Δεν επιτρέπεται να αλλάξετε τον τίτλο του ιστότοπου", "error.site.update.permission": "Δεν επιτρέπεται η ενημέρωση του ιστότοπου", + "error.structure.validation": "There's an error on the \"{field}\" field in row {index}", + "error.template.default.notFound": "Το προεπιλεγμένο πρότυπο δεν υπάρχει", "error.unexpected": "An unexpected error occurred! Enable debug mode for more info: https://getkirby.com/docs/reference/system/options/debug", @@ -175,7 +231,7 @@ "error.user.changeRole.permission": "Δεν επιτρέπεται να αλλάξετε το ρόλο του χρήστη \"{name}\"", "error.user.changeRole.toAdmin": "You are not allowed to promote someone to the admin role", "error.user.create.permission": "Δεν επιτρέπεται η δημιουργία αυτού του χρήστη", - "error.user.delete": "Ο χρήστης δεν μπορούσε να διαγραφεί", + "error.user.delete": "\u039f \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7\u03c2 \u03b4\u03b5\u03bd \u03bc\u03c0\u03bf\u03c1\u03bf\u03cd\u03c3\u03b5 \u03bd\u03b1 \u03b4\u03b9\u03b1\u03b3\u03c1\u03b1\u03c6\u03b5\u03af", "error.user.delete.lastAdmin": "Δεν είναι δυνατή η διαγραφή του τελευταίου διαχειριστή", "error.user.delete.lastUser": "Δεν είναι δυνατή η διαγραφή του τελευταίου χρήστη", "error.user.delete.permission": "Δεν επιτρέπεται να διαγράψετ τον χρήστη \"{name}\"", @@ -185,7 +241,7 @@ "error.user.notFound": "Δεν είναι δυνατή η εύρεση του χρήστη \"{name}\"", "error.user.password.excessive": "Please enter a valid password. Passwords must not be longer than 1000 characters.", "error.user.password.invalid": "Παρακαλώ εισάγετε έναν έγκυρο κωδικό πρόσβασης. Οι κωδικοί πρόσβασης πρέπει να έχουν μήκος τουλάχιστον 8 χαρακτήρων.", - "error.user.password.notSame": "Παρακαλούμε επιβεβαιώστε τον Κωδικό Πρόσβασης", + "error.user.password.notSame": "\u03a0\u03b1\u03c1\u03b1\u03ba\u03b1\u03bb\u03bf\u03cd\u03bc\u03b5 \u03b5\u03c0\u03b9\u03b2\u03b5\u03b2\u03b1\u03b9\u03ce\u03c3\u03c4\u03b5 \u03c4\u03bf\u03bd \u039a\u03c9\u03b4\u03b9\u03ba\u03cc \u03a0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2", "error.user.password.undefined": "Ο χρήστης δεν έχει κωδικό πρόσβασης", "error.user.password.wrong": "Wrong password", "error.user.role.invalid": "Παρακαλώ εισαγάγετε έναν έγκυρο ρόλο", @@ -195,8 +251,10 @@ "error.validation.accepted": "Παρακαλώ επιβεβαιώστε", "error.validation.alpha": "Παρακαλώ εισάγετε μόνο χαρακτήρες μεταξύ των a-z", "error.validation.alphanum": "Παρακαλώ εισάγετε μόνο χαρακτήρες μεταξύ των a-z ή αριθμούς απο το 0 έως το 9", + "error.validation.anchor": "Please enter a correct link anchor", "error.validation.between": "Παρακαλώ εισάγετε μια τιμή μεταξύ \"{min}\" και \"{max}\"", "error.validation.boolean": "Παρακαλώ επιβεβαιώστε ή αρνηθείτε", + "error.validation.color": "Please enter a valid color in the {format} format", "error.validation.contains": "Παρακαλώ καταχωρίστε μια τιμή που περιέχει \"{needle}\"", "error.validation.date": "Παρακαλώ εισάγετε μία έγκυρη ημερομηνία", "error.validation.date.after": "Please enter a date after {date}", @@ -211,6 +269,7 @@ "error.validation.integer": "Παρακαλώ εισάγετε έναν έγκυρο ακέραιο αριθμό", "error.validation.ip": "Παρακαλώ εισάγετε μια έγκυρη διεύθυνση IP", "error.validation.less": "Παρακαλώ εισάγετε μια τιμή μικρότερη από {max}", + "error.validation.linkType": "The link type is not allowed", "error.validation.match": "Η τιμή δεν ταιριάζει με το αναμενόμενο πρότυπο", "error.validation.max": "Παρακαλώ εισάγετε μια τιμή ίση ή μικρότερη από {max}", "error.validation.maxlength": "Παρακαλώ εισάγετε μια μικρότερη τιμή. (max. {max} χαρακτήρες)", @@ -227,15 +286,18 @@ "error.validation.same": "Παρακαλώ εισάγετε \"{other}\"", "error.validation.size": "Το μέγεθος της τιμής πρέπει να είναι \"{size}\"", "error.validation.startswith": "Η τιμή πρέπει να αρχίζει με \"{start}\"", + "error.validation.tel": "Please enter an unformatted phone number", "error.validation.time": "Παρακαλώ εισάγετε μια έγκυρη ώρα", "error.validation.time.after": "Please enter a time after {time}", "error.validation.time.before": "Please enter a time before {time}", "error.validation.time.between": "Please enter a time between {min} and {max}", + "error.validation.uuid": "Please enter a valid UUID", "error.validation.url": "Παρακαλώ εισάγετε μια έγκυρη διεύθυνση URL", "expand": "Expand", "expand.all": "Expand All", + "field.invalid": "The field is invalid", "field.required": "The field is required", "field.blocks.changeType": "Change type", "field.blocks.code.name": "Κώδικας", @@ -245,8 +307,9 @@ "field.blocks.delete.confirm.all": "Do you really want to delete all blocks?", "field.blocks.delete.confirm.selected": "Do you really want to delete the selected blocks?", "field.blocks.empty": "No blocks yet", + "field.blocks.fieldsets.empty": "No fieldsets yet", "field.blocks.fieldsets.label": "Please select a block type …", - "field.blocks.fieldsets.paste": "Press {{ shortcut }} to paste/import blocks from your clipboard", + "field.blocks.fieldsets.paste": "Press {{ shortcut }} to import layouts/blocks from your clipboard Only those allowed in the current field will get inserted.", "field.blocks.gallery.name": "Gallery", "field.blocks.gallery.images.empty": "No images yet", "field.blocks.gallery.images.label": "Images", @@ -254,11 +317,16 @@ "field.blocks.heading.name": "Heading", "field.blocks.heading.text": "Text", "field.blocks.heading.placeholder": "Heading …", + "field.blocks.figure.back.plain": "Plain", + "field.blocks.figure.back.pattern.light": "Pattern (light)", + "field.blocks.figure.back.pattern.dark": "Pattern (dark)", "field.blocks.image.alt": "Alternative text", "field.blocks.image.caption": "Caption", "field.blocks.image.crop": "Crop", "field.blocks.image.link": "Σύνδεσμος", "field.blocks.image.location": "Location", + "field.blocks.image.location.internal": "This website", + "field.blocks.image.location.external": "External source", "field.blocks.image.name": "Εικόνα", "field.blocks.image.placeholder": "Select an image", "field.blocks.image.ratio": "Ratio", @@ -275,63 +343,97 @@ "field.blocks.quote.citation.placeholder": "by …", "field.blocks.text.name": "Text", "field.blocks.text.placeholder": "Text …", + "field.blocks.video.autoplay": "Autoplay", "field.blocks.video.caption": "Caption", + "field.blocks.video.controls": "Controls", + "field.blocks.video.location": "Location", + "field.blocks.video.loop": "Loop", + "field.blocks.video.muted": "Muted", "field.blocks.video.name": "Video", "field.blocks.video.placeholder": "Enter a video URL", + "field.blocks.video.poster": "Poster", + "field.blocks.video.preload": "Preload", "field.blocks.video.url.label": "Video-URL", "field.blocks.video.url.placeholder": "https://youtube.com/?v=", - "field.files.empty": "Δεν έχουν επιλεγεί αρχεία ακόμα", + "field.entries.delete.confirm.all": "Do you really want to delete all entries?", + "field.entries.empty": "Δεν υπάρχουν ακόμη καταχωρίσεις.", + "field.files.empty": "Δεν έχουν επιλεγεί αρχεία ακόμα", + "field.files.empty.single": "No file selected yet", + + "field.layout.change": "Change layout", "field.layout.delete": "Delete layout", "field.layout.delete.confirm": "Do you really want to delete this layout?", + "field.layout.delete.confirm.all": "Do you really want to delete all layouts?", "field.layout.empty": "No rows yet", "field.layout.select": "Select a layout", "field.object.empty": "No information yet", "field.pages.empty": "Δεν έχουν επιλεγεί ακόμη σελίδες", + "field.pages.empty.single": "No page selected yet", - "field.structure.delete.confirm": "Είστε σίγουρος ότι θέλετε να διαγράψετε αυτήν την καταχώριση;", + "field.structure.delete.confirm": "\u0395\u03af\u03c3\u03c4\u03b5 \u03c3\u03af\u03b3\u03bf\u03c5\u03c1\u03bf\u03c2 \u03cc\u03c4\u03b9 \u03b8\u03ad\u03bb\u03b5\u03c4\u03b5 \u03bd\u03b1 \u03b4\u03b9\u03b1\u03b3\u03c1\u03ac\u03c8\u03b5\u03c4\u03b5 \u03b1\u03c5\u03c4\u03ae\u03bd \u03c4\u03b7\u03bd \u03ba\u03b1\u03c4\u03b1\u03c7\u03ce\u03c1\u03b9\u03c3\u03b7;", "field.structure.delete.confirm.all": "Do you really want to delete all entries?", - "field.structure.empty": "Δεν υπάρχουν ακόμη καταχωρίσεις.", + "field.structure.empty": "\u0394\u03b5\u03bd \u03c5\u03c0\u03ac\u03c1\u03c7\u03bf\u03c5\u03bd \u03b1\u03ba\u03cc\u03bc\u03b7 \u03ba\u03b1\u03c4\u03b1\u03c7\u03c9\u03c1\u03af\u03c3\u03b5\u03b9\u03c2.", "field.users.empty": "Δεν έχουν επιλεγεί ακόμη χρήστες", + "field.users.empty.single": "No user selected yet", + "fields.empty": "No fields yet", + + "file": "Αρχείο", "file.blueprint": "This file has no blueprint yet. You can define the setup in /site/blueprints/files/{blueprint}.yml", - "file.delete.confirm": "Θέλετε σίγουρα να διαγράψετε αυτό το αρχείο;", + "file.changeTemplate": "Αλλαγή προτύπου", + "file.changeTemplate.notice": "Changing the file's template will remove content for fields that don't match in type. If the new template defines certain rules, e.g. image dimensions, those will also be applied irreversibly. Use with caution.", + "file.delete.confirm": "\u0398\u03ad\u03bb\u03b5\u03c4\u03b5 \u03c3\u03af\u03b3\u03bf\u03c5\u03c1\u03b1 \u03bd\u03b1 \u03b4\u03b9\u03b1\u03b3\u03c1\u03ac\u03c8\u03b5\u03c4\u03b5 \u03b1\u03c5\u03c4\u03cc \u03c4\u03bf \u03b1\u03c1\u03c7\u03b5\u03af\u03bf;", + "file.focus.placeholder": "Set focal point", + "file.focus.reset": "Remove focal point", + "file.focus.title": "Focus", "file.sort": "Change position", - "files": "Files", + "files": "Αρχεία", + "files.delete.confirm.selected": "Do you really want to delete the selected files? This action cannot be undone.", "files.empty": "Δεν υπάρχουν ακόμα αρχεία", + "filter": "Filter", + + "form.discard": "Discard changes", + "form.discard.confirm": "Do you really want to discard all your changes?", + "form.locked": "This content is disabled for you as it is currently edited by another user", + "form.unsaved": "The current changes have not yet been saved", + "form.preview": "Preview changes", + "form.preview.draft": "Preview draft", + "hide": "Hide", "hour": "Ώρα", + "hue": "Hue", "import": "Import", "info": "Info", - "insert": "Insert", + "insert": "\u0395\u03b9\u03c3\u03b1\u03b3\u03c9\u03b3\u03ae", "insert.after": "Insert after", "insert.before": "Insert before", "install": "Εγκατάσταση", - "installation": "Installation", + "installation": "Εγκατάσταση", "installation.completed": "Ο πίνακας ελέγχου έχει εγκατασταθεί", "installation.disabled": "Η εγκατάσταση του πίνακα ελέγχου είναι απενεργοποιημένη για δημόσιους διακομιστές από προεπιλογή. Εκτελέστε την εγκατάσταση σε ένα τοπικό μηχάνημα ή ενεργοποιήστε την με την επιλογή panel.install.", - "installation.issues.accounts": "Ο φάκελος /site/accounts δεν είναι εγγράψιμος", - "installation.issues.content": "Ο φάκελος content και όλοι οι υποφάκελοι πρέπει να είναι εγγράψιμοι.", + "installation.issues.accounts": "\u039f \u03c6\u03ac\u03ba\u03b5\u03bb\u03bf\u03c2 \/site\/accounts \u03b4\u03b5\u03bd \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03b3\u03b3\u03c1\u03ac\u03c8\u03b9\u03bc\u03bf\u03c2", + "installation.issues.content": "\u039f \u03c6\u03ac\u03ba\u03b5\u03bb\u03bf\u03c2 content \u03ba\u03b1\u03b9 \u03cc\u03bb\u03bf\u03b9 \u03bf\u03b9 \u03c5\u03c0\u03bf\u03c6\u03ac\u03ba\u03b5\u03bb\u03bf\u03b9 \u03c0\u03c1\u03ad\u03c0\u03b5\u03b9 \u03bd\u03b1 \u03b5\u03af\u03bd\u03b1\u03b9 \u03b5\u03b3\u03b3\u03c1\u03ac\u03c8\u03b9\u03bc\u03bf\u03b9.", "installation.issues.curl": "Απαιτείται η επέκταση CURL", "installation.issues.headline": "Ο πίνακας ελέγχου δεν μπορεί να εγκατασταθεί", "installation.issues.mbstring": "Απαιτείται η επέκταση MB String ", "installation.issues.media": "Ο φάκελος /media δεν υπάρχει ή δεν είναι εγγράψιμος", "installation.issues.php": "Βεβαιωθείτε ότι χρησιμοποιήτε PHP 8+", - "installation.issues.server": "To Kirby απαιτεί Apache, Nginx ή Caddy", "installation.issues.sessions": "Ο φάκελος /site/sessions δεν υπάρχει ή δεν είναι εγγράψιμος", - "language": "Γλώσσα", + "language": "\u0393\u03bb\u03ce\u03c3\u03c3\u03b1", "language.code": "Κώδικας", "language.convert": "Χρήση ως προεπιλογή", "language.convert.confirm": "

Θέλετε πραγματικά να μετατρέψετε τη {name} στην προεπιλεγμένη γλώσσα; Αυτό δεν μπορεί να ανακληθεί.

Αν το {name} χει μη μεταφρασμένο περιεχόμενο, δεν θα υπάρχει πλέον έγκυρη εναλλακτική λύση και τμήματα του ιστότοπού σας ενδέχεται να είναι κενά.

", "language.create": "Προσθέστε μια νέα γλώσσα", + "language.default": "Προεπιλεγμένη γλώσσα", "language.delete.confirm": "Θέλετε πραγματικά να διαγράψετε τη γλώσσα {name} συμπεριλαμβανομένων όλων των μεταφράσεων; Αυτό δεν μπορεί να αναιρεθεί!", "language.deleted": "Η γλώσσα έχει διαγραφεί", "language.direction": "Κατεύθυνση ανάγνωσης", @@ -340,7 +442,16 @@ "language.locale": "Συμβολοσειρά τοπικής γλώσσας PHP", "language.locale.warning": "You are using a custom locale set up. Please modify it in the language file in /site/languages", "language.name": "Ονομασία", + "language.secondary": "Secondary language", + "language.settings": "Language settings", "language.updated": "Η γλώσσα έχει ενημερωθεί", + "language.variables": "Language variables", + "language.variables.empty": "No translations yet", + + "language.variable.delete.confirm": "Do you really want to delete the variable for {key}?", + "language.variable.key": "Key", + "language.variable.notFound": "The variable could not be found", + "language.variable.value": "Value", "languages": "Γλώσσες", "languages.default": "Προεπιλεγμένη γλώσσα", @@ -348,36 +459,57 @@ "languages.secondary": "Δευτερεύουσες γλώσσες", "languages.secondary.empty": "Δεν υπάρχουν ακόμα δευτερεύουσες γλώσσες", - "license": "Άδεια Χρήσης του Kirby", + "license": "\u0386\u03b4\u03b5\u03b9\u03b1 \u03a7\u03c1\u03ae\u03c3\u03b7\u03c2 \u03c4\u03bf\u03c5 Kirby", + "license.activate": "Activate it now", + "license.activate.label": "Please activate your license", + "license.activate.domain": "Your license will be activated for {host}.", + "license.activate.local": "You are about to activate your Kirby license for your local domain {host}. If this site will be deployed to a public domain, please activate it there instead. If {host} is the domain you want to use your license for, please continue.", + "license.activated": "Activated", "license.buy": "Αγοράστε μια άδεια", - "license.register": "Εγγραφή", + "license.code": "Κώδικας", + "license.code.help": "You received your license code after the purchase via email. Please copy and paste it here.", + "license.code.label": "Παρακαλώ εισαγάγετε τον κωδικό άδειας χρήσης", + "license.status.active.info": "Includes new major versions until {date}", + "license.status.active.label": "Valid license", + "license.status.demo.info": "This is a demo installation", + "license.status.demo.label": "Demo", + "license.status.inactive.info": "Renew license to update to new major versions", + "license.status.inactive.label": "No new major versions", + "license.status.legacy.bubble": "Ready to renew your license?", + "license.status.legacy.info": "Your license does not cover this version", + "license.status.legacy.label": "Please renew your license", + "license.status.missing.bubble": "Ready to launch your site?", + "license.status.missing.info": "No valid license", + "license.status.missing.label": "Please activate your license", + "license.status.unknown.info": "The license status is unknown", + "license.status.unknown.label": "Unknown", "license.manage": "Manage your licenses", - "license.register.help": "Έχετε λάβει τον κωδικό άδειας χρήσης μετά την αγορά μέσω ηλεκτρονικού ταχυδρομείου. Παρακαλώ αντιγράψτε και επικολλήστε τον για να εγγραφείτε.", - "license.register.label": "Παρακαλώ εισαγάγετε τον κωδικό άδειας χρήσης", - "license.register.domain": "Your license will be registered to {host}.", - "license.register.local": "You are about to register your license for your local domain {host}. If this site will be deployed to a public domain, please register it there instead. If {host} is the domain you want to license Kirby to, please continue.", - "license.register.success": "Σας ευχαριστούμε για την υποστήριξη του Kirby", - "license.unregistered": "Αυτό είναι ένα μη καταχωρημένο demo του Kirby", + "license.purchased": "Purchased", + "license.success": "Σας ευχαριστούμε για την υποστήριξη του Kirby", "license.unregistered.label": "Unregistered", - "link": "Σύνδεσμος", - "link.text": "Κείμενο συνδέσμου", + "link": "\u03a3\u03cd\u03bd\u03b4\u03b5\u03c3\u03bc\u03bf\u03c2", + "link.text": "\u039a\u03b5\u03af\u03bc\u03b5\u03bd\u03bf \u03c3\u03c5\u03bd\u03b4\u03ad\u03c3\u03bc\u03bf\u03c5", "loading": "Φόρτωση", "lock.unsaved": "Unsaved changes", - "lock.unsaved.empty": "There are no more unsaved changes", - "lock.isLocked": "Unsaved changes by {email}", - "lock.file.isLocked": "The file is currently being edited by {email} and cannot be changed.", - "lock.page.isLocked": "The page is currently being edited by {email} and cannot be changed.", + "lock.unsaved.empty": "There are no unsaved changes", + "lock.unsaved.files": "Unsaved files", + "lock.unsaved.pages": "Unsaved pages", + "lock.unsaved.users": "Unsaved accounts", + "lock.isLocked": "Unsaved changes by {email}", "lock.unlock": "Unlock", - "lock.isUnlocked": "Your unsaved changes have been overwritten by another user. You can download your changes to merge them manually.", + "lock.unlock.submit": "Unlock and overwrite unsaved changes by {email}", + "lock.isUnlocked": "Was unlocked by another user", - "login": "Log in", + "login": "Σύνδεση", "login.code.label.login": "Login code", "login.code.label.password-reset": "Password reset code", "login.code.placeholder.email": "000 000", + "login.code.placeholder.totp": "000000", "login.code.text.email": "If your email address is registered, the requested code was sent via email.", + "login.code.text.totp": "Please enter the one‑time code from your authenticator app.", "login.email.login.body": "Hi {user.nameOrEmail},\n\nYou recently requested a login code for the Panel of {site}.\nThe following login code will be valid for {timeout} minutes:\n\n{code}\n\nIf you did not request a login code, please ignore this email or contact your administrator if you have questions.\nFor security, please DO NOT forward this email.", "login.email.login.subject": "Your login code", "login.email.password-reset.body": "Hi {user.nameOrEmail},\n\nYou recently requested a password reset code for the Panel of {site}.\nThe following password reset code will be valid for {timeout} minutes:\n\n{code}\n\nIf you did not request a password reset code, please ignore this email or contact your administrator if you have questions.\nFor security, please DO NOT forward this email.", @@ -388,58 +520,80 @@ "login.toggleText.code.email-password": "Login with password", "login.toggleText.password-reset.email": "Forgot your password?", "login.toggleText.password-reset.email-password": "← Back to login", + "login.totp.enable.option": "Set up one‑time codes", + "login.totp.enable.intro": "Authenticator apps can generate one‑time codes that are used as a second factor when signing into your account.", + "login.totp.enable.qr.label": "1. Scan this QR code", + "login.totp.enable.qr.help": "Unable to scan? Add the setup key {secret} manually to your authenticator app.", + "login.totp.enable.confirm.headline": "2. Confirm with generated code", + "login.totp.enable.confirm.text": "Your app generates a new one‑time code every 30 seconds. Enter the current code to complete the setup:", + "login.totp.enable.confirm.label": "Current code", + "login.totp.enable.confirm.help": "After this setup, we will ask you for a one‑time code every time you log in.", + "login.totp.enable.success": "One‑time codes enabled", + "login.totp.disable.option": "Disable one‑time codes", + "login.totp.disable.label": "Enter your password to disable one‑time codes", + "login.totp.disable.help": "In the future, a different second factor like a login code sent via email will be requested when you log in. You can always set up one‑time codes again later.", + "login.totp.disable.admin": "

This will disable one‑time codes for {user}.

In the future, a different second factor like a login code sent via email will be requested when they log in. {user} can set up one‑time codes again after their next login.

", + "login.totp.disable.success": "One‑time codes disabled", - "logout": "Log out", + "logout": "Αποσύνδεση", + "merge": "Merge", "menu": "Μενού", "meridiem": "Π.Μ./Μ.Μ", "mime": "Τύπος πολυμέσων", "minutes": "Λεπτά", "month": "Μήνας", - "months.april": "Απρίλιος", - "months.august": "August", - "months.december": "December", + "months.april": "\u0391\u03c0\u03c1\u03af\u03bb\u03b9\u03bf\u03c2", + "months.august": "\u0391\u03cd\u03b3\u03bf\u03c5\u03c3\u03c4\u03bf\u03c2", + "months.december": "\u0394\u03b5\u03ba\u03ad\u03bc\u03b2\u03c1\u03b9\u03bf\u03c2", "months.february": "Φεβρουάριος", - "months.january": "January", - "months.july": "Ιούλιος", - "months.june": "June", - "months.march": "March", - "months.may": "May", - "months.november": "November", - "months.october": "October", - "months.september": "September", + "months.january": "\u0399\u03b1\u03bd\u03bf\u03c5\u03ac\u03c1\u03b9\u03bf\u03c2", + "months.july": "\u0399\u03bf\u03cd\u03bb\u03b9\u03bf\u03c2", + "months.june": "\u0399\u03bf\u03cd\u03bd\u03b9\u03bf\u03c2", + "months.march": "\u039c\u03ac\u03c1\u03c4\u03b9\u03bf\u03c2", + "months.may": "\u039c\u03ac\u03b9\u03bf\u03c2", + "months.november": "\u039d\u03bf\u03ad\u03bc\u03b2\u03c1\u03b9\u03bf\u03c2", + "months.october": "\u039f\u03ba\u03c4\u03ce\u03b2\u03c1\u03b9\u03bf\u03c2", + "months.september": "\u03a3\u03b5\u03c0\u03c4\u03ad\u03bc\u03b2\u03c1\u03b9\u03bf\u03c2", "more": "Περισσότερα", + "move": "Move", "name": "Ονομασία", "next": "Επόμενο", + "night": "Night", "no": "no", "off": "off", "on": "on", "open": "Άνοιγμα", "open.newWindow": "Open in new window", + "option": "Option", "options": "Eπιλογές", "options.none": "No options", + "options.all": "Show all {count} options", "orientation": "Προσανατολισμός", "orientation.landscape": "Οριζόντιος", "orientation.portrait": "Κάθετος", "orientation.square": "Τετράγωνος", + "page": "Page", "page.blueprint": "This page has no blueprint yet. You can define the setup in /site/blueprints/pages/{blueprint}.yml", - "page.changeSlug": "Change URL", - "page.changeSlug.fromTitle": "Create from title", + "page.changeSlug": "\u0391\u03bb\u03bb\u03b1\u03b3\u03ae URL", + "page.changeSlug.fromTitle": "\u0394\u03b7\u03bc\u03b9\u03bf\u03c5\u03c1\u03b3\u03af\u03b1 \u03b1\u03c0\u03cc \u03c4\u03bf\u03bd \u03c4\u03af\u03c4\u03bb\u03bf", "page.changeStatus": "Αλλαγή κατάστασης", "page.changeStatus.position": "Επιλέξτε μια θέση", "page.changeStatus.select": "Επιλέξτε μια νέα κατάσταση", "page.changeTemplate": "Αλλαγή προτύπου", - "page.delete.confirm": "Θέλετε σίγουρα να διαγράψετε αυτήν τη σελίδα;", + "page.changeTemplate.notice": "Changing the page's template will remove content for fields that don't match in type. Use with caution.", + "page.create": "Create as {status}", + "page.delete.confirm": "\u0398\u03ad\u03bb\u03b5\u03c4\u03b5 \u03c3\u03af\u03b3\u03bf\u03c5\u03c1\u03b1 \u03bd\u03b1 \u03b4\u03b9\u03b1\u03b3\u03c1\u03ac\u03c8\u03b5\u03c4\u03b5 \u03b1\u03c5\u03c4\u03ae\u03bd \u03c4\u03b7 \u03c3\u03b5\u03bb\u03af\u03b4\u03b1;", "page.delete.confirm.subpages": "Αυτή η σελίδα έχει υποσελίδες.
Όλες οι υποσελίδες θα διαγραφούν επίσης.", "page.delete.confirm.title": "Εισάγετε τον τίτλο της σελίδας για επιβεβαίωση", - "page.draft.create": "Δημιουργία προσχεδίου", "page.duplicate.appendix": "Αντιγραφή", "page.duplicate.files": "Copy files", "page.duplicate.pages": "Copy pages", + "page.move": "Move page", "page.sort": "Change position", "page.status": "Kατάσταση", "page.status.draft": "Προσχέδιο", @@ -449,30 +603,38 @@ "page.status.unlisted": "Μη καταχωρημένο", "page.status.unlisted.description": "Η σελίδα είναι προσβάσιμη μόνο μέσω της διεύθυνσης URL", - "pages": "Pages", + "pages": "Σελίδες", + "pages.delete.confirm.selected": "Do you really want to delete the selected pages? This action cannot be undone.", "pages.empty": "Δεν υπάρχουν ακόμα σελίδες", "pages.status.draft": "Προσχέδια", "pages.status.listed": "Δημοσιευμένο", "pages.status.unlisted": "Μη καταχωρημένο", - "pagination.page": "Page", + "pagination.page": "Σελίδα", - "password": "Password", + "password": "\u039a\u03c9\u03b4\u03b9\u03ba\u03cc\u03c2 \u03a0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2", "paste": "Paste", "paste.after": "Paste after", + "paste.success": "{count} pasted!", "pixel": "Εικονοστοιχέιο", "plugin": "Plugin", "plugins": "Plugins", "prev": "Προηγούμενο", "preview": "Preview", + + "publish": "Publish", + "published": "Δημοσιευμένο", + "remove": "Αφαίρεση", "rename": "Μετονομασία", - "replace": "Replace", - "retry": "Επανάληψη", - "revert": "Αγνόηση", + "renew": "Renew", + "replace": "\u0391\u03bd\u03c4\u03b9\u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7", + "replace.with": "Replace with", + "retry": "\u0395\u03c0\u03b1\u03bd\u03ac\u03bb\u03b7\u03c8\u03b7", + "revert": "\u0391\u03b3\u03bd\u03cc\u03b7\u03c3\u03b7", "revert.confirm": "Do you really want to delete all unsaved changes?", - "role": "Role", + "role": "\u03a1\u03cc\u03bb\u03bf\u03c2", "role.admin.description": "The admin has all rights", "role.admin.title": "Admin", "role.all": "Όλα", @@ -481,12 +643,15 @@ "role.nobody.description": "This is a fallback role without any permissions", "role.nobody.title": "Nobody", - "save": "Save", + "save": "\u0391\u03c0\u03bf\u03b8\u03ae\u03ba\u03b5\u03c5\u03c3\u03b7", + "saved": "Saved", "search": "Αναζήτηση", + "searching": "Searching", "search.min": "Enter {min} characters to search", - "search.all": "Show all", + "search.all": "Show all {count} results", "search.results.none": "No results", + "section.invalid": "The section is invalid", "section.required": "The section is required", "security": "Security", @@ -496,18 +661,27 @@ "show": "Show", "site.blueprint": "The site has no blueprint yet. You can define the setup in /site/blueprints/site.yml", "size": "Μέγεθος", - "slug": "Επίθεμα URL", + "slug": "\u0395\u03c0\u03af\u03b8\u03b5\u03bc\u03b1 URL", "sort": "Ταξινόμηση", + "sort.drag": "Drag to sort …", + "split": "Split", "stats.empty": "No reports", + "status": "Kατάσταση", + + "system.info.copy": "Copy info", + "system.info.copied": "System info copied", "system.issues.content": "The content folder seems to be exposed", "system.issues.eol.kirby": "Your installed Kirby version has reached end-of-life and will not receive further security updates", "system.issues.eol.plugin": "Your installed version of the { plugin } plugin is has reached end-of-life and will not receive further security updates", + "system.issues.eol.php": "Your installed PHP release { release } has reached end-of-life and will not receive further security updates", "system.issues.debug": "Debugging must be turned off in production", "system.issues.git": "The .git folder seems to be exposed", "system.issues.https": "We recommend HTTPS for all your sites", "system.issues.kirby": "The kirby folder seems to be exposed", + "system.issues.local": "The site is running locally with relaxed security checks", "system.issues.site": "The site folder seems to be exposed", + "system.issues.vue.compiler": "The Vue template compiler is enabled", "system.issues.vulnerability.kirby": "Your installation might be affected by the following vulnerability ({ severity } severity): { description }", "system.issues.vulnerability.plugin": "Your installation might be affected by the following vulnerability in the { plugin } plugin ({ severity } severity): { description }", "system.updateStatus": "Update status", @@ -520,13 +694,22 @@ "system.updateStatus.update": "Free update { version } available", "system.updateStatus.upgrade": "Upgrade { version } available", - "title": "Title", - "template": "Template", + "tel": "Phone", + "tel.placeholder": "+49123456789", + "template": "\u03a0\u03c1\u03cc\u03c4\u03c5\u03c0\u03bf", + + "theme": "Theme", + "theme.light": "Lights on", + "theme.dark": "Lights off", + "theme.automatic": "Match system default", + + "title": "Τίτλος", "today": "Σήμερα", + "toolbar.button.clear": "Clear formatting", "toolbar.button.code": "Κώδικας", - "toolbar.button.bold": "Έντονη γραφή", - "toolbar.button.email": "Διεύθυνση ηλεκτρονικού ταχυδρομείου", + "toolbar.button.bold": "\u0388\u03bd\u03c4\u03bf\u03bd\u03b7 \u03b3\u03c1\u03b1\u03c6\u03ae", + "toolbar.button.email": "Email", "toolbar.button.headings": "Επικεφαλίδες", "toolbar.button.heading.1": "Επικεφαλίδα 1", "toolbar.button.heading.2": "Επικεφαλίδα 2", @@ -534,22 +717,26 @@ "toolbar.button.heading.4": "Heading 4", "toolbar.button.heading.5": "Heading 5", "toolbar.button.heading.6": "Heading 6", - "toolbar.button.italic": "Πλάγια γραφή", + "toolbar.button.italic": "\u03a0\u03bb\u03ac\u03b3\u03b9\u03b1 \u03b3\u03c1\u03b1\u03c6\u03ae", "toolbar.button.file": "Αρχείο", "toolbar.button.file.select": "Select a file", "toolbar.button.file.upload": "Upload a file", - "toolbar.button.link": "Σύνδεσμος", + "toolbar.button.link": "\u03a3\u03cd\u03bd\u03b4\u03b5\u03c3\u03bc\u03bf\u03c2", "toolbar.button.paragraph": "Paragraph", "toolbar.button.strike": "Strike-through", + "toolbar.button.sub": "Subscript", + "toolbar.button.sup": "Superscript", "toolbar.button.ol": "Ταξινομημένη λίστα", "toolbar.button.underline": "Underline", "toolbar.button.ul": "Λίστα κουκκίδων", "translation.author": "Ομάδα Kirby", "translation.direction": "ltr", - "translation.name": "Ελληνικά", + "translation.name": "\u0395\u03bb\u03bb\u03b7\u03bd\u03b9\u03ba\u03ac", "translation.locale": "el_GR", + "type": "Type", + "upload": "Μεταφόρτωση", "upload.error.cantMove": "The uploaded file could not be moved", "upload.error.cantWrite": "Failed to write file to disk", @@ -562,7 +749,7 @@ "upload.error.noFiles": "No files were uploaded", "upload.error.partial": "The uploaded file was only partially uploaded", "upload.error.tmpDir": "Missing a temporary folder", - "upload.errors": "Error", + "upload.errors": "Σφάλμα", "upload.progress": "Μεταφόρτωση...", "url": "Διεύθινση url", @@ -574,28 +761,32 @@ "user.changeLanguage": "Αλλαγή γλώσσας", "user.changeName": "Μετονομασία χρήστη", "user.changePassword": "Αλλαγή κωδικού πρόσβασης", - "user.changePassword.new": "New password", + "user.changePassword.current": "Your current password", + "user.changePassword.new": "Νέος Κωδικός Πρόσβασης", "user.changePassword.new.confirm": "Επαληθεύση κωδικού πρόσβασης", "user.changeRole": "Αλλαγή ρόλου", "user.changeRole.select": "Επιλογή νέου ρόλου", - "user.create": "Add a new user", + "user.create": "Προσθήκη νέου χρήστη", "user.delete": "Διαγραφή χρήστη", - "user.delete.confirm": "Θέλετε σίγουρα να διαγράψετε αυτόν τον χρήστη;", + "user.delete.confirm": "\u0398\u03ad\u03bb\u03b5\u03c4\u03b5 \u03c3\u03af\u03b3\u03bf\u03c5\u03c1\u03b1 \u03bd\u03b1 \u03b4\u03b9\u03b1\u03b3\u03c1\u03ac\u03c8\u03b5\u03c4\u03b5 \u03b1\u03c5\u03c4\u03cc\u03bd \u03c4\u03bf\u03bd \u03c7\u03c1\u03ae\u03c3\u03c4\u03b7;", - "users": "Users", + "users": "Χρήστες", - "version": "Έκδοση Kirby", + "version": "\u0388\u03ba\u03b4\u03bf\u03c3\u03b7 Kirby", + "version.changes": "Changed version", + "version.compare": "Compare versions", "version.current": "Current version", "version.latest": "Latest version", "versionInformation": "Version information", - "view.account": "Your account", - "view.installation": "Installation", + "view": "View", + "view.account": "\u039f \u03bb\u03bf\u03b3\u03b1\u03c1\u03b9\u03b1\u03c3\u03bc\u03cc\u03c2 \u03c3\u03b1\u03c2", + "view.installation": "\u0395\u03b3\u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7", "view.languages": "Γλώσσες", "view.resetPassword": "Reset password", "view.site": "Iστοσελίδα", "view.system": "System", - "view.users": "Users", + "view.users": "\u03a7\u03c1\u03ae\u03c3\u03c4\u03b5\u03c2", "welcome": "Καλώς ήρθατε", "year": "Έτος", diff --git a/kirby/i18n/translations/en.json b/kirby/i18n/translations/en.json index 4ada2bc..f556c6f 100644 --- a/kirby/i18n/translations/en.json +++ b/kirby/i18n/translations/en.json @@ -3,19 +3,26 @@ "account.delete": "Delete your account", "account.delete.confirm": "Do you really want to delete your account? You will be logged out immediately. Your account cannot be recovered.", + "activate": "Activate", "add": "Add", + "alpha": "Alpha", "author": "Author", "avatar": "Profile picture", "back": "Back", "cancel": "Cancel", "change": "Change", "close": "Close", + "changes": "Changes", "confirm": "Ok", "collapse": "Collapse", "collapse.all": "Collapse All", + "color": "Color", + "coordinates": "Coordinates", "copy": "Copy", "copy.all": "Copy all", + "copy.success": "{count} copied!", "create": "Create", + "custom": "Custom", "date": "Date", "date.select": "Select a date", @@ -34,13 +41,20 @@ "delete": "Delete", "delete.all": "Delete all", + "dialog.fields.empty": "This dialog has no fields", "dialog.files.empty": "No files to select", "dialog.pages.empty": "No pages to select", + "dialog.text.empty": "This dialog does not define any text", "dialog.users.empty": "No users to select", "dimensions": "Dimensions", + "disable": "Disable", "disabled": "Disabled", "discard": "Discard", + + "drawer.fields.empty": "This drawer has no fields", + + "domain": "Domain", "download": "Download", "duplicate": "Duplicate", @@ -49,11 +63,13 @@ "email": "Email", "email.placeholder": "mail@example.com", + "enter": "Enter", "entries": "Entries", "entry": "Entry", "environment": "Environment", + "error": "Error", "error.access.code": "Invalid code", "error.access.login": "Invalid login", "error.access.panel": "You are not allowed to access the panel", @@ -77,10 +93,14 @@ "error.email.preset.notFound": "The email preset \"{name}\" cannot be found", "error.field.converter.invalid": "Invalid converter \"{converter}\"", + "error.field.link.options": "Invalid options: {options}", "error.field.type.missing": "Field \"{ name }\": The field type \"{ type }\" does not exist", "error.file.changeName.empty": "The name must not be empty", "error.file.changeName.permission": "You are not allowed to change the name of \"{filename}\"", + "error.file.changeTemplate.invalid": "The template for the file \"{id}\" cannot be changed to \"{template}\" (valid: \"{blueprints}\")", + "error.file.changeTemplate.permission": "You are not allowed to change the template for the file \"{id}\"", + "error.file.duplicate": "A file with the name \"{filename}\" already exists", "error.file.extension.forbidden": "The extension \"{extension}\" is not allowed", "error.file.extension.invalid": "Invalid extension: {extension}", @@ -95,6 +115,7 @@ "error.file.minheight": "The height of the image must be at least {height} pixels", "error.file.minsize": "The file is too small", "error.file.minwidth": "The width of the image must be at least {width} pixels", + "error.file.name.unique": "The filename must be unique", "error.file.name.missing": "The filename must not be empty", "error.file.notFound": "The file \"{filename}\" cannot be found", "error.file.orientation": "The orientation of the image must be \"{orientation}\"", @@ -116,15 +137,20 @@ "error.layout.validation.block": "There's an error on the \"{field}\" field in block {blockIndex} using the \"{fieldset}\" block type in layout {layoutIndex}", "error.layout.validation.settings": "There's an error in layout {index} settings", - "error.license.format": "Please enter a valid license key", + "error.license.domain": "The domain for the license is missing", "error.license.email": "Please enter a valid email address", + "error.license.format": "Please enter a valid license code", "error.license.verification": "The license could not be verified", + "error.login.totp.confirm.invalid": "Invalid code", + "error.login.totp.confirm.missing": "Please enter the current code", + "error.object.validation": "There’s an error in the \"{label}\" field:\n{message}", "error.offline": "The Panel is currently offline", "error.page.changeSlug.permission": "You are not allowed to change the URL appendix for \"{slug}\"", + "error.page.changeSlug.reserved": "The path of top-level pages must not start with \"{path}\"", "error.page.changeStatus.incomplete": "The page has errors and cannot be published", "error.page.changeStatus.permission": "The status for this page cannot be changed", "error.page.changeStatus.toDraft.invalid": "The page \"{slug}\" cannot be converted to a draft", @@ -140,6 +166,13 @@ "error.page.draft.duplicate": "A page draft with the URL appendix \"{slug}\" already exists", "error.page.duplicate": "A page with the URL appendix \"{slug}\" already exists", "error.page.duplicate.permission": "You are not allowed to duplicate \"{slug}\"", + "error.page.move.ancestor": "The page cannot be moved into itself", + "error.page.move.directory": "The page directory cannot be moved", + "error.page.move.duplicate": "A sub page with the URL appendix \"{slug}\" already exists", + "error.page.move.noSections": "The page \"{parent}\" cannot be a parent of any page because it lacks any pages sections in its blueprint", + "error.page.move.notFound": "The moved page could not be found", + "error.page.move.permission": "You are not allowed to move \"{slug}\"", + "error.page.move.template": "The \"{template}\" template is not accepted as a subpage of \"{parent}\"", "error.page.notFound": "The page \"{slug}\" cannot be found", "error.page.num.invalid": "Please enter a valid sorting number. Numbers must not be negative.", "error.page.slug.invalid": "Please enter a valid URL appendix", @@ -166,6 +199,8 @@ "error.site.changeTitle.permission": "You are not allowed to change the title of the site", "error.site.update.permission": "You are not allowed to update the site", + "error.structure.validation": "There's an error on the \"{field}\" field in row {index}", + "error.template.default.notFound": "The default template does not exist", "error.unexpected": "An unexpected error occurred! Enable debug mode for more info: https://getkirby.com/docs/reference/system/options/debug", @@ -198,8 +233,10 @@ "error.validation.accepted": "Please confirm", "error.validation.alpha": "Please only enter characters between a-z", "error.validation.alphanum": "Please only enter characters between a-z or numerals 0-9", + "error.validation.anchor": "Please enter a correct link anchor", "error.validation.between": "Please enter a value between \"{min}\" and \"{max}\"", "error.validation.boolean": "Please confirm or deny", + "error.validation.color": "Please enter a valid color in the {format} format", "error.validation.contains": "Please enter a value that contains \"{needle}\"", "error.validation.date": "Please enter a valid date", "error.validation.date.after": "Please enter a date after {date}", @@ -214,6 +251,7 @@ "error.validation.integer": "Please enter a valid integer", "error.validation.ip": "Please enter a valid IP address", "error.validation.less": "Please enter a value lower than {max}", + "error.validation.linkType": "The link type is not allowed", "error.validation.match": "The value does not match the expected pattern", "error.validation.max": "Please enter a value equal to or lower than {max}", "error.validation.maxlength": "Please enter a shorter value. (max. {max} characters)", @@ -230,15 +268,18 @@ "error.validation.same": "Please enter \"{other}\"", "error.validation.size": "The size of the value must be \"{size}\"", "error.validation.startswith": "The value must start with \"{start}\"", + "error.validation.tel": "Please enter an unformatted phone number", "error.validation.time": "Please enter a valid time", "error.validation.time.after": "Please enter a time after {time}", "error.validation.time.before": "Please enter a time before {time}", "error.validation.time.between": "Please enter a time between {min} and {max}", + "error.validation.uuid": "Please enter a valid UUID", "error.validation.url": "Please enter a valid URL", "expand": "Expand", "expand.all": "Expand All", + "field.invalid": "The field is invalid", "field.required": "The field is required", "field.blocks.changeType": "Change type", "field.blocks.code.name": "Code", @@ -248,8 +289,9 @@ "field.blocks.delete.confirm.all": "Do you really want to delete all blocks?", "field.blocks.delete.confirm.selected": "Do you really want to delete the selected blocks?", "field.blocks.empty": "No blocks yet", + "field.blocks.fieldsets.empty": "No fieldsets yet", "field.blocks.fieldsets.label": "Please select a block type …", - "field.blocks.fieldsets.paste": "Press {{ shortcut }} to paste/import blocks from your clipboard", + "field.blocks.fieldsets.paste": "Press {{ shortcut }} to import layouts/blocks from your clipboard Only those allowed in the current field will get inserted.", "field.blocks.gallery.name": "Gallery", "field.blocks.gallery.images.empty": "No images yet", "field.blocks.gallery.images.label": "Images", @@ -257,11 +299,16 @@ "field.blocks.heading.name": "Heading", "field.blocks.heading.text": "Text", "field.blocks.heading.placeholder": "Heading …", + "field.blocks.figure.back.plain": "Plain", + "field.blocks.figure.back.pattern.light": "Pattern (light)", + "field.blocks.figure.back.pattern.dark": "Pattern (dark)", "field.blocks.image.alt": "Alternative text", "field.blocks.image.caption": "Caption", "field.blocks.image.crop": "Crop", "field.blocks.image.link": "Link", "field.blocks.image.location": "Location", + "field.blocks.image.location.internal": "This website", + "field.blocks.image.location.external": "External source", "field.blocks.image.name": "Image", "field.blocks.image.placeholder": "Select an image", "field.blocks.image.ratio": "Ratio", @@ -278,38 +325,61 @@ "field.blocks.quote.citation.placeholder": "by …", "field.blocks.text.name": "Text", "field.blocks.text.placeholder": "Text …", + "field.blocks.video.autoplay": "Autoplay", "field.blocks.video.caption": "Caption", + "field.blocks.video.controls": "Controls", + "field.blocks.video.location": "Location", + "field.blocks.video.loop": "Loop", + "field.blocks.video.muted": "Muted", "field.blocks.video.name": "Video", "field.blocks.video.placeholder": "Enter a video URL", + "field.blocks.video.poster": "Poster", + "field.blocks.video.preload": "Preload", "field.blocks.video.url.label": "Video-URL", "field.blocks.video.url.placeholder": "https://youtube.com/?v=", "field.files.empty": "No files selected yet", + "field.files.empty.single": "No file selected yet", + "field.layout.change": "Change layout", "field.layout.delete": "Delete layout", "field.layout.delete.confirm": "Do you really want to delete this layout?", + "field.layout.delete.confirm.all": "Do you really want to delete all layouts?", "field.layout.empty": "No rows yet", "field.layout.select": "Select a layout", "field.object.empty": "No information yet", "field.pages.empty": "No pages selected yet", + "field.pages.empty.single": "No page selected yet", "field.structure.delete.confirm": "Do you really want to delete this row?", "field.structure.delete.confirm.all": "Do you really want to delete all entries?", "field.structure.empty": "No entries yet", "field.users.empty": "No users selected yet", + "field.users.empty.single": "No user selected yet", + "fields.empty": "No fields yet", + + "file": "File", "file.blueprint": "This file has no blueprint yet. You can define the setup in /site/blueprints/files/{blueprint}.yml", + "file.changeTemplate": "Change template", + "file.changeTemplate.notice": "Changing the file's template will remove content for fields that don't match in type. If the new template defines certain rules, e.g. image dimensions, those will also be applied irreversibly. Use with caution.", "file.delete.confirm": "Do you really want to delete
{filename}?", + "file.focus.placeholder": "Set focal point", + "file.focus.reset": "Remove focal point", + "file.focus.title": "Focus", "file.sort": "Change position", "files": "Files", "files.empty": "No files yet", + "filter": "Filter", + "hide": "Hide", "hour": "Hour", + "hue": "Hue", "import": "Import", "info": "Info", "insert": "Insert", @@ -327,7 +397,6 @@ "installation.issues.mbstring": "The MB String extension is required", "installation.issues.media": "The /media folder does not exist or is not writable", "installation.issues.php": "Make sure to use PHP 8+", - "installation.issues.server": "Kirby requires Apache, Nginx or Caddy", "installation.issues.sessions": "The /site/sessions folder does not exist or is not writable", "language": "Language", @@ -335,6 +404,7 @@ "language.convert": "Make default", "language.convert.confirm": "

Do you really want to convert {name} to the default language? This cannot be undone.

If {name} has untranslated content, there will no longer be a valid fallback and parts of your site might be empty.

", "language.create": "Add a new language", + "language.default": "Default language", "language.delete.confirm": "Do you really want to delete the language {name} including all translations? This cannot be undone!", "language.deleted": "The language has been deleted", "language.direction": "Reading direction", @@ -343,7 +413,16 @@ "language.locale": "PHP locale string", "language.locale.warning": "You are using a custom locale set up. Please modify it in the language file in /site/languages", "language.name": "Name", + "language.secondary": "Secondary language", + "language.settings": "Language settings", "language.updated": "The language has been updated", + "language.variables": "Language variables", + "language.variables.empty": "No translations yet", + + "language.variable.delete.confirm": "Do you really want to delete the variable for {key}?", + "language.variable.key": "Key", + "language.variable.notFound": "The variable could not be found", + "language.variable.value": "Value", "languages": "Languages", "languages.default": "Default language", @@ -352,15 +431,30 @@ "languages.secondary.empty": "There are no secondary languages yet", "license": "License", + "license.activate": "Activate it now", + "license.activate.label": "Please activate your license", + "license.activate.domain": "Your license will be activated for {host}.", + "license.activate.local": "You are about to activate your Kirby license for your local domain {host}. If this site will be deployed to a public domain, please activate it there instead. If {host} is the domain you want to use your license for, please continue.", + "license.activated": "Activated", "license.buy": "Buy a license", - "license.register": "Register", + "license.code": "Code", + "license.code.help": "You received your license code after the purchase via email. Please copy and paste it here.", + "license.code.label": "Please enter your license code", + "license.status.active.info": "Includes new major versions until {date}", + "license.status.active.label": "Valid license", + "license.status.demo.info": "This is a demo installation", + "license.status.demo.label": "Demo", + "license.status.inactive.info": "Renew license to update to new major versions", + "license.status.inactive.label": "No new major versions", + "license.status.legacy.bubble": "Ready to renew your license?", + "license.status.legacy.info": "Your license does not cover this version", + "license.status.legacy.label": "Please renew your license", + "license.status.missing.bubble": "Ready to launch your site?", + "license.status.missing.info": "No valid license", + "license.status.missing.label": "Please activate your license", "license.manage": "Manage your licenses", - "license.register.help": "You received your license code after the purchase via email. Please copy and paste it to register.", - "license.register.label": "Please enter your license code", - "license.register.domain": "Your license will be registered to {host}.", - "license.register.local": "You are about to register your license for your local domain {host}. If this site will be deployed to a public domain, please register it there instead. If {host} is the domain you want to license Kirby to, please continue.", - "license.register.success": "Thank you for supporting Kirby", - "license.unregistered": "This is an unregistered demo of Kirby", + "license.purchased": "Purchased", + "license.success": "Thank you for supporting Kirby", "license.unregistered.label": "Unregistered", "link": "Link", @@ -370,17 +464,18 @@ "lock.unsaved": "Unsaved changes", "lock.unsaved.empty": "There are no more unsaved changes", - "lock.isLocked": "Unsaved changes by {email}", - "lock.file.isLocked": "The file is currently being edited by {email} and cannot be changed.", - "lock.page.isLocked": "The page is currently being edited by {email} and cannot be changed.", + "lock.isLocked": "Unsaved changes by {email}", "lock.unlock": "Unlock", - "lock.isUnlocked": "Your unsaved changes have been overwritten by another user. You can download your changes to merge them manually.", + "lock.unlock.submit": "Unlock and overwrite unsaved changes by {email}", + "lock.isUnlocked": "Was unlocked by another user", "login": "Log in", "login.code.label.login": "Login code", "login.code.label.password-reset": "Password reset code", "login.code.placeholder.email": "000 000", + "login.code.placeholder.totp": "000000", "login.code.text.email": "If your email address is registered, the requested code was sent via email.", + "login.code.text.totp": "Please enter the one‑time code from your authenticator app.", "login.email.login.body": "Hi {user.nameOrEmail},\n\nYou recently requested a login code for the Panel of {site}.\nThe following login code will be valid for {timeout} minutes:\n\n{code}\n\nIf you did not request a login code, please ignore this email or contact your administrator if you have questions.\nFor security, please DO NOT forward this email.", "login.email.login.subject": "Your login code", "login.email.password-reset.body": "Hi {user.nameOrEmail},\n\nYou recently requested a password reset code for the Panel of {site}.\nThe following password reset code will be valid for {timeout} minutes:\n\n{code}\n\nIf you did not request a password reset code, please ignore this email or contact your administrator if you have questions.\nFor security, please DO NOT forward this email.", @@ -391,9 +486,24 @@ "login.toggleText.code.email-password": "Login with password", "login.toggleText.password-reset.email": "Forgot your password?", "login.toggleText.password-reset.email-password": "← Back to login", + "login.totp.enable.option": "Set up one‑time codes", + "login.totp.enable.intro": "Authenticator apps can generate one‑time codes that are used as a second factor when signing into your account.", + "login.totp.enable.qr.label": "1. Scan this QR code", + "login.totp.enable.qr.help": "Unable to scan? Add the setup key {secret} manually to your authenticator app.", + "login.totp.enable.confirm.headline": "2. Confirm with generated code", + "login.totp.enable.confirm.text": "Your app generates a new one‑time code every 30 seconds. Enter the current code to complete the setup:", + "login.totp.enable.confirm.label": "Current code", + "login.totp.enable.confirm.help": "After this setup, we will ask you for a one‑time code every time you log in.", + "login.totp.enable.success": "One‑time codes enabled", + "login.totp.disable.option": "Disable one‑time codes", + "login.totp.disable.label": "Enter your password to disable one‑time codes", + "login.totp.disable.help": "In the future, a different second factor like a login code sent via email will be requested when you log in. You can always set up one‑time codes again later.", + "login.totp.disable.admin": "

This will disable one‑time codes for {user}.

In the future, a different second factor like a login code sent via email will be requested when they log in. {user} can set up one‑time codes again after their next login.

", + "login.totp.disable.success": "One‑time codes disabled", "logout": "Log out", + "merge": "Merge", "menu": "Menu", "meridiem": "AM/PM", "mime": "Media Type", @@ -414,21 +524,26 @@ "months.september": "September", "more": "More", + "move": "Move", "name": "Name", "next": "Next", + "night": "Night", "no": "no", "off": "off", "on": "on", "open": "Open", "open.newWindow": "Open in new window", + "option": "Option", "options": "Options", "options.none": "No options", + "options.all": "Show all {count} options", "orientation": "Orientation", "orientation.landscape": "Landscape", "orientation.portrait": "Portrait", "orientation.square": "Square", + "page": "Page", "page.blueprint": "This page has no blueprint yet. You can define the setup in /site/blueprints/pages/{blueprint}.yml", "page.changeSlug": "Change URL", "page.changeSlug.fromTitle": "Create from title", @@ -436,13 +551,15 @@ "page.changeStatus.position": "Please select a position", "page.changeStatus.select": "Select a new status", "page.changeTemplate": "Change template", + "page.changeTemplate.notice": "Changing the page's template will remove content for fields that don't match in type. Use with caution.", + "page.create": "Create as {status}", "page.delete.confirm": "Do you really want to delete {title}?", "page.delete.confirm.subpages": "This page has subpages.
All subpages will be deleted as well.", "page.delete.confirm.title": "Enter the page title to confirm", - "page.draft.create": "Create draft", "page.duplicate.appendix": "Copy", "page.duplicate.files": "Copy files", "page.duplicate.pages": "Copy pages", + "page.move": "Move page", "page.sort": "Change position", "page.status": "Status", "page.status.draft": "Draft", @@ -463,6 +580,7 @@ "password": "Password", "paste": "Paste", "paste.after": "Paste after", + "paste.success": "{count} pasted!", "pixel": "Pixel", "plugin": "Plugin", "plugins": "Plugins", @@ -470,7 +588,9 @@ "preview": "Preview", "remove": "Remove", "rename": "Rename", + "renew": "Renew", "replace": "Replace", + "replace.with": "Replace with", "retry": "Try again", "revert": "Revert", "revert.confirm": "Do you really want to delete all unsaved changes?", @@ -486,10 +606,12 @@ "save": "Save", "search": "Search", + "searching": "Searching", "search.min": "Enter {min} characters to search", - "search.all": "Show all", + "search.all": "Show all {count} results", "search.results.none": "No results", + "section.invalid": "The section is invalid", "section.required": "The section is required", "security": "Security", @@ -501,8 +623,15 @@ "size": "Size", "slug": "URL appendix", "sort": "Sort", + "sort.drag": "Drag to sort …", + "split": "Split", "stats.empty": "No reports", + "status": "Status", + + "system.info.copy": "Copy info", + "system.info.copied": "System info copied", + "system.issues.api.methods": "Your server does not support PATCH requests", "system.issues.content": "The content folder seems to be exposed", "system.issues.eol.kirby": "Your installed Kirby version has reached end-of-life and will not receive further security updates", "system.issues.eol.plugin": "Your installed version of the { plugin } plugin is has reached end-of-life and will not receive further security updates", @@ -511,7 +640,9 @@ "system.issues.git": "The .git folder seems to be exposed", "system.issues.https": "We recommend HTTPS for all your sites", "system.issues.kirby": "The kirby folder seems to be exposed", + "system.issues.local": "The site is running locally with relaxed security checks", "system.issues.site": "The site folder seems to be exposed", + "system.issues.vue.compiler": "The Vue template compiler is enabled", "system.issues.vulnerability.kirby": "Your installation might be affected by the following vulnerability ({ severity } severity): { description }", "system.issues.vulnerability.plugin": "Your installation might be affected by the following vulnerability in the { plugin } plugin ({ severity } severity): { description }", "system.updateStatus": "Update status", @@ -524,10 +655,13 @@ "system.updateStatus.update": "Free update { version } available", "system.updateStatus.upgrade": "Upgrade { version } available", - "title": "Title", + "tel": "Phone", + "tel.placeholder": "+49123456789", "template": "Template", + "title": "Title", "today": "Today", + "toolbar.button.clear": "Clear formatting", "toolbar.button.code": "Code", "toolbar.button.bold": "Bold", "toolbar.button.email": "Email", @@ -545,6 +679,8 @@ "toolbar.button.link": "Link", "toolbar.button.paragraph": "Paragraph", "toolbar.button.strike": "Strike-through", + "toolbar.button.sub": "Subscript", + "toolbar.button.sup": "Superscript", "toolbar.button.ol": "Ordered list", "toolbar.button.underline": "Underline", "toolbar.button.ul": "Bullet list", @@ -554,6 +690,8 @@ "translation.name": "English", "translation.locale": "en_US", + "type": "Type", + "upload": "Upload", "upload.error.cantMove": "The uploaded file could not be moved", "upload.error.cantWrite": "Failed to write file to disk", @@ -578,6 +716,7 @@ "user.changeLanguage": "Change language", "user.changeName": "Rename this user", "user.changePassword": "Change password", + "user.changePassword.current": "Your current password", "user.changePassword.new": "New password", "user.changePassword.new.confirm": "Confirm the new password…", "user.changeRole": "Change role", diff --git a/kirby/i18n/translations/eo.json b/kirby/i18n/translations/eo.json index 0ee83fc..c4a8fd4 100644 --- a/kirby/i18n/translations/eo.json +++ b/kirby/i18n/translations/eo.json @@ -3,19 +3,28 @@ "account.delete": "Forigi vian konton", "account.delete.confirm": "Ĉu vi certe deziras forigi vian konton? Vi estos tuj elsalutita. Ne eblos malforigi vian konton.", + "activate": "Activate", "add": "Aldoni", + "alpha": "Alpha", "author": "Aŭtoro", "avatar": "Profilbildo", "back": "Reen", "cancel": "Nuligi", "change": "Ŝanĝi", "close": "Fermi", + "changes": "Changes", "confirm": "Bone", "collapse": "Fermi", "collapse.all": "Fermi ĉiujn", + "color": "Color", + "coordinates": "Coordinates", "copy": "Kopii", "copy.all": "Kopii ĉiujn", + "copy.success": "{count} copied!", + "copy.success.multiple": "{count} copied!", + "copy.url": "Copy URL", "create": "Krei", + "custom": "Custom", "date": "Dato", "date.select": "Elekti daton", @@ -34,13 +43,20 @@ "delete": "Forigi", "delete.all": "Forigi ĉiujn", + "dialog.fields.empty": "This dialog has no fields", "dialog.files.empty": "Neniu dosiero por elekti", "dialog.pages.empty": "Neniu paĝo por elekti", + "dialog.text.empty": "This dialog does not define any text", "dialog.users.empty": "Neniu uzanto por elekti", "dimensions": "Dimensioj", + "disable": "Disable", "disabled": "Malebligita", "discard": "Forĵeti", + + "drawer.fields.empty": "This drawer has no fields", + + "domain": "Domain", "download": "Elŝuti", "duplicate": "Duobligi", @@ -49,11 +65,13 @@ "email": "Retpoŝto", "email.placeholder": "retpoŝto@ekzemplo.com", + "enter": "Enter", "entries": "Entries", "entry": "Entry", "environment": "Medio", + "error": "Eraro", "error.access.code": "Nevalida kodo", "error.access.login": "Nevalida ensaluto", "error.access.panel": "Vi ne rajtas eniri la administran panelon", @@ -74,13 +92,31 @@ "error.cache.type.invalid": "Invalid cache type \"{type}\"", + "error.content.lock.delete": "The version is locked and cannot be deleted", + "error.content.lock.move": "The source version is locked and cannot be moved", + "error.content.lock.publish": "This version is already published", + "error.content.lock.replace": "The version is locked and cannot be replaced", + "error.content.lock.update": "The version is locked and cannot be updated", + + "error.entries.max.plural": "You must not add more than {max} entries", + "error.entries.max.singular": "You must not add more than one entry", + "error.entries.min.plural": "You must add at least {min} entries", + "error.entries.min.singular": "You must add at least one entry", + "error.entries.supports": "\"{type}\" field type is not supported for the entries field", + "error.entries.validation": "There's an error on the \"{field}\" field in row {index}", + "error.email.preset.notFound": "La retpoŝta antaŭagordo \"{name}\" ne estas trovebla", "error.field.converter.invalid": "Nevalida konvertilo \"{converter}\"", + "error.field.link.options": "Invalid options: {options}", "error.field.type.missing": "Field \"{ name }\": The field type \"{ type }\" does not exist", "error.file.changeName.empty": "La nomo ne rajtas esti malplena", "error.file.changeName.permission": "Vi ne rajtas ŝanĝi la nomon de \"{filename}\"", + "error.file.changeTemplate.invalid": "The template for the file \"{id}\" cannot be changed to \"{template}\" (valid: \"{blueprints}\")", + "error.file.changeTemplate.permission": "You are not allowed to change the template for the file \"{id}\"", + + "error.file.delete.multiple": "Not all files could be deleted. Try each remaining file individually to see the specific error that prevents deletion.", "error.file.duplicate": "Jam ekzistas dosiero nomita \"{filename}\"", "error.file.extension.forbidden": "La dosiersufikso \"{extension}\" ne estas permesita", "error.file.extension.invalid": "Nevalida dosiersufikso: {extension}", @@ -95,9 +131,11 @@ "error.file.minheight": "La bildo devas esti almenaŭ {height} bilderojn alta", "error.file.minsize": "La dosiero estas tro malgranda", "error.file.minwidth": "La bildo devas esti almenaŭ {width} bilderojn larĝa", + "error.file.name.unique": "The filename must be unique", "error.file.name.missing": "La dosiernomo ne rajtas esti malplena", "error.file.notFound": "La dosiero \"{filename}\" ne troveblas", "error.file.orientation": "La orientiĝo de la bildo devas esti \"{orientation}\"", + "error.file.sort.permission": "You are not allowed to change the sorting of \"{filename}\"", "error.file.type.forbidden": "Vi ne rajtas alŝuti dosiertipon {type}", "error.file.type.invalid": "Nevalida dosiertipo: {type}", "error.file.undefined": "La dosiero ne troveblas", @@ -106,22 +144,30 @@ "error.form.notSaved": "Ne eblis konservi la formularon", "error.language.code": "Bonvolu entajpi validan kodon por la lingvo", + "error.language.create.permission": "You are not allowed to create a language", + "error.language.delete.permission": "You are not allowed to delete the language", "error.language.duplicate": "La lingvo jam ekzistas", "error.language.name": "Bonvolu entajpi validan nomon por la lingvo", "error.language.notFound": "La lingvo ne troveblas", + "error.language.update.permission": "You are not allowed to update the language", "error.layout.validation.block": "There's an error on the \"{field}\" field in block {blockIndex} using the \"{fieldset}\" block type in layout {layoutIndex}", "error.layout.validation.settings": "Estas eraro en la agordoj de blokaranĝo {index}", - "error.license.format": "Bonvolu entajpi validan kodon de permisilo", + "error.license.domain": "The domain for the license is missing", "error.license.email": "Bonvolu entajpi validan retpoŝtadreson", + "error.license.format": "Please enter a valid license code", "error.license.verification": "Ne eblis kontroli la permisilon", + "error.login.totp.confirm.invalid": "Nevalida kodo", + "error.login.totp.confirm.missing": "Please enter the current code", + "error.object.validation": "There’s an error in the \"{label}\" field:\n{message}", "error.offline": "La panelo estas ĉi-momente nekonektita", "error.page.changeSlug.permission": "Vi ne rajtas ŝanĝi la URL-nomon de \"{slug}\"", + "error.page.changeSlug.reserved": "The path of top-level pages must not start with \"{path}\"", "error.page.changeStatus.incomplete": "La paĝo havas erarojn, kaj tiel ne povas esti publikigita", "error.page.changeStatus.permission": "La paĝstato ne estas ŝanĝebla", "error.page.changeStatus.toDraft.invalid": "Ne eblas konverti la paĝon \"{slug}\" al malneto", @@ -133,10 +179,18 @@ "error.page.delete": "Ne eblas forigi la paĝon \"{slug}\"", "error.page.delete.confirm": "Bonvolu entajpi la titolon de la paĝo for konfirmi", "error.page.delete.hasChildren": "Ne eblas forigi la paĝon ĉar ĝi havas subpaĝojn", + "error.page.delete.multiple": "Not all pages could be deleted. Try each remaining page individually to see the specific error that prevents deletion.", "error.page.delete.permission": "Vi ne rajtas forigi \"{slug}\"", "error.page.draft.duplicate": "Malneto uzanta la URL-nomon \"{slug}\" jam ekzistas", "error.page.duplicate": "Paĝo uzanta la URL-nomon \"{slug}\" jam ekzistas", "error.page.duplicate.permission": "Vi ne rajtas duobligi \"{slug}\"", + "error.page.move.ancestor": "The page cannot be moved into itself", + "error.page.move.directory": "The page directory cannot be moved", + "error.page.move.duplicate": "A sub page with the URL appendix \"{slug}\" already exists", + "error.page.move.noSections": "The page \"{parent}\" cannot be a parent of any page because it lacks any pages sections in its blueprint", + "error.page.move.notFound": "The moved page could not be found", + "error.page.move.permission": "You are not allowed to move \"{slug}\"", + "error.page.move.template": "The \"{template}\" template is not accepted as a subpage of \"{parent}\"", "error.page.notFound": "La paĝo \"{slug}\" ne troveblas", "error.page.num.invalid": "Bonvolu entajpi validan ord-numeron. Numeroj devas esti pozitivaj.", "error.page.slug.invalid": "Bonvolu entajpi validan URL-nomon", @@ -163,6 +217,8 @@ "error.site.changeTitle.permission": "Vi ne rajtas ŝanĝi la titolon de la retejo", "error.site.update.permission": "Vi ne rajtas ĝisdatigi la retejon", + "error.structure.validation": "There's an error on the \"{field}\" field in row {index}", + "error.template.default.notFound": "La defaŭlta ŝablono ne ekzistas", "error.unexpected": "An unexpected error occurred! Enable debug mode for more info: https://getkirby.com/docs/reference/system/options/debug", @@ -195,8 +251,10 @@ "error.validation.accepted": "Bonvolu konfirmi", "error.validation.alpha": "Bonvolu entajpi nur literojn inter a-z", "error.validation.alphanum": "Bonvolu entajpi nur aŭ literojn inter a-z aũ numerojn inter 0-9", + "error.validation.anchor": "Please enter a correct link anchor", "error.validation.between": "Bonvolu entajpi valoron inter \"{min}\" kaj \"{max}\"", "error.validation.boolean": "Bonvolu konfirmi aŭ malkonfirmi", + "error.validation.color": "Please enter a valid color in the {format} format", "error.validation.contains": "Bonvolu entajpi valoron kiu enhavas \"{needle}\"", "error.validation.date": "Bonvolu entajpi validan daton", "error.validation.date.after": "Bonvolu entajpi daton post {date}", @@ -211,6 +269,7 @@ "error.validation.integer": "Bonvolu entajpi validan entjeron", "error.validation.ip": "Bonvolu entajpi validan IP-adreson", "error.validation.less": "Bonvolu entajpi valoron malpli ol {max}", + "error.validation.linkType": "The link type is not allowed", "error.validation.match": "La valoro ne kongruas al la atendata ŝablono", "error.validation.max": "Bonvolu entajpi valoron egalan al aũ malpli ol {max}", "error.validation.maxlength": "Bonvolu entajpi pli mallongan valoron (maksimume {max} literojn)", @@ -227,15 +286,18 @@ "error.validation.same": "Bonvolu entajpi \"{other}\"", "error.validation.size": "La grando de la valoro devas esti \"{size}\"", "error.validation.startswith": "La valoro devas komenciĝi per \"{start}\"", + "error.validation.tel": "Please enter an unformatted phone number", "error.validation.time": "Bonvolu entajpi validan horaron", "error.validation.time.after": "Bonvolu entajpi horaron post {time}", "error.validation.time.before": "Bonvolu entajpi horaron antaŭ {time}", "error.validation.time.between": "Bonvolu entajpi horaron inter {min} kaj {max}", + "error.validation.uuid": "Please enter a valid UUID", "error.validation.url": "Bonvolu entajpi validan URL", "expand": "Etendi", "expand.all": "Etendi ĉiujn", + "field.invalid": "The field is invalid", "field.required": "La kampo ne rajtas esti malplena", "field.blocks.changeType": "Ŝanĝi tipon", "field.blocks.code.name": "Kodo", @@ -245,8 +307,9 @@ "field.blocks.delete.confirm.all": "Ĉu vi certe volas forigi ĉiujn blokojn?", "field.blocks.delete.confirm.selected": "Ĉu vi certe volas forigi la elektitajn blokojn?", "field.blocks.empty": "Ankoraŭ neniu bloko", + "field.blocks.fieldsets.empty": "No fieldsets yet", "field.blocks.fieldsets.label": "Bonvolu elekti tipon de bloko ...", - "field.blocks.fieldsets.paste": "Premu {{ shortcut }}por alglui/importi blokojn el via tondujo", + "field.blocks.fieldsets.paste": "Press {{ shortcut }} to import layouts/blocks from your clipboard Only those allowed in the current field will get inserted.", "field.blocks.gallery.name": "Galerio", "field.blocks.gallery.images.empty": "Ankoraŭ neniu bildo", "field.blocks.gallery.images.label": "Bildoj", @@ -254,11 +317,16 @@ "field.blocks.heading.name": "Titolo", "field.blocks.heading.text": "Teksto", "field.blocks.heading.placeholder": "Titolo ...", + "field.blocks.figure.back.plain": "Plain", + "field.blocks.figure.back.pattern.light": "Pattern (light)", + "field.blocks.figure.back.pattern.dark": "Pattern (dark)", "field.blocks.image.alt": "Alternativa titolo", "field.blocks.image.caption": "Apudskribo", "field.blocks.image.crop": "Stuci", "field.blocks.image.link": "Ligilo", "field.blocks.image.location": "Loko", + "field.blocks.image.location.internal": "This website", + "field.blocks.image.location.external": "External source", "field.blocks.image.name": "Bildo", "field.blocks.image.placeholder": "Elekti bildon", "field.blocks.image.ratio": "Proporcio", @@ -275,38 +343,72 @@ "field.blocks.quote.citation.placeholder": "de ...", "field.blocks.text.name": "Teksto", "field.blocks.text.placeholder": "Teksto ...", + "field.blocks.video.autoplay": "Autoplay", "field.blocks.video.caption": "Apudskribo", + "field.blocks.video.controls": "Controls", + "field.blocks.video.location": "Loko", + "field.blocks.video.loop": "Loop", + "field.blocks.video.muted": "Muted", "field.blocks.video.name": "Videâjo", "field.blocks.video.placeholder": "Entajpi URL de videaĵo", + "field.blocks.video.poster": "Poster", + "field.blocks.video.preload": "Preload", "field.blocks.video.url.label": "Video-URL", "field.blocks.video.url.placeholder": "https://youtube.com/?v=", - "field.files.empty": "Ankoraŭ neniu dosiero elektita", + "field.entries.delete.confirm.all": "Do you really want to delete all entries?", + "field.entries.empty": "Ankoraŭ neniu enigo", + "field.files.empty": "Ankoraŭ neniu dosiero elektita", + "field.files.empty.single": "No file selected yet", + + "field.layout.change": "Change layout", "field.layout.delete": "Forigi blokaranĝo", "field.layout.delete.confirm": "Ĉu vi certe volas forigi ĉi tiun blokaranĝon?", + "field.layout.delete.confirm.all": "Do you really want to delete all layouts?", "field.layout.empty": "Ankoraŭ neniu vico", "field.layout.select": "Elekti blokaranĝon", "field.object.empty": "No information yet", "field.pages.empty": "Ankoraŭ neniu paĝo elektita", + "field.pages.empty.single": "No page selected yet", "field.structure.delete.confirm": "Ĉu vi certe volas forigi ĉi tiun vicon?", "field.structure.delete.confirm.all": "Do you really want to delete all entries?", "field.structure.empty": "Ankoraŭ neniu enigo", "field.users.empty": "Ankoraŭ neniu uzanto elektita", + "field.users.empty.single": "No user selected yet", + "fields.empty": "No fields yet", + + "file": "Dosiero", "file.blueprint": "Ĉi tiu dosiero ankoraŭ havas neniun planon. Vi povas difini planon ĉe /site/blueprints/files/{blueprint}.yml", + "file.changeTemplate": "Ŝanĝi ŝablonon", + "file.changeTemplate.notice": "Changing the file's template will remove content for fields that don't match in type. If the new template defines certain rules, e.g. image dimensions, those will also be applied irreversibly. Use with caution.", "file.delete.confirm": "Ĉu vi certe vollas forigi
{filename}?", + "file.focus.placeholder": "Set focal point", + "file.focus.reset": "Remove focal point", + "file.focus.title": "Focus", "file.sort": "Ŝanĝi ordon", "files": "Dosieroj", + "files.delete.confirm.selected": "Do you really want to delete the selected files? This action cannot be undone.", "files.empty": "Ankoraŭ neniu dosiero", + "filter": "Filter", + + "form.discard": "Discard changes", + "form.discard.confirm": "Do you really want to discard all your changes?", + "form.locked": "This content is disabled for you as it is currently edited by another user", + "form.unsaved": "The current changes have not yet been saved", + "form.preview": "Preview changes", + "form.preview.draft": "Preview draft", + "hide": "Kaŝi", "hour": "Horo", + "hue": "Hue", "import": "Importi", "info": "Info", "insert": "Enmeti", @@ -324,7 +426,6 @@ "installation.issues.mbstring": "La kromprogramo MB String estas deviga", "installation.issues.media": "La dosierujo /media ne ekzistas, aũ ne estas skribebla", "installation.issues.php": "Nepre uzu PHP 8+", - "installation.issues.server": "Kirby bezonas Apache, NginxCaddy", "installation.issues.sessions": "La dosierujo /site/sessions ne ekzistas, aŭ ne estas skribebla", "language": "Lingvo", @@ -332,6 +433,7 @@ "language.convert": "Farigi defaŭlton", "language.convert.confirm": "

Ĉu vi certe volas konverti {name} al la defaŭlta lingvo? Ĉi tion vi ne povos malfari.

Se {name} havas netradukitan enhavon, tiuj tekstoj nun ne havos defaŭlton, kaj simple ne aperos en via retejo.

", "language.create": "Aldoni novan lingvon", + "language.default": "Defaŭlta lingvo", "language.delete.confirm": "Ĉu vi certe volas forigi la lingvon {name}, inkluzive de ĉiuj tradukoj? Vi ne povos malfari tion!", "language.deleted": "La lingvo estas forigita", "language.direction": "Direkto de leĝado", @@ -340,7 +442,16 @@ "language.locale": "Lokaĵaro de PHP", "language.locale.warning": "Vi uzas tajloritan agordon de lokaĵaro. Bonvolu ŝanĝi viajn agordojn laŭmende en la lingva dosiero ĉe /site/languages", "language.name": "Nomo", + "language.secondary": "Secondary language", + "language.settings": "Language settings", "language.updated": "La lingvo estas ĝisdatigita", + "language.variables": "Language variables", + "language.variables.empty": "No translations yet", + + "language.variable.delete.confirm": "Do you really want to delete the variable for {key}?", + "language.variable.key": "Key", + "language.variable.notFound": "The variable could not be found", + "language.variable.value": "Value", "languages": "Lingvoj", "languages.default": "Defaŭlta lingvo", @@ -349,15 +460,32 @@ "languages.secondary.empty": "Ankoraŭ estas neniu kromlingvoj", "license": "Permisilo", + "license.activate": "Activate it now", + "license.activate.label": "Please activate your license", + "license.activate.domain": "Your license will be activated for {host}.", + "license.activate.local": "You are about to activate your Kirby license for your local domain {host}. If this site will be deployed to a public domain, please activate it there instead. If {host} is the domain you want to use your license for, please continue.", + "license.activated": "Activated", "license.buy": "Aĉeti permisilon", - "license.register": "Registriĝi", + "license.code": "Kodo", + "license.code.help": "You received your license code after the purchase via email. Please copy and paste it here.", + "license.code.label": "Bonvolu entajpi vian kodon de permisilo", + "license.status.active.info": "Includes new major versions until {date}", + "license.status.active.label": "Valid license", + "license.status.demo.info": "This is a demo installation", + "license.status.demo.label": "Demo", + "license.status.inactive.info": "Renew license to update to new major versions", + "license.status.inactive.label": "No new major versions", + "license.status.legacy.bubble": "Ready to renew your license?", + "license.status.legacy.info": "Your license does not cover this version", + "license.status.legacy.label": "Please renew your license", + "license.status.missing.bubble": "Ready to launch your site?", + "license.status.missing.info": "No valid license", + "license.status.missing.label": "Please activate your license", + "license.status.unknown.info": "The license status is unknown", + "license.status.unknown.label": "Unknown", "license.manage": "Manage your licenses", - "license.register.help": "Vi ricevis vian kodon de permisilo retpoŝte, post aĉeti ĝin. Bonvolu kopii kaj alglui ĝin por registriĝi.", - "license.register.label": "Bonvolu entajpi vian kodon de permisilo", - "license.register.domain": "Your license will be registered to {host}.", - "license.register.local": "You are about to register your license for your local domain {host}. If this site will be deployed to a public domain, please register it there instead. If {host} is the domain you want to license Kirby to, please continue.", - "license.register.success": "Dankon pro subteni Kirby", - "license.unregistered": "Ĉi tiu estas neregistrita kopio de Kirby", + "license.purchased": "Purchased", + "license.success": "Dankon pro subteni Kirby", "license.unregistered.label": "Unregistered", "link": "Ligilo", @@ -367,17 +495,21 @@ "lock.unsaved": "Nekonservitaj ŝanĝoj", "lock.unsaved.empty": "Ĉiuj ŝanĝoj estas nun konservitaj", - "lock.isLocked": "Nekonservitaj ŝanĝoj de {email}", - "lock.file.isLocked": "La dosiero estas ĉi-momente redaktata de {email}, kaj tial ne povas esti ŝanĝita", - "lock.page.isLocked": "La paĝo estas ĉi-momente redaktata de {email}, kaj tial ne povas esti ŝanĝita", + "lock.unsaved.files": "Unsaved files", + "lock.unsaved.pages": "Unsaved pages", + "lock.unsaved.users": "Unsaved accounts", + "lock.isLocked": "Unsaved changes by {email}", "lock.unlock": "Malŝlosi", - "lock.isUnlocked": "Viaj nekonservitaj ŝanĝoj estas ŝanĝitaj de alia uzanto. Vi povas elŝuti dosieron kun viaj ŝanĝoj por permane kunfandi ilin.", + "lock.unlock.submit": "Unlock and overwrite unsaved changes by {email}", + "lock.isUnlocked": "Was unlocked by another user", "login": "Log in", "login.code.label.login": "Ensaluta kodo", "login.code.label.password-reset": "Kodo por restarigi pasvorton", "login.code.placeholder.email": "000 000", + "login.code.placeholder.totp": "000000", "login.code.text.email": "Se via retpoŝtadreso estas enregistrita, via kodo estis sendita retpoŝte", + "login.code.text.totp": "Please enter the one‑time code from your authenticator app.", "login.email.login.body": "Saluton {user.nameOrEmail},\n\nVi petis ensalutan kodon por la panelo de la retejo {site}.\nLa sekvanta kodo validos dum {timeout} minutoj:\n\n{code}\n\nSe vi ne petis ensalutan kodon, bonvolu ignori ĉi tiun mesaĝon, aŭ kontaktu vian sistem-administranton se vi havas demandojn.\nPro sekureco, bonvolu NE plusendi ĉi tiun mesaĝon.", "login.email.login.subject": "Via ensaluta kodo", "login.email.password-reset.body": "Saluton {user.nameOrEmail},\n\nVi petis kodon por restarigi vian pasvorton por la panelo de la retejo {site}.\nLa sekvanta kodo validos dum {timeout} minutoj:\n\n{code}\n\nSe vi ne petis kodon por restarigi vian pasvorton, bonvolu ignori ĉi tiun mesaĝon, aŭ kontaktu vian sistem-administranton se vi havas demandojn.\nPro sekureco, bonvolu NE plusendi ĉi tiun mesaĝon.", @@ -388,9 +520,24 @@ "login.toggleText.code.email-password": "Ensaluti per pasvorto", "login.toggleText.password-reset.email": "Ĉu vi forgesis vian pasvorton?", "login.toggleText.password-reset.email-password": "← Reen al ensaluto", + "login.totp.enable.option": "Set up one‑time codes", + "login.totp.enable.intro": "Authenticator apps can generate one‑time codes that are used as a second factor when signing into your account.", + "login.totp.enable.qr.label": "1. Scan this QR code", + "login.totp.enable.qr.help": "Unable to scan? Add the setup key {secret} manually to your authenticator app.", + "login.totp.enable.confirm.headline": "2. Confirm with generated code", + "login.totp.enable.confirm.text": "Your app generates a new one‑time code every 30 seconds. Enter the current code to complete the setup:", + "login.totp.enable.confirm.label": "Current code", + "login.totp.enable.confirm.help": "After this setup, we will ask you for a one‑time code every time you log in.", + "login.totp.enable.success": "One‑time codes enabled", + "login.totp.disable.option": "Disable one‑time codes", + "login.totp.disable.label": "Enter your password to disable one‑time codes", + "login.totp.disable.help": "In the future, a different second factor like a login code sent via email will be requested when you log in. You can always set up one‑time codes again later.", + "login.totp.disable.admin": "

This will disable one‑time codes for {user}.

In the future, a different second factor like a login code sent via email will be requested when they log in. {user} can set up one‑time codes again after their next login.

", + "login.totp.disable.success": "One‑time codes disabled", "logout": "Elsaluti", + "merge": "Merge", "menu": "Menuo", "meridiem": "atm/ptm", "mime": "Tipo de aŭdvidaĵo", @@ -411,21 +558,26 @@ "months.september": "septembro", "more": "Pli", + "move": "Move", "name": "Nomo", "next": "Sekve", + "night": "Night", "no": "ne", "off": "ne", "on": "jes", "open": "Malfermi", "open.newWindow": "Malfermi novan fenestron", + "option": "Option", "options": "Opcioj", "options.none": "Neniu opcio", + "options.all": "Show all {count} options", "orientation": "Orientiĝo", "orientation.landscape": "Horizontala", "orientation.portrait": "Vertikala", "orientation.square": "Kvadrata", + "page": "Paĝo", "page.blueprint": "Ĉi tiu paĝo ankoraŭ ne havas planon. Vi povas difini planon ĉe /site/blueprints/pages/{blueprint}.yml", "page.changeSlug": "Ŝanĝi URL", "page.changeSlug.fromTitle": "Krei el titolo", @@ -433,13 +585,15 @@ "page.changeStatus.position": "Bonvolu elekti ordon", "page.changeStatus.select": "Elekti novan staton", "page.changeTemplate": "Ŝanĝi ŝablonon", + "page.changeTemplate.notice": "Changing the page's template will remove content for fields that don't match in type. Use with caution.", + "page.create": "Create as {status}", "page.delete.confirm": "Ĉu vi certe volas forigi {title}?", "page.delete.confirm.subpages": "Ĉi tiu paĝo havas subpaĝojn.
Ĉiuj subpaĝoj estos ankaŭ forigitaj.", "page.delete.confirm.title": "Entajpu la titolon de la paĝo por konfirmi", - "page.draft.create": "Krei malneton", "page.duplicate.appendix": "Kopii", "page.duplicate.files": "Kopii dosierojn", "page.duplicate.pages": "Kopii paĝojn", + "page.move": "Move page", "page.sort": "Ŝanĝi ordon", "page.status": "Stato", "page.status.draft": "Malneto", @@ -450,6 +604,7 @@ "page.status.unlisted.description": "La paĝo estas atingebla nur per URL", "pages": "Paĝoj", + "pages.delete.confirm.selected": "Do you really want to delete the selected pages? This action cannot be undone.", "pages.empty": "Ankoraŭ neniu paĝo", "pages.status.draft": "Malnetoj", "pages.status.listed": "Publikigita", @@ -460,14 +615,21 @@ "password": "Pasvorto", "paste": "Alglui", "paste.after": "Alglui post", + "paste.success": "{count} pasted!", "pixel": "Pikselo", "plugin": "Plugin", "plugins": "Kromprogramoj", "prev": "Antaŭe", "preview": "Antaŭrigardi", + + "publish": "Publish", + "published": "Publikigita", + "remove": "Forigi", "rename": "Ŝanĝi nomon", + "renew": "Renew", "replace": "Anstataŭi", + "replace.with": "Replace with", "retry": "Provi denove", "revert": "Malfari", "revert.confirm": "Ĉu vi certe volas forigi ĉiujn nekonservitajn ŝanĝojn?", @@ -482,11 +644,14 @@ "role.nobody.title": "Neniu", "save": "Konservi", + "saved": "Saved", "search": "Serĉi", + "searching": "Searching", "search.min": "Entajpu {min} literojn por serĉi", - "search.all": "Montri ĉiujn", + "search.all": "Show all {count} results", "search.results.none": "Neniu rezulto", + "section.invalid": "The section is invalid", "section.required": "La sekcio estas deviga", "security": "Security", @@ -498,16 +663,25 @@ "size": "Grando", "slug": "URL-nomo", "sort": "Ordigi", + "sort.drag": "Drag to sort …", + "split": "Split", "stats.empty": "No reports", + "status": "Stato", + + "system.info.copy": "Copy info", + "system.info.copied": "System info copied", "system.issues.content": "The content folder seems to be exposed", "system.issues.eol.kirby": "Your installed Kirby version has reached end-of-life and will not receive further security updates", "system.issues.eol.plugin": "Your installed version of the { plugin } plugin is has reached end-of-life and will not receive further security updates", + "system.issues.eol.php": "Your installed PHP release { release } has reached end-of-life and will not receive further security updates", "system.issues.debug": "Debugging must be turned off in production", "system.issues.git": "The .git folder seems to be exposed", "system.issues.https": "We recommend HTTPS for all your sites", "system.issues.kirby": "The kirby folder seems to be exposed", + "system.issues.local": "The site is running locally with relaxed security checks", "system.issues.site": "The site folder seems to be exposed", + "system.issues.vue.compiler": "The Vue template compiler is enabled", "system.issues.vulnerability.kirby": "Your installation might be affected by the following vulnerability ({ severity } severity): { description }", "system.issues.vulnerability.plugin": "Your installation might be affected by the following vulnerability in the { plugin } plugin ({ severity } severity): { description }", "system.updateStatus": "Update status", @@ -520,10 +694,19 @@ "system.updateStatus.update": "Free update { version } available", "system.updateStatus.upgrade": "Upgrade { version } available", - "title": "Titolo", + "tel": "Phone", + "tel.placeholder": "+49123456789", "template": "Ŝablono", + + "theme": "Theme", + "theme.light": "Lights on", + "theme.dark": "Lights off", + "theme.automatic": "Match system default", + + "title": "Titolo", "today": "Hodiaŭ", + "toolbar.button.clear": "Clear formatting", "toolbar.button.code": "Kodo", "toolbar.button.bold": "Grasa", "toolbar.button.email": "Retpoŝto", @@ -541,6 +724,8 @@ "toolbar.button.link": "Ligilo", "toolbar.button.paragraph": "Paragrafo", "toolbar.button.strike": "Trastrekita", + "toolbar.button.sub": "Subscript", + "toolbar.button.sup": "Superscript", "toolbar.button.ol": "Numerita listo", "toolbar.button.underline": "Substrekita", "toolbar.button.ul": "Bula listo", @@ -550,6 +735,8 @@ "translation.name": "Esperanto", "translation.locale": "eo", + "type": "Type", + "upload": "Alŝuti", "upload.error.cantMove": "Ne eblis movi la alŝutita dosiero", "upload.error.cantWrite": "Ne eblis registri la dosieron en la diskon", @@ -574,6 +761,7 @@ "user.changeLanguage": "Ŝanĝi lingvon", "user.changeName": "Ŝangi la nomon de la uzanto", "user.changePassword": "Ŝanĝi pasvorton", + "user.changePassword.current": "Your current password", "user.changePassword.new": "Nova pasvorto", "user.changePassword.new.confirm": "Konfirmi la novan pasvorton...", "user.changeRole": "Ŝanĝi rolon", @@ -585,10 +773,13 @@ "users": "Uzantoj", "version": "Versio", + "version.changes": "Changed version", + "version.compare": "Compare versions", "version.current": "Current version", "version.latest": "Latest version", "versionInformation": "Version information", + "view": "View", "view.account": "Via konto", "view.installation": "Instalado", "view.languages": "Lingvoj", diff --git a/kirby/i18n/translations/es_419.json b/kirby/i18n/translations/es_419.json index f9e8324..bbfa123 100644 --- a/kirby/i18n/translations/es_419.json +++ b/kirby/i18n/translations/es_419.json @@ -3,57 +3,75 @@ "account.delete": "Eliminar cuenta", "account.delete.confirm": "¿Realmente quieres eliminar tu cuenta? Tu sesión se cerrará inmediatamente. Tu cuenta no podrá ser recuperada. ", - "add": "Add", + "activate": "Activate", + "add": "Agregar", + "alpha": "Alpha", "author": "Autor", - "avatar": "Profile picture", - "back": "Back", - "cancel": "Cancel", + "avatar": "Foto de perfil", + "back": "Regresar", + "cancel": "Cancelar", "change": "Cambiar", - "close": "Close", + "close": "Cerrar", + "changes": "Changes", "confirm": "De acuerdo", "collapse": "Colapsar", "collapse.all": "Colapsar todos", + "color": "Color", + "coordinates": "Coordinates", "copy": "Copiar", "copy.all": "Copiar todo", + "copy.success": "{count} copied!", + "copy.success.multiple": "{count} copied!", + "copy.url": "Copy URL", "create": "Crear", + "custom": "Custom", - "date": "Date", + "date": "Fecha", "date.select": "Selecciona una fecha", "day": "Día", "days.fri": "Vie", - "days.mon": "Mon", - "days.sat": "Sáb", + "days.mon": "Lun", + "days.sat": "S\u00e1b", "days.sun": "Dom", - "days.thu": "Thu", - "days.tue": "Tue", - "days.wed": "Mi", + "days.thu": "Jue", + "days.tue": "Mar", + "days.wed": "Mi\u00e9", "debugging": "Depuración", "delete": "Eliminar", "delete.all": "Eliminar todos", + "dialog.fields.empty": "This dialog has no fields", "dialog.files.empty": "No has seleccionado ningún archivo", "dialog.pages.empty": "No has seleccionado ninguna página", + "dialog.text.empty": "This dialog does not define any text", "dialog.users.empty": "No has seleccionado ningún usuario", "dimensions": "Dimensiones", + "disable": "Disable", "disabled": "Deshabilitado", - "discard": "Discard", + "discard": "Descartar", + + "drawer.fields.empty": "This drawer has no fields", + + "domain": "Domain", "download": "Descargar", "duplicate": "Duplicar", - "edit": "Edit", + "edit": "Editar", "email": "Correo Electrónico", "email.placeholder": "correo@ejemplo.com", + "enter": "Enter", "entries": "Entradas", "entry": "Entrada", "environment": "Ambiente", + "error": "Error", "error.access.code": "Código inválido", "error.access.login": "Ingreso inválido", "error.access.panel": "No tienes permitido acceder al panel", @@ -74,13 +92,31 @@ "error.cache.type.invalid": "Tipo de caché \"{tipo}\" no válido", + "error.content.lock.delete": "The version is locked and cannot be deleted", + "error.content.lock.move": "The source version is locked and cannot be moved", + "error.content.lock.publish": "This version is already published", + "error.content.lock.replace": "The version is locked and cannot be replaced", + "error.content.lock.update": "The version is locked and cannot be updated", + + "error.entries.max.plural": "You must not add more than {max} entries", + "error.entries.max.singular": "You must not add more than one entry", + "error.entries.min.plural": "You must add at least {min} entries", + "error.entries.min.singular": "You must add at least one entry", + "error.entries.supports": "\"{type}\" field type is not supported for the entries field", + "error.entries.validation": "There's an error on the \"{field}\" field in row {index}", + "error.email.preset.notFound": "El preajuste de email \"{name}\" no se pudo encontrar.", "error.field.converter.invalid": "Convertidor inválido \"{converter}\"", + "error.field.link.options": "Invalid options: {options}", "error.field.type.missing": "Campo \"{ name }\": El tipo de campo \"{ type }\" no existe", "error.file.changeName.empty": "El nombre no debe estar vacío", "error.file.changeName.permission": "No tienes permitido cambiar el nombre de \"{filename}\"", + "error.file.changeTemplate.invalid": "The template for the file \"{id}\" cannot be changed to \"{template}\" (valid: \"{blueprints}\")", + "error.file.changeTemplate.permission": "You are not allowed to change the template for the file \"{id}\"", + + "error.file.delete.multiple": "Not all files could be deleted. Try each remaining file individually to see the specific error that prevents deletion.", "error.file.duplicate": "Ya existe un archivo con el nombre \"{filename}\".", "error.file.extension.forbidden": "La extensión \"{extension}\" no está permitida.", "error.file.extension.invalid": "Extensión inválida: {extension}", @@ -95,9 +131,11 @@ "error.file.minheight": "La altura de la imagen debe ser de al menos {height} pixeles", "error.file.minsize": "El archivo es muy pequeño", "error.file.minwidth": "El ancho de la imagen debe ser de al menos {width} pixeles", + "error.file.name.unique": "The filename must be unique", "error.file.name.missing": "El nombre del archivo no debe estar vacío.", "error.file.notFound": "El archivo \"{filename}\" no pudo ser encontrado.", "error.file.orientation": "La orientación de la imagen debe ser \"{orientation}\"", + "error.file.sort.permission": "You are not allowed to change the sorting of \"{filename}\"", "error.file.type.forbidden": "No está permitido subir archivos {type}.", "error.file.type.invalid": "Tipo de archivo inválido: {type}", "error.file.undefined": "El archivo no se puede encontrar", @@ -106,22 +144,30 @@ "error.form.notSaved": "No se pudo guardar el formulario", "error.language.code": "Por favor introduce un código válido para el idioma", + "error.language.create.permission": "You are not allowed to create a language", + "error.language.delete.permission": "You are not allowed to delete the language", "error.language.duplicate": "El idioma ya existe", "error.language.name": "Por favor introduce un nombre válido para el idioma", "error.language.notFound": "No se pudo encontrar el idioma", + "error.language.update.permission": "You are not allowed to update the language", "error.layout.validation.block": "Hay un error en el campo \"{field}\" del bloque {blockIndex} que utiliza el tipo de bloque \"{fieldset}\" en el layout {layoutIndex}", "error.layout.validation.settings": "Hay un error en los ajustes del layout {index}", - "error.license.format": "Por favor introduce una llave de licencia válida", + "error.license.domain": "The domain for the license is missing", "error.license.email": "Por favor ingresa un correo electrónico valido", + "error.license.format": "Please enter a valid license code", "error.license.verification": "La licencia no pude ser verificada", + "error.login.totp.confirm.invalid": "Código inválido", + "error.login.totp.confirm.missing": "Please enter the current code", + "error.object.validation": "Hay un error en el campo \"{label}\":\n{message}", "error.offline": "El Panel se encuentra fuera de linea ", "error.page.changeSlug.permission": "No está permitido cambiar el apéndice de URL para \"{slug}\".", + "error.page.changeSlug.reserved": "The path of top-level pages must not start with \"{path}\"", "error.page.changeStatus.incomplete": "La página tiene errores y no puede ser publicada.", "error.page.changeStatus.permission": "El estado de esta página no se puede cambiar.", "error.page.changeStatus.toDraft.invalid": "La página \"{slug}\" no se puede convertir en un borrador", @@ -133,17 +179,25 @@ "error.page.delete": "La página \"{slug}\" no se puede eliminar", "error.page.delete.confirm": "Por favor, introduce el título de la página para confirmar", "error.page.delete.hasChildren": "La página tiene subpáginas y no se puede eliminar", + "error.page.delete.multiple": "Not all pages could be deleted. Try each remaining page individually to see the specific error that prevents deletion.", "error.page.delete.permission": "No tienes permiso para borrar \"{slug}\"", "error.page.draft.duplicate": "Ya existe un borrador de página con el apéndice de URL \"{slug}\"", "error.page.duplicate": "Ya existe una página con el apéndice de URL \"{slug}\"", "error.page.duplicate.permission": "No tienes permitido duplicar \"{slug}\"", + "error.page.move.ancestor": "The page cannot be moved into itself", + "error.page.move.directory": "The page directory cannot be moved", + "error.page.move.duplicate": "A sub page with the URL appendix \"{slug}\" already exists", + "error.page.move.noSections": "The page \"{parent}\" cannot be a parent of any page because it lacks any pages sections in its blueprint", + "error.page.move.notFound": "The moved page could not be found", + "error.page.move.permission": "You are not allowed to move \"{slug}\"", + "error.page.move.template": "The \"{template}\" template is not accepted as a subpage of \"{parent}\"", "error.page.notFound": "La página \"{slug}\" no se encuentra", "error.page.num.invalid": "Por favor, introduce un número de posición válido. Los números no deben ser negativos.", "error.page.slug.invalid": "Por favor, introduce un apéndice de URL válido", "error.page.slug.maxlength": "La longitud del slug debe ser inferior a \"{length}\" caracteres", "error.page.sort.permission": "La página \"{slug}\" no se puede ordenar", "error.page.status.invalid": "Por favor, establece una estado de página válido", - "error.page.undefined": "La página no fue encontrada", + "error.page.undefined": "La p\u00e1gina no fue encontrada", "error.page.update.permission": "No tienes permiso para actualizar \"{slug}\"", "error.section.files.max.plural": "No debes agregar más de {max} archivos a la sección \"{section}\"", @@ -163,6 +217,8 @@ "error.site.changeTitle.permission": "No tienes permiso para cambiar el título del sitio", "error.site.update.permission": "No tienes permiso de actualizar el sitio", + "error.structure.validation": "There's an error on the \"{field}\" field in row {index}", + "error.template.default.notFound": "La plantilla predeterminada no existe", "error.unexpected": "¡Se ha producido un error inesperado! Activa el modo de depuración para obtener más información: https://getkirby.com/docs/reference/system/options/debug", @@ -176,7 +232,7 @@ "error.user.changeRole.toAdmin": "No tienes permitido promover a alguien al rol de admin", "error.user.create.permission": "No tienes permiso de crear este usuario", "error.user.delete": "El ususario no pudo ser eliminado", - "error.user.delete.lastAdmin": "Usted no puede borrar el último administrador", + "error.user.delete.lastAdmin": "Usted no puede borrar el \u00faltimo administrador", "error.user.delete.lastUser": "El último usuario no puede ser borrado", "error.user.delete.permission": "Usted no tiene permitido borrar este usuario", "error.user.duplicate": "Ya existe un usuario con el email \"{email}\"", @@ -185,7 +241,7 @@ "error.user.notFound": "El usuario no pudo ser encontrado", "error.user.password.excessive": "Please enter a valid password. Passwords must not be longer than 1000 characters.", "error.user.password.invalid": "Por favor ingresa una contraseña valida. Las contraseñas deben tener al menos 8 caracteres de largo.", - "error.user.password.notSame": "Por favor confirma la contraseña", + "error.user.password.notSame": "Por favor confirma la contrase\u00f1a", "error.user.password.undefined": "El usuario no tiene contraseña", "error.user.password.wrong": "Contraseña incorrecta", "error.user.role.invalid": "Por favor ingresa un rol valido", @@ -195,8 +251,10 @@ "error.validation.accepted": "Por favor, confirma", "error.validation.alpha": "Por favor ingrese solo caracteres entre a-z", "error.validation.alphanum": "Por favor ingrese solo caracteres entre a-z o números entre 0-9", + "error.validation.anchor": "Please enter a correct link anchor", "error.validation.between": "Por favor ingrese valores entre \"{min}\" y \"{max}\"", "error.validation.boolean": "Por favor confirme o niegue", + "error.validation.color": "Please enter a valid color in the {format} format", "error.validation.contains": "Por favor ingrese valores que contengan \"{needle}\"", "error.validation.date": "Por favor ingresa una fecha válida", "error.validation.date.after": "Por favor introduce una fecha posterior a {date}", @@ -211,6 +269,7 @@ "error.validation.integer": "Por favor ingresa un entero válido", "error.validation.ip": "Por favor ingresa una dirección IP válida", "error.validation.less": "Por favor ingresa un valor menor a {max}", + "error.validation.linkType": "The link type is not allowed", "error.validation.match": "El valor no coincide con el patrón esperado", "error.validation.max": "Por favor ingresa un valor menor o igual a {max}", "error.validation.maxlength": "Por favor ingresa un valor mas corto. (max. {max} caracteres)", @@ -227,15 +286,18 @@ "error.validation.same": "Por favor ingresa \"{other}\"", "error.validation.size": "El tamaño del valor debe ser \"{size}\"", "error.validation.startswith": "El valor debe comenzar con \"{start}\"", + "error.validation.tel": "Please enter an unformatted phone number", "error.validation.time": "Por favor ingresa una hora válida", "error.validation.time.after": "Por favor ingresa una fecha después de {time}", "error.validation.time.before": "Por favor ingresa una fecha antes de {time}", "error.validation.time.between": "Por favor ingresa un fecha entre {min} y {max}", + "error.validation.uuid": "Please enter a valid UUID", "error.validation.url": "Por favor ingresa un URL válido", "expand": "Expandir", "expand.all": "Expandir todo", + "field.invalid": "The field is invalid", "field.required": "Este campo es requerido", "field.blocks.changeType": "Cambiar tipo", "field.blocks.code.name": "Código", @@ -245,8 +307,9 @@ "field.blocks.delete.confirm.all": "¿Seguro que quieres eliminar todos los bloques?", "field.blocks.delete.confirm.selected": "¿Seguro que quieres eliminar los bloques seleccionados?", "field.blocks.empty": "No hay bloques aún", + "field.blocks.fieldsets.empty": "No fieldsets yet", "field.blocks.fieldsets.label": "Por favor selecciona un tipo de bloque...", - "field.blocks.fieldsets.paste": "Presiona {{ shortcut }}para pegar/importar bloques en tu portapapeles ", + "field.blocks.fieldsets.paste": "Press {{ shortcut }} to import layouts/blocks from your clipboard Only those allowed in the current field will get inserted.", "field.blocks.gallery.name": "Galería", "field.blocks.gallery.images.empty": "No hay imágenes aún", "field.blocks.gallery.images.label": "Imágenes", @@ -254,11 +317,16 @@ "field.blocks.heading.name": "Encabezado", "field.blocks.heading.text": "Texto", "field.blocks.heading.placeholder": "Encabezado...", + "field.blocks.figure.back.plain": "Plain", + "field.blocks.figure.back.pattern.light": "Pattern (light)", + "field.blocks.figure.back.pattern.dark": "Pattern (dark)", "field.blocks.image.alt": "Texto alternativo", "field.blocks.image.caption": "Leyenda", "field.blocks.image.crop": "Cortar", "field.blocks.image.link": "Enlace", "field.blocks.image.location": "Ubicación", + "field.blocks.image.location.internal": "This website", + "field.blocks.image.location.external": "External source", "field.blocks.image.name": "Imágen", "field.blocks.image.placeholder": "Selecciona una imagen", "field.blocks.image.ratio": "Proporción", @@ -275,38 +343,72 @@ "field.blocks.quote.citation.placeholder": "Por ...", "field.blocks.text.name": "Texto", "field.blocks.text.placeholder": "Texto ...", + "field.blocks.video.autoplay": "Autoplay", "field.blocks.video.caption": "Leyenda", + "field.blocks.video.controls": "Controls", + "field.blocks.video.location": "Ubicación", + "field.blocks.video.loop": "Loop", + "field.blocks.video.muted": "Muted", "field.blocks.video.name": "Video", "field.blocks.video.placeholder": "Introduce la URL de un vídeo", + "field.blocks.video.poster": "Poster", + "field.blocks.video.preload": "Preload", "field.blocks.video.url.label": "Vídeo-URL", "field.blocks.video.url.placeholder": "https://youtube.com/?v=", - "field.files.empty": "Aún no ha seleccionado ningún archivo", + "field.entries.delete.confirm.all": "¿Realmente quieres eliminar todas las entradas?", + "field.entries.empty": "Aún no existen entradas.", + "field.files.empty": "Aún no ha seleccionado ningún archivo", + "field.files.empty.single": "No file selected yet", + + "field.layout.change": "Change layout", "field.layout.delete": "Eliminar layout", "field.layout.delete.confirm": "¿Realmente quieres eliminar este layout?", + "field.layout.delete.confirm.all": "Do you really want to delete all layouts?", "field.layout.empty": "Aún no hay filas", "field.layout.select": "Seleccionar layout", "field.object.empty": "Aún no hay información", "field.pages.empty": "Aún no ha seleccionado ningúna pagina", + "field.pages.empty.single": "No page selected yet", - "field.structure.delete.confirm": "¿En realidad desea borrar esta entrada?", + "field.structure.delete.confirm": "\u00bfEn realidad desea borrar esta entrada?", "field.structure.delete.confirm.all": "¿Realmente quieres eliminar todas las entradas?", - "field.structure.empty": "Aún no existen entradas.", + "field.structure.empty": "A\u00fan no existen entradas.", "field.users.empty": "Aún no ha seleccionado ningún usuario", + "field.users.empty.single": "No user selected yet", + "fields.empty": "No fields yet", + + "file": "Archivo", "file.blueprint": "Este archivo aún no tiene blueprint. Puedes definir la configuración en /site/blueprints/files/{blueprint}.yml", - "file.delete.confirm": "¿Estás seguro que deseas eliminar este archivo?", + "file.changeTemplate": "Cambiar plantilla", + "file.changeTemplate.notice": "Changing the file's template will remove content for fields that don't match in type. If the new template defines certain rules, e.g. image dimensions, those will also be applied irreversibly. Use with caution.", + "file.delete.confirm": "\u00bfEst\u00e1s seguro que deseas eliminar este archivo?", + "file.focus.placeholder": "Set focal point", + "file.focus.reset": "Remove focal point", + "file.focus.title": "Focus", "file.sort": "Cambiar posición", - "files": "Files", + "files": "Archivos", + "files.delete.confirm.selected": "Do you really want to delete the selected files? This action cannot be undone.", "files.empty": "Aún no existen archivos", + "filter": "Filter", + + "form.discard": "Discard changes", + "form.discard.confirm": "Do you really want to discard all your changes?", + "form.locked": "This content is disabled for you as it is currently edited by another user", + "form.unsaved": "The current changes have not yet been saved", + "form.preview": "Preview changes", + "form.preview.draft": "Preview draft", + "hide": "Ocultar", "hour": "Hora", + "hue": "Hue", "import": "Importar", "info": "Info", "insert": "Insertar", @@ -324,7 +426,6 @@ "installation.issues.mbstring": "Se requiere la extensión MB String.", "installation.issues.media": "La carpeta /media no existe o no posee permisos de escritura.", "installation.issues.php": "Asegurese de estar usando PHP 8+", - "installation.issues.server": "Kirby requiere Apache, Nginx, Caddy", "installation.issues.sessions": "La carpeta /site/sessions no existe o no posee permisos de escritura.", "language": "Idioma", @@ -332,6 +433,7 @@ "language.convert": "Hacer por defecto", "language.convert.confirm": "

Realmente deseas convertir {name} al idioma por defecto? Esta acción no se puede deshacer.

Si {name} tiene contenido sin traducir, no habrá vuelta atras y tu sitio puede quedar con partes sin contenido.

", "language.create": "Añadir nuevo idioma", + "language.default": "Idioma por defecto", "language.delete.confirm": "

", "language.deleted": "El idioma ha sido borrado", "language.direction": "Dirección de lectura", @@ -340,7 +442,16 @@ "language.locale": "Cadena de localización PHP", "language.locale.warning": "Estas utilizando un configuración local. Por favor modifícalo en el archivo del lenguaje en /site/languages", "language.name": "Nombre", + "language.secondary": "Secondary language", + "language.settings": "Language settings", "language.updated": "El idioma a sido actualizado", + "language.variables": "Language variables", + "language.variables.empty": "No translations yet", + + "language.variable.delete.confirm": "Do you really want to delete the variable for {key}?", + "language.variable.key": "Key", + "language.variable.notFound": "The variable could not be found", + "language.variable.value": "Value", "languages": "Idiomas", "languages.default": "Idioma por defecto", @@ -349,15 +460,32 @@ "languages.secondary.empty": "Todavía no hay idiomas secundarios", "license": "Licencia", + "license.activate": "Activate it now", + "license.activate.label": "Please activate your license", + "license.activate.domain": "Your license will be activated for {host}.", + "license.activate.local": "You are about to activate your Kirby license for your local domain {host}. If this site will be deployed to a public domain, please activate it there instead. If {host} is the domain you want to use your license for, please continue.", + "license.activated": "Activated", "license.buy": "Comprar una licencia", - "license.register": "Registrar", + "license.code": "Código", + "license.code.help": "You received your license code after the purchase via email. Please copy and paste it here.", + "license.code.label": "Por favor, ingresa tu código de licencia", + "license.status.active.info": "Includes new major versions until {date}", + "license.status.active.label": "Valid license", + "license.status.demo.info": "This is a demo installation", + "license.status.demo.label": "Demo", + "license.status.inactive.info": "Renew license to update to new major versions", + "license.status.inactive.label": "No new major versions", + "license.status.legacy.bubble": "Ready to renew your license?", + "license.status.legacy.info": "Your license does not cover this version", + "license.status.legacy.label": "Please renew your license", + "license.status.missing.bubble": "Ready to launch your site?", + "license.status.missing.info": "No valid license", + "license.status.missing.label": "Please activate your license", + "license.status.unknown.info": "The license status is unknown", + "license.status.unknown.label": "Unknown", "license.manage": "Gestiona tus licencias", - "license.register.help": "Recibió su código de licencia después de la compra por correo electrónico. Por favor copie y pegue para registrarse.", - "license.register.label": "Por favor, ingresa tu código de licencia", - "license.register.domain": "Tu licencia será registrada para {host}.", - "license.register.local": "Estás a punto de registrar tu licencia para el dominio local {host}. Si este sitio va a ser desplegado en un dominio público, por favor regístralo allí. Si {host} es el dominio en el que quiere registrar Kirby, por favor continúa.", - "license.register.success": "Gracias por apoyar a Kirby", - "license.unregistered": "Este es un demo no registrado de Kirby", + "license.purchased": "Purchased", + "license.success": "Gracias por apoyar a Kirby", "license.unregistered.label": "No registrado", "link": "Enlace", @@ -367,17 +495,21 @@ "lock.unsaved": "Cambios sin guardar", "lock.unsaved.empty": "No hay más cambios sin guardar", - "lock.isLocked": "Cambios sin guardar por {email}", - "lock.file.isLocked": "El archivo está siendo actualmente editado por {email} y no puede ser cambiado.", - "lock.page.isLocked": "La página está siendo actualmente editada por {email} y no puede ser cambiada.", + "lock.unsaved.files": "Unsaved files", + "lock.unsaved.pages": "Unsaved pages", + "lock.unsaved.users": "Unsaved accounts", + "lock.isLocked": "Unsaved changes by {email}", "lock.unlock": "Desbloquear", - "lock.isUnlocked": "Tus cambios sin guardar han sido sobrescritos por otro usuario. Puedes descargar los cambios y fusionarlos manualmente.", + "lock.unlock.submit": "Unlock and overwrite unsaved changes by {email}", + "lock.isUnlocked": "Was unlocked by another user", - "login": "Log in", + "login": "Iniciar sesión", "login.code.label.login": "Código de inicio de sesión", "login.code.label.password-reset": "Código de restablecimiento de contraseña", "login.code.placeholder.email": "000 000", + "login.code.placeholder.totp": "000000", "login.code.text.email": "Si tu dirección de correo electrónico está registrada, el código solicitado fue enviado por correo electrónico.", + "login.code.text.totp": "Please enter the one‑time code from your authenticator app.", "login.email.login.body": "Hola {user.nameOrEmail},\n\nHas pedido, recientemente, un código de restablecimiento de contraseña para el Panel del sitio {site}.\nEl siguiente código de restablecimiento de contraseña será válido por {timeout} minutos:\n\n{code}\n\nSi no pediste un código de restablecimiento de contraseña, por favor ignora este correo o contacta a tu administrador si tienes dudas.\nPor seguridad, por favor NO reenvíes este correo.", "login.email.login.subject": "Tu código de inicio de sesión", "login.email.password-reset.body": "Hola {user.nameOrEmail},\n\nHas pedido, recientemente, un código de restablecimiento de contraseña para el Panel del sitio {site}.\nEl siguiente código de restablecimiento de contraseña será válido por {timeout} minutos:\n\n{code}\n\nSi no pediste un código de restablecimiento de contraseña, por favor ignora este correo o contacta a tu administrador si tienes dudas.\nPor seguridad, por favor NO reenvíes este correo.", @@ -388,58 +520,80 @@ "login.toggleText.code.email-password": "Iniciar sesión con contraseña", "login.toggleText.password-reset.email": "¿Olvidaste tu contraseña?", "login.toggleText.password-reset.email-password": "← Volver al inicio de sesión", + "login.totp.enable.option": "Set up one‑time codes", + "login.totp.enable.intro": "Authenticator apps can generate one‑time codes that are used as a second factor when signing into your account.", + "login.totp.enable.qr.label": "1. Scan this QR code", + "login.totp.enable.qr.help": "Unable to scan? Add the setup key {secret} manually to your authenticator app.", + "login.totp.enable.confirm.headline": "2. Confirm with generated code", + "login.totp.enable.confirm.text": "Your app generates a new one‑time code every 30 seconds. Enter the current code to complete the setup:", + "login.totp.enable.confirm.label": "Current code", + "login.totp.enable.confirm.help": "After this setup, we will ask you for a one‑time code every time you log in.", + "login.totp.enable.success": "One‑time codes enabled", + "login.totp.disable.option": "Disable one‑time codes", + "login.totp.disable.label": "Enter your password to disable one‑time codes", + "login.totp.disable.help": "In the future, a different second factor like a login code sent via email will be requested when you log in. You can always set up one‑time codes again later.", + "login.totp.disable.admin": "

This will disable one‑time codes for {user}.

In the future, a different second factor like a login code sent via email will be requested when they log in. {user} can set up one‑time codes again after their next login.

", + "login.totp.disable.success": "One‑time codes disabled", - "logout": "Log out", + "logout": "Cerrar sesión", + "merge": "Merge", "menu": "Menù", "meridiem": "AM/PM", "mime": "Tipos de medios", "minutes": "Minutos", "month": "Mes", - "months.april": "April", - "months.august": "August", + "months.april": "Abril", + "months.august": "Agosto", "months.december": "Diciembre", "months.february": "Febrero", - "months.january": "January", - "months.july": "July", - "months.june": "June", - "months.march": "March", - "months.may": "May", + "months.january": "Enero", + "months.july": "Julio", + "months.june": "Junio", + "months.march": "Marzo", + "months.may": "Mayo", "months.november": "Noviembre", - "months.october": "October", - "months.september": "September", + "months.october": "Octubre", + "months.september": "Septiembre", "more": "Màs", + "move": "Move", "name": "Nombre", "next": "Siguiente", + "night": "Night", "no": "no", "off": "Apagado", "on": "Encendido", "open": "Abrir", "open.newWindow": "Abrir en una ventana nueva", + "option": "Option", "options": "Opciones", "options.none": "Sin opciones", + "options.all": "Show all {count} options", "orientation": "Orientación", "orientation.landscape": "Paisaje", "orientation.portrait": "Retrato", "orientation.square": "Diapositiva", + "page": "Página", "page.blueprint": "Este archivo aún no tiene blueprint. Puedes definir la configuración en /site/blueprints/pages/{blueprint}.yml", - "page.changeSlug": "Change URL", - "page.changeSlug.fromTitle": "Crear en base al título", + "page.changeSlug": "Cambiar URL", + "page.changeSlug.fromTitle": "Crear a partir del t\u00edtulo", "page.changeStatus": "Cambiar estado", "page.changeStatus.position": "Por favor selecciona una posición", "page.changeStatus.select": "Selecciona un nuevo estado", "page.changeTemplate": "Cambiar plantilla", + "page.changeTemplate.notice": "Changing the page's template will remove content for fields that don't match in type. Use with caution.", + "page.create": "Create as {status}", "page.delete.confirm": "¿Estás seguro que deseas eliminar {title}?", "page.delete.confirm.subpages": "Esta página tiene subpáginas.
Todas las súbpaginas serán eliminadas también.", "page.delete.confirm.title": "Introduce el título de la página para confirmar", - "page.draft.create": "Crear borrador", "page.duplicate.appendix": "Copiar", "page.duplicate.files": "Copiar archivos", "page.duplicate.pages": "Copiar páginas", + "page.move": "Move page", "page.sort": "Cambiar posición", "page.status": "Estado", "page.status.draft": "Borrador", @@ -449,25 +603,33 @@ "page.status.unlisted": "No publicada", "page.status.unlisted.description": "La página sólo es accesible vía URL", - "pages": "Paginas", + "pages": "Páginas", + "pages.delete.confirm.selected": "Do you really want to delete the selected pages? This action cannot be undone.", "pages.empty": "No hay páginas aún", "pages.status.draft": "Borradores", "pages.status.listed": "Publicado", - "pages.status.unlisted": "No publicada", + "pages.status.unlisted": "No publicado", "pagination.page": "Página", - "password": "Password", + "password": "Contrase\u00f1a", "paste": "Pegar", "paste.after": "Pegar después", + "paste.success": "{count} pasted!", "pixel": "Pixel", "plugin": "Plugin", "plugins": "Plugins", "prev": "Anterior", "preview": "Previsualizar", + + "publish": "Publish", + "published": "Publicado", + "remove": "Eliminar", "rename": "Renombrar", - "replace": "Remplazar", + "renew": "Renew", + "replace": "Reemplazar", + "replace.with": "Replace with", "retry": "Reintentar", "revert": "Revertir", "revert.confirm": "¿Realmente quieres eliminar todos los cambios sin guardar?", @@ -481,12 +643,15 @@ "role.nobody.description": "Este es un rol alternativo sin permisos", "role.nobody.title": "Nadie", - "save": "Save", + "save": "Guardar", + "saved": "Saved", "search": "Buscar", + "searching": "Searching", "search.min": "Introduce {min} caracteres para buscar", - "search.all": "Mostrar todo", + "search.all": "Show all {count} results", "search.results.none": "Sin resultados", + "section.invalid": "The section is invalid", "section.required": "Esta sección es requerida", "security": "Seguridad", @@ -498,16 +663,25 @@ "size": "Tamaño", "slug": "Apéndice URL", "sort": "Ordenar", + "sort.drag": "Drag to sort …", + "split": "Split", "stats.empty": "Sin informes", + "status": "Estado", + + "system.info.copy": "Copy info", + "system.info.copied": "System info copied", "system.issues.content": "La carpeta content parece estar expuesta", "system.issues.eol.kirby": "La versión de Kirby que tienes instalada ha llegado al final de su vida útil y no recibirá más actualizaciones de seguridad.", "system.issues.eol.plugin": "Tu versión instalada del plugin { plugin } ha llegado al final de su vida útil y no recibirá más actualizaciones de seguridad.", + "system.issues.eol.php": "Your installed PHP release { release } has reached end-of-life and will not receive further security updates", "system.issues.debug": "La depuración debe estar desactivada en producción", "system.issues.git": "La carpeta .git parece estar expuesta", "system.issues.https": "Recomendamos HTTPS para todos tus sitios web", "system.issues.kirby": "La carpeta kirby parece estar expuesta", + "system.issues.local": "The site is running locally with relaxed security checks", "system.issues.site": "La carpeta site parece estar expuesta", + "system.issues.vue.compiler": "The Vue template compiler is enabled", "system.issues.vulnerability.kirby": "Tu instalación podría estar afectada por la siguiente vulnerabilidad ({ severity } gravedad): { description }", "system.issues.vulnerability.plugin": "Tu instalación podría estar afectada por la siguiente vulnerabilidad en el plugin { plugin } ({ severity } gravedad): { description }", "system.updateStatus": "Estado de actualización", @@ -520,13 +694,22 @@ "system.updateStatus.update": "Actualización gratuita {version} disponible", "system.updateStatus.upgrade": "Actualización {versión} disponible", + "tel": "Phone", + "tel.placeholder": "+49123456789", + "template": "Plantilla", + + "theme": "Theme", + "theme.light": "Lights on", + "theme.dark": "Lights off", + "theme.automatic": "Match system default", + "title": "Título", - "template": "Template", "today": "Hoy", + "toolbar.button.clear": "Clear formatting", "toolbar.button.code": "Código", "toolbar.button.bold": "Negrita", - "toolbar.button.email": "Correo Electrónico", + "toolbar.button.email": "Email", "toolbar.button.headings": "Encabezados", "toolbar.button.heading.1": "Encabezado 1", "toolbar.button.heading.2": "Encabezado 2", @@ -534,13 +717,15 @@ "toolbar.button.heading.4": "Encabezado 4", "toolbar.button.heading.5": "Encabezado 5", "toolbar.button.heading.6": "Encabezado 6", - "toolbar.button.italic": "Texto en Itálicas", + "toolbar.button.italic": "Texto en It\u00e1licas", "toolbar.button.file": "Archivo", "toolbar.button.file.select": "Selecciona un archivo", "toolbar.button.file.upload": "Sube un archivo", "toolbar.button.link": "Enlace", "toolbar.button.paragraph": "Parágrafo", "toolbar.button.strike": "Tachado", + "toolbar.button.sub": "Subscript", + "toolbar.button.sup": "Superscript", "toolbar.button.ol": "Lista en orden", "toolbar.button.underline": "Subrayado", "toolbar.button.ul": "Lista de viñetas", @@ -550,6 +735,8 @@ "translation.name": "Español (América Latina)", "translation.locale": "es_419", + "type": "Type", + "upload": "Subir", "upload.error.cantMove": "El archivo subido no puede ser movido", "upload.error.cantWrite": "Error al escribir el archivo en el disco", @@ -574,23 +761,27 @@ "user.changeLanguage": "Cambiar idioma", "user.changeName": "Renombrar este usuario", "user.changePassword": "Cambiar la contraseña", - "user.changePassword.new": "New password", + "user.changePassword.current": "Your current password", + "user.changePassword.new": "Nueva contraseña", "user.changePassword.new.confirm": "Confirma la nueva contraseña...", "user.changeRole": "Cambiar rol", "user.changeRole.select": "Selecciona un nuevo rol", - "user.create": "Añadir un nuevo usuario", + "user.create": "Agregar un nuevo usuario", "user.delete": "Eliminar este usuario", "user.delete.confirm": "¿Estás seguro que deseas eliminar
{email}?", "users": "Usuarios", "version": "Versión", + "version.changes": "Changed version", + "version.compare": "Compare versions", "version.current": "Versión actual", "version.latest": "Última versión", "versionInformation": "información sobre la versión", + "view": "View", "view.account": "Tu cuenta", - "view.installation": "Instalación", + "view.installation": "Instalaci\u00f3n", "view.languages": "Idiomas", "view.resetPassword": "Restablecer contraseña", "view.site": "Sitio", diff --git a/kirby/i18n/translations/es_ES.json b/kirby/i18n/translations/es_ES.json index 6fd4762..c572229 100644 --- a/kirby/i18n/translations/es_ES.json +++ b/kirby/i18n/translations/es_ES.json @@ -3,30 +3,39 @@ "account.delete": "Borrar cuenta", "account.delete.confirm": "¿Realmente quieres eliminar tu cuenta? Tu sesión se cerrará inmediatamente. La cuenta no podrá ser recuperada.", - "add": "Add", + "activate": "Activate", + "add": "Añadir", + "alpha": "Alpha", "author": "Autor", - "avatar": "Profile picture", - "back": "Back", - "cancel": "Cancel", + "avatar": "Foto de perfil", + "back": "Atrás", + "cancel": "Cancelar", "change": "Cambiar", - "close": "Close", + "close": "Cerrar", + "changes": "Changes", "confirm": "Confirmar", "collapse": "Colapsar", "collapse.all": "Colapsar todo", + "color": "Color", + "coordinates": "Coordinates", "copy": "Copiar", "copy.all": "Copiar todo", + "copy.success": "{count} copied!", + "copy.success.multiple": "{count} copied!", + "copy.url": "Copy URL", "create": "Crear", + "custom": "Custom", - "date": "Date", + "date": "Fecha", "date.select": "Selecciona una fecha", "day": "Día", - "days.fri": "Vie", - "days.mon": "Mon", + "days.fri": "Vi", + "days.mon": "Lu", "days.sat": "Sá", - "days.sun": "Dom", - "days.thu": "Thu", - "days.tue": "Tue", + "days.sun": "Do", + "days.thu": "Ju", + "days.tue": "Ma", "days.wed": "Mi", "debugging": "Depuración", @@ -34,26 +43,35 @@ "delete": "Eliminar", "delete.all": "Eliminar todo", + "dialog.fields.empty": "This dialog has no fields", "dialog.files.empty": "No hay archivos para seleccionar", "dialog.pages.empty": "No hay páginas para seleccionar", + "dialog.text.empty": "This dialog does not define any text", "dialog.users.empty": "No hay usuarios para seleccionar", "dimensions": "Dimensiones", + "disable": "Disable", "disabled": "Desabilitado", - "discard": "Discard", + "discard": "Descartar", + + "drawer.fields.empty": "This drawer has no fields", + + "domain": "Domain", "download": "Descargar", "duplicate": "Duplicar", - "edit": "Edit", + "edit": "Editar", "email": "Correo electrónico", "email.placeholder": "correo@ejemplo.com", + "enter": "Enter", "entries": "Entradas", "entry": "Entrada", "environment": "Entorno", + "error": "Error", "error.access.code": "Código inválido", "error.access.login": "Inicio de sesión inválido", "error.access.panel": "No tienes permiso para acceder al panel", @@ -74,13 +92,31 @@ "error.cache.type.invalid": "Tipo de caché inválido \"{tipo}\"", + "error.content.lock.delete": "The version is locked and cannot be deleted", + "error.content.lock.move": "The source version is locked and cannot be moved", + "error.content.lock.publish": "This version is already published", + "error.content.lock.replace": "The version is locked and cannot be replaced", + "error.content.lock.update": "The version is locked and cannot be updated", + + "error.entries.max.plural": "You must not add more than {max} entries", + "error.entries.max.singular": "You must not add more than one entry", + "error.entries.min.plural": "You must add at least {min} entries", + "error.entries.min.singular": "You must add at least one entry", + "error.entries.supports": "\"{type}\" field type is not supported for the entries field", + "error.entries.validation": "There's an error on the \"{field}\" field in row {index}", + "error.email.preset.notFound": "El preset del correo \"{name}\" no puede ser encontrado", "error.field.converter.invalid": "Convertidor \"{converter}\" inválido", + "error.field.link.options": "Invalid options: {options}", "error.field.type.missing": "Campo \"{ name }\": El tipo de campo \"{ type }\" no existe", "error.file.changeName.empty": "El nombre no debe estar vacío", "error.file.changeName.permission": "No tienes permiso para cambiar el nombre de \"{filename}\"", + "error.file.changeTemplate.invalid": "The template for the file \"{id}\" cannot be changed to \"{template}\" (valid: \"{blueprints}\")", + "error.file.changeTemplate.permission": "You are not allowed to change the template for the file \"{id}\"", + + "error.file.delete.multiple": "Not all files could be deleted. Try each remaining file individually to see the specific error that prevents deletion.", "error.file.duplicate": "Ya existe un archivo con el nombre \"{filename}\"", "error.file.extension.forbidden": "La extensión \"{extension}\" no está permitida", "error.file.extension.invalid": "Extensión inválida: {extension}", @@ -95,9 +131,11 @@ "error.file.minheight": "La altura de la imagen debe ser de al menos {height} pixeles", "error.file.minsize": "El archivo es demasiado pequeño", "error.file.minwidth": "El ancho de la imagen debe ser de al menos {width} pixeles", + "error.file.name.unique": "The filename must be unique", "error.file.name.missing": "El nombre de archivo no debe estar vacío", "error.file.notFound": "El archivo \"{filename}\" no puede ser encontrado", "error.file.orientation": "La orientación de la imagen debe ser \"{orientation}", + "error.file.sort.permission": "You are not allowed to change the sorting of \"{filename}\"", "error.file.type.forbidden": "No tienes permiso para subir archivos {type}", "error.file.type.invalid": "Tipo de archivo inválido: {type}", "error.file.undefined": "El archivo no puede ser encontrado", @@ -106,22 +144,30 @@ "error.form.notSaved": "El formulario no pudo ser guardado", "error.language.code": "Por favor, introduce un código válido para el idioma", + "error.language.create.permission": "You are not allowed to create a language", + "error.language.delete.permission": "You are not allowed to delete the language", "error.language.duplicate": "El idioma ya existe", "error.language.name": "Por favor, introduce un nombre válido para el idioma", "error.language.notFound": "No se pudo encontrar el idioma", + "error.language.update.permission": "You are not allowed to update the language", "error.layout.validation.block": "Hay un error en el campo \"{field}\" del bloque {blockIndex} que utiliza el tipo de bloque \"{fieldset}\" en el layout {layoutIndex}", "error.layout.validation.settings": "Hay un error en los ajustes del layout {index}", - "error.license.format": "Por favor, introduce una llave de licencia válida", + "error.license.domain": "The domain for the license is missing", "error.license.email": "Por favor, introduce un correo electrónico válido", + "error.license.format": "Please enter a valid license code", "error.license.verification": "La licencia no pudo ser verificada", + "error.login.totp.confirm.invalid": "Código inválido", + "error.login.totp.confirm.missing": "Please enter the current code", + "error.object.validation": "Hay un error en el campo \"{label}\":\n{message}", "error.offline": "El Panel se encuentra actualmente fuera de línea ", "error.page.changeSlug.permission": "No tienes permiso para cambiar el apéndice de URL para \"{slug}\"", + "error.page.changeSlug.reserved": "The path of top-level pages must not start with \"{path}\"", "error.page.changeStatus.incomplete": "La página tiene errores y no puede ser publicada.", "error.page.changeStatus.permission": "El estado de esta página no se puede cambiar", "error.page.changeStatus.toDraft.invalid": "La página \"{slug}\" no se puede convertir a borrador", @@ -133,10 +179,18 @@ "error.page.delete": "La página \"{slug}\" no se puede eliminar", "error.page.delete.confirm": "Por favor, introduce el título de la página para confirmar", "error.page.delete.hasChildren": "La página tiene subpáginas y no se puede eliminar", + "error.page.delete.multiple": "Not all pages could be deleted. Try each remaining page individually to see the specific error that prevents deletion.", "error.page.delete.permission": "No tienes permiso para eliminar \"{slug}\"", "error.page.draft.duplicate": "Un borrador de página con el apéndice de URL \"{slug}\" ya existe", "error.page.duplicate": "Una página con el apéndice de URL \"{slug}\" ya existe", "error.page.duplicate.permission": "No tienes permiso para duplicar \"{slug}\"", + "error.page.move.ancestor": "The page cannot be moved into itself", + "error.page.move.directory": "The page directory cannot be moved", + "error.page.move.duplicate": "A sub page with the URL appendix \"{slug}\" already exists", + "error.page.move.noSections": "The page \"{parent}\" cannot be a parent of any page because it lacks any pages sections in its blueprint", + "error.page.move.notFound": "The moved page could not be found", + "error.page.move.permission": "You are not allowed to move \"{slug}\"", + "error.page.move.template": "The \"{template}\" template is not accepted as a subpage of \"{parent}\"", "error.page.notFound": "No se puede encontrar la página \"{slug}\"", "error.page.num.invalid": "Por favor, introduce un número de ordenación válido. Los números no deben ser negativos.", "error.page.slug.invalid": "Por favor, introduce un apéndice de URL válido", @@ -163,6 +217,8 @@ "error.site.changeTitle.permission": "No tienes permiso para cambiar el título del sitio", "error.site.update.permission": "No tienes permiso para actualizar el sitio", + "error.structure.validation": "There's an error on the \"{field}\" field in row {index}", + "error.template.default.notFound": "La plantilla por defecto no existe", "error.unexpected": "¡Se ha producido un error inesperado! Activa el modo de depuración para obtener más información: https://getkirby.com/docs/reference/system/options/debug", @@ -180,7 +236,7 @@ "error.user.delete.lastUser": "No se puede eliminar el último usuario ", "error.user.delete.permission": "No tienes permiso para eliminar el usuario \"{name}\"", "error.user.duplicate": "Un usuario con la dirección de correo electrónico \"{email}\" ya existe", - "error.user.email.invalid": "Por favor, introduce un correo electrónico válido", + "error.user.email.invalid": "Por favor, introduce una dirección de correo electrónico válida", "error.user.language.invalid": "Por favor, introduce un idioma válido", "error.user.notFound": "No se puede encontrar el usuario \"{name}\"", "error.user.password.excessive": "Please enter a valid password. Passwords must not be longer than 1000 characters.", @@ -195,8 +251,10 @@ "error.validation.accepted": "Por favor, confirma", "error.validation.alpha": "Por favor, introduce solo caracteres entre a-z", "error.validation.alphanum": "Por favor, introduce solo caracteres entre a-z o numerales 0-9", + "error.validation.anchor": "Please enter a correct link anchor", "error.validation.between": "Por favor, introduce un valor entre \"{min}\" y \"{max}\"", "error.validation.boolean": "Por favor, confirma o rechaza", + "error.validation.color": "Please enter a valid color in the {format} format", "error.validation.contains": "Por favor, introduce un valor que contenga \"{needle}\"", "error.validation.date": "Por favor, introduce una fecha válida", "error.validation.date.after": "Por favor, introduce una fecha posterior a {date}", @@ -211,6 +269,7 @@ "error.validation.integer": "Por favor, introduce un numero integro válido", "error.validation.ip": "Por favor, introduce una dirección IP válida", "error.validation.less": "Por favor, introduce un valor inferior a {max}", + "error.validation.linkType": "The link type is not allowed", "error.validation.match": "El valor no coincide con el patrón esperado", "error.validation.max": "Por favor, introduce un valor igual o inferior a {max}", "error.validation.maxlength": "Por favor, introduce un valor más corto. (max. {max} caracteres)", @@ -227,15 +286,18 @@ "error.validation.same": "Por favor, introduce \"{other}\"", "error.validation.size": "El tamaño del valor debe ser \"{size}\"", "error.validation.startswith": "El valor debe comenzar con \"{start}\"", + "error.validation.tel": "Please enter an unformatted phone number", "error.validation.time": "Por favor, introduce una hora válida", "error.validation.time.after": "Por favor, introduce una fecha después de {time}", "error.validation.time.before": "Por favor, introduce una fecha antes de {time}", "error.validation.time.between": "Por favor, introduce un fecha entre {min} y {max}", + "error.validation.uuid": "Please enter a valid UUID", "error.validation.url": "Por favor, introduce un URL válido", "expand": "Expandir", "expand.all": "Expandir todo", + "field.invalid": "The field is invalid", "field.required": "Este campo es obligatorio", "field.blocks.changeType": "Cambiar tipo", "field.blocks.code.name": "Código", @@ -245,8 +307,9 @@ "field.blocks.delete.confirm.all": "¿Realmente quieres eliminar todos los bloques?", "field.blocks.delete.confirm.selected": "¿Realmente quieres eliminar los bloques seleccionados?", "field.blocks.empty": "Aún no hay bloques", + "field.blocks.fieldsets.empty": "No fieldsets yet", "field.blocks.fieldsets.label": "Por favor, selecciona un tipo de bloque...", - "field.blocks.fieldsets.paste": "Presiona {{ shortcut }}para pegar/importar bloques en tu portapapeles ", + "field.blocks.fieldsets.paste": "Press {{ shortcut }} to import layouts/blocks from your clipboard Only those allowed in the current field will get inserted.", "field.blocks.gallery.name": "Galería", "field.blocks.gallery.images.empty": "Aún no hay imágenes", "field.blocks.gallery.images.label": "Imágenes", @@ -254,11 +317,16 @@ "field.blocks.heading.name": "Encabezado", "field.blocks.heading.text": "Texto", "field.blocks.heading.placeholder": "Encabezado...", + "field.blocks.figure.back.plain": "Plain", + "field.blocks.figure.back.pattern.light": "Pattern (light)", + "field.blocks.figure.back.pattern.dark": "Pattern (dark)", "field.blocks.image.alt": "Texto alternativo", "field.blocks.image.caption": "Leyenda", "field.blocks.image.crop": "Cortar", "field.blocks.image.link": "Enlace", "field.blocks.image.location": "Ubicación", + "field.blocks.image.location.internal": "This website", + "field.blocks.image.location.external": "External source", "field.blocks.image.name": "Imágen", "field.blocks.image.placeholder": "Selecciona una imagen", "field.blocks.image.ratio": "Proporción", @@ -275,38 +343,72 @@ "field.blocks.quote.citation.placeholder": "Por ...", "field.blocks.text.name": "Texto", "field.blocks.text.placeholder": "Texto ...", + "field.blocks.video.autoplay": "Autoplay", "field.blocks.video.caption": "Leyenda", + "field.blocks.video.controls": "Controls", + "field.blocks.video.location": "Ubicación", + "field.blocks.video.loop": "Loop", + "field.blocks.video.muted": "Muted", "field.blocks.video.name": "Video", "field.blocks.video.placeholder": "Introduce la URL de un vídeo", + "field.blocks.video.poster": "Poster", + "field.blocks.video.preload": "Preload", "field.blocks.video.url.label": "Vídeo-URL", "field.blocks.video.url.placeholder": "https://youtube.com/?v=", - "field.files.empty": "Aún no hay archivos seleccionados", + "field.entries.delete.confirm.all": "¿Realmente quieres eliminar todas las entradas?", + "field.entries.empty": "Aún no hay entradas", + "field.files.empty": "Aún no hay archivos seleccionados", + "field.files.empty.single": "No file selected yet", + + "field.layout.change": "Change layout", "field.layout.delete": "Eliminar layout", "field.layout.delete.confirm": "¿Realmente quieres eliminar este layout?", + "field.layout.delete.confirm.all": "Do you really want to delete all layouts?", "field.layout.empty": "Aún no hay filas", "field.layout.select": "Seleccionar layout", "field.object.empty": "Aún no hay información", "field.pages.empty": "Aún no hay páginas seleccionadas", + "field.pages.empty.single": "No page selected yet", "field.structure.delete.confirm": "¿Realmente quieres eliminar esta fila?", "field.structure.delete.confirm.all": "¿Realmente quieres eliminar todas las entradas?", "field.structure.empty": "Aún no hay entradas", "field.users.empty": "Aún no hay usuarios seleccionados", + "field.users.empty.single": "No user selected yet", + "fields.empty": "No fields yet", + + "file": "Archivo", "file.blueprint": "Este archivo aún no tiene blueprint. Puedes definir la configuración en /site/blueprints/files/{blueprint}.yml", + "file.changeTemplate": "Cambiar plantilla", + "file.changeTemplate.notice": "Changing the file's template will remove content for fields that don't match in type. If the new template defines certain rules, e.g. image dimensions, those will also be applied irreversibly. Use with caution.", "file.delete.confirm": "¿Realmente quieres eliminar
{filename}?", + "file.focus.placeholder": "Set focal point", + "file.focus.reset": "Remove focal point", + "file.focus.title": "Focus", "file.sort": "Cambiar posición", - "files": "Files", + "files": "Archivos", + "files.delete.confirm.selected": "Do you really want to delete the selected files? This action cannot be undone.", "files.empty": "Aún no hay archivos", + "filter": "Filter", + + "form.discard": "Discard changes", + "form.discard.confirm": "Do you really want to discard all your changes?", + "form.locked": "This content is disabled for you as it is currently edited by another user", + "form.unsaved": "The current changes have not yet been saved", + "form.preview": "Preview changes", + "form.preview.draft": "Preview draft", + "hide": "Ocultar", "hour": "Hora", + "hue": "Hue", "import": "Importar", "info": "Info", "insert": "Insertar", @@ -324,7 +426,6 @@ "installation.issues.mbstring": "La extension MB String es requerida", "installation.issues.media": "La carpeta /media no existe o no se puede escribir", "installation.issues.php": "Asegurese de estar usando PHP 8+", - "installation.issues.server": "Kirby requiere Apache, Nginx o Caddy", "installation.issues.sessions": "La carpeta /site/sessions no existe o no se puede escribir", "language": "Idioma", @@ -332,6 +433,7 @@ "language.convert": "Hacer por defecto", "language.convert.confirm": "

¿Realmente quieres convertir {name} al idioma por defecto? Esto no se puede deshacer.

Si {name} tiene contenido sin traducir, ya no habrá un respaldo válido y algunas partes de tu sitio podrían estar vacías.

", "language.create": "Añadir un nuevo idioma", + "language.default": "Idioma predeterminado", "language.delete.confirm": "¿Realmente quieres eliminar el idioma {name} incluyendo todas las traducciones? ¡Esto no se puede deshacer!", "language.deleted": "El idioma ha sido eliminado", "language.direction": "Leyendo dirección", @@ -340,7 +442,16 @@ "language.locale": "PHP locale string", "language.locale.warning": "Estás utilizando una configuración local. Por favor, modifícalo en el archivo del lenguaje en /site/languages", "language.name": "Nombre", + "language.secondary": "Secondary language", + "language.settings": "Language settings", "language.updated": "El idioma ha sido actualizado", + "language.variables": "Language variables", + "language.variables.empty": "No translations yet", + + "language.variable.delete.confirm": "Do you really want to delete the variable for {key}?", + "language.variable.key": "Key", + "language.variable.notFound": "The variable could not be found", + "language.variable.value": "Value", "languages": "Idiomas", "languages.default": "Idioma predeterminado", @@ -349,35 +460,56 @@ "languages.secondary.empty": "Aún no hay idiomas secundarios", "license": "Licencia", + "license.activate": "Activate it now", + "license.activate.label": "Please activate your license", + "license.activate.domain": "Your license will be activated for {host}.", + "license.activate.local": "You are about to activate your Kirby license for your local domain {host}. If this site will be deployed to a public domain, please activate it there instead. If {host} is the domain you want to use your license for, please continue.", + "license.activated": "Activated", "license.buy": "Comprar una licencia", - "license.register": "Registro", + "license.code": "Código", + "license.code.help": "You received your license code after the purchase via email. Please copy and paste it here.", + "license.code.label": "Por favor, introduce tu código de licencia", + "license.status.active.info": "Includes new major versions until {date}", + "license.status.active.label": "Valid license", + "license.status.demo.info": "This is a demo installation", + "license.status.demo.label": "Demo", + "license.status.inactive.info": "Renew license to update to new major versions", + "license.status.inactive.label": "No new major versions", + "license.status.legacy.bubble": "Ready to renew your license?", + "license.status.legacy.info": "Your license does not cover this version", + "license.status.legacy.label": "Please renew your license", + "license.status.missing.bubble": "Ready to launch your site?", + "license.status.missing.info": "No valid license", + "license.status.missing.label": "Please activate your license", + "license.status.unknown.info": "The license status is unknown", + "license.status.unknown.label": "Unknown", "license.manage": "Gestiona licencias", - "license.register.help": "Recibiste tu código de licencia tras la compra por correo electrónico. Por favor, copia y pega para registrarte.", - "license.register.label": "Por favor, introduce tu código de licencia", - "license.register.domain": "Tu licencia será registrada para {host}.", - "license.register.local": "Estás a punto de registrar tu licencia para el dominio local {host}. Si este sitio va a ser desplegado en un dominio público, por favor regístralo allí. Si {host} es el dominio en el que quiere registrar Kirby, por favor continúa.", - "license.register.success": "Gracias por apoyar a Kirby", - "license.unregistered": "Esta es una demo no registrada de Kirby", + "license.purchased": "Purchased", + "license.success": "Gracias por apoyar a Kirby", "license.unregistered.label": "No registrado", "link": "Enlace", - "link.text": "Texto de Enlace", + "link.text": "Texto del enlace", "loading": "Cargando", "lock.unsaved": "Cambios sin guardar", "lock.unsaved.empty": "No hay más cambios sin guardar", - "lock.isLocked": "Cambios sin guardar por {email}", - "lock.file.isLocked": "El archivo está siendo actualmente editado por {email} y no puede ser cambiado.", - "lock.page.isLocked": "La página está siendo actualmente editada por {email} y no puede ser cambiada.", + "lock.unsaved.files": "Unsaved files", + "lock.unsaved.pages": "Unsaved pages", + "lock.unsaved.users": "Unsaved accounts", + "lock.isLocked": "Unsaved changes by {email}", "lock.unlock": "Desbloquear", - "lock.isUnlocked": "Tus cambios sin guardar han sido sobrescritos por otro usuario. Puedes descargar los cambios y fusionarlos manualmente.", + "lock.unlock.submit": "Unlock and overwrite unsaved changes by {email}", + "lock.isUnlocked": "Was unlocked by another user", - "login": "Log in", + "login": "Iniciar sesión", "login.code.label.login": "Código de inicio de sesión", "login.code.label.password-reset": "Código de restablecimiento de contraseña", "login.code.placeholder.email": "000 000", + "login.code.placeholder.totp": "000000", "login.code.text.email": "Si tu dirección de correo electrónico está registrada, el código solicitado fue enviado por correo electrónico.", + "login.code.text.totp": "Please enter the one‑time code from your authenticator app.", "login.email.login.body": "Hola {user.nameOrEmail},\n\nHas pedido, recientemente, un código de restablecimiento de contraseña para el Panel del sitio {site}.\nEl siguiente código de restablecimiento de contraseña será válido por {timeout} minutos:\n\n{code}\n\nSi no pediste un código de restablecimiento de contraseña, por favor ignora este correo o contacta a tu administrador si tienes dudas.\nPor seguridad, por favor NO reenvíes este correo.", "login.email.login.subject": "Tu código de inicio de sesión", "login.email.password-reset.body": "Hola {user.nameOrEmail},\n\nHas pedido, recientemente, un código de restablecimiento de contraseña para el Panel del sitio {site}.\nEl siguiente código de restablecimiento de contraseña será válido por {timeout} minutos:\n\n{code}\n\nSi no pediste un código de restablecimiento de contraseña, por favor ignora este correo o contacta a tu administrador si tienes dudas.\nPor seguridad, por favor NO reenvíes este correo.", @@ -388,58 +520,80 @@ "login.toggleText.code.email-password": "Iniciar sesión con contraseña", "login.toggleText.password-reset.email": "¿Olvidaste tu contraseña?", "login.toggleText.password-reset.email-password": "← Volver al inicio de sesión", + "login.totp.enable.option": "Set up one‑time codes", + "login.totp.enable.intro": "Authenticator apps can generate one‑time codes that are used as a second factor when signing into your account.", + "login.totp.enable.qr.label": "1. Scan this QR code", + "login.totp.enable.qr.help": "Unable to scan? Add the setup key {secret} manually to your authenticator app.", + "login.totp.enable.confirm.headline": "2. Confirm with generated code", + "login.totp.enable.confirm.text": "Your app generates a new one‑time code every 30 seconds. Enter the current code to complete the setup:", + "login.totp.enable.confirm.label": "Current code", + "login.totp.enable.confirm.help": "After this setup, we will ask you for a one‑time code every time you log in.", + "login.totp.enable.success": "One‑time codes enabled", + "login.totp.disable.option": "Disable one‑time codes", + "login.totp.disable.label": "Enter your password to disable one‑time codes", + "login.totp.disable.help": "In the future, a different second factor like a login code sent via email will be requested when you log in. You can always set up one‑time codes again later.", + "login.totp.disable.admin": "

This will disable one‑time codes for {user}.

In the future, a different second factor like a login code sent via email will be requested when they log in. {user} can set up one‑time codes again after their next login.

", + "login.totp.disable.success": "One‑time codes disabled", - "logout": "Log out", + "logout": "Cerrar sesión", + "merge": "Merge", "menu": "Menu", "meridiem": "AM/PM", "mime": "Tipos de medios", "minutes": "Minutos", "month": "Mes", - "months.april": "April", - "months.august": "August", + "months.april": "Abril", + "months.august": "Agosto", "months.december": "Diciembre", "months.february": "Febrero", - "months.january": "January", - "months.july": "July", - "months.june": "June", - "months.march": "March", - "months.may": "May", + "months.january": "Enero", + "months.july": "Julio", + "months.june": "Junio", + "months.march": "Marzo", + "months.may": "Mayo", "months.november": "Noviembre", - "months.october": "October", - "months.september": "September", + "months.october": "Octubre", + "months.september": "Septiembre", "more": "Más", + "move": "Move", "name": "Nombre", "next": "Siguiente", + "night": "Night", "no": "no", "off": "Apagado", "on": "Encendido", "open": "Abrir", "open.newWindow": "Abrir en una ventana nueva", + "option": "Option", "options": "Opciones", "options.none": "Sin opciones", + "options.all": "Show all {count} options", "orientation": "Orientación", "orientation.landscape": "Paisaje", "orientation.portrait": "Retrato", "orientation.square": "Cuadrado", + "page": "Página", "page.blueprint": "Este archivo aún no tiene blueprint. Puedes definir la configuración en /site/blueprints/pages/{blueprint}.yml", - "page.changeSlug": "Change URL", + "page.changeSlug": "Cambiar URL", "page.changeSlug.fromTitle": "Crear en base al título", "page.changeStatus": "Cambiar estado", "page.changeStatus.position": "Por favor, selecciona una posición", "page.changeStatus.select": "Selecciona un nuevo estado", "page.changeTemplate": "Cambiar plantilla", + "page.changeTemplate.notice": "Changing the page's template will remove content for fields that don't match in type. Use with caution.", + "page.create": "Create as {status}", "page.delete.confirm": "¿Realmente quieres eliminar {title}?", "page.delete.confirm.subpages": "Esta página tiene subpáginas.
Todas las subpáginas también serán eliminadas.", "page.delete.confirm.title": "Introduce el título de la página para confirmar", - "page.draft.create": "Crear borrador", "page.duplicate.appendix": "Copiar", "page.duplicate.files": "Copiar archivos", "page.duplicate.pages": "Copiar páginas", + "page.move": "Move page", "page.sort": "Cambiar posición", "page.status": "Estado", "page.status.draft": "Borrador", @@ -450,6 +604,7 @@ "page.status.unlisted.description": "La página solo es accesible vía URL", "pages": "Paginas", + "pages.delete.confirm.selected": "Do you really want to delete the selected pages? This action cannot be undone.", "pages.empty": "Aún no hay páginas", "pages.status.draft": "Borradores", "pages.status.listed": "Publicadas", @@ -457,17 +612,24 @@ "pagination.page": "Página", - "password": "Password", + "password": "Contraseña", "paste": "Pegar", "paste.after": "Pegar después", + "paste.success": "{count} pasted!", "pixel": "Pixel", "plugin": "Plugin", "plugins": "Plugins", "prev": "Anterior", "preview": "Previsualizar", + + "publish": "Publish", + "published": "Publicadas", + "remove": "Eliminar", "rename": "Renombrar", + "renew": "Renew", "replace": "Remplazar", + "replace.with": "Replace with", "retry": "Inténtalo de nuevo", "revert": "Revertir", "revert.confirm": "¿Realmente quieres eliminar todos los cambios sin guardar?", @@ -481,12 +643,15 @@ "role.nobody.description": "Este es un rol alternativo sin permisos", "role.nobody.title": "Nadie", - "save": "Save", + "save": "Guardar", + "saved": "Saved", "search": "Buscar", + "searching": "Searching", "search.min": "Introduce {min} caracteres para buscar", - "search.all": "Mostrar todo", + "search.all": "Show all {count} results", "search.results.none": "Sin resultados", + "section.invalid": "The section is invalid", "section.required": "Esta sección es obligatoria", "security": "Seguridad", @@ -498,16 +663,25 @@ "size": "Tamaño", "slug": "Apéndice de URL", "sort": "Ordenar", + "sort.drag": "Drag to sort …", + "split": "Split", "stats.empty": "Sin informes", + "status": "Estado", + + "system.info.copy": "Copy info", + "system.info.copied": "System info copied", "system.issues.content": "La carpeta content parece estar expuesta", "system.issues.eol.kirby": "La versión de Kirby que tienes instalada ha llegado al final de su vida útil y no recibirá más actualizaciones de seguridad.", "system.issues.eol.plugin": "La versión del plugin { plugin } que tienes instalada ha llegado al final de su vida útil y no recibirá más actualizaciones de seguridad.", + "system.issues.eol.php": "Your installed PHP release { release } has reached end-of-life and will not receive further security updates", "system.issues.debug": "La depuración debe estar desactivada en producción", "system.issues.git": "La carpeta .git parece estar expuesta", "system.issues.https": "Recomendamos HTTPS para todos tus sitios web", "system.issues.kirby": "La carpeta kirby parece estar expuesta", + "system.issues.local": "The site is running locally with relaxed security checks", "system.issues.site": "La carpeta site parece estar expuesta", + "system.issues.vue.compiler": "The Vue template compiler is enabled", "system.issues.vulnerability.kirby": "Tu instalación podría estar afectada por la siguiente vulnerabilidad ({ severity } gravedad): { description }", "system.issues.vulnerability.plugin": "Tu instalación podría estar afectada por la siguiente vulnerabilidad en el plugin { plugin } ({ severity } gravedad): { description }", "system.updateStatus": "Estado de actualización", @@ -520,10 +694,19 @@ "system.updateStatus.update": "Actualización gratuita {version} disponible", "system.updateStatus.upgrade": "Actualización {versión} disponible", + "tel": "Phone", + "tel.placeholder": "+49123456789", + "template": "Plantilla", + + "theme": "Theme", + "theme.light": "Lights on", + "theme.dark": "Lights off", + "theme.automatic": "Match system default", + "title": "Título", - "template": "Template", "today": "Hoy", + "toolbar.button.clear": "Clear formatting", "toolbar.button.code": "Código", "toolbar.button.bold": "Negrita", "toolbar.button.email": "Correo electrónico", @@ -541,6 +724,8 @@ "toolbar.button.link": "Enlace", "toolbar.button.paragraph": "Parágrafo", "toolbar.button.strike": "Tachado", + "toolbar.button.sub": "Subscript", + "toolbar.button.sup": "Superscript", "toolbar.button.ol": "Lista ordenada", "toolbar.button.underline": "Subrayado", "toolbar.button.ul": "Lista de viñetas", @@ -550,6 +735,8 @@ "translation.name": "Español", "translation.locale": "es_ES", + "type": "Type", + "upload": "Subir", "upload.error.cantMove": "El archivo subido no pudo ser movido", "upload.error.cantWrite": "Error al escribir el archivo en el disco", @@ -574,7 +761,8 @@ "user.changeLanguage": "Cambiar idioma", "user.changeName": "Renombrar a este usuario", "user.changePassword": "Cambiar contraseña", - "user.changePassword.new": "New password", + "user.changePassword.current": "Your current password", + "user.changePassword.new": "Nueva contraseña", "user.changePassword.new.confirm": "Confirmar nueva contraseña…", "user.changeRole": "Cambiar rol", "user.changeRole.select": "Seleccionar un nuevo rol", @@ -585,10 +773,13 @@ "users": "Usuarios", "version": "Versión", + "version.changes": "Changed version", + "version.compare": "Compare versions", "version.current": "Versión actual", "version.latest": "Última versión", "versionInformation": "Información sobre la versión", + "view": "View", "view.account": "Tu cuenta", "view.installation": "Instalación", "view.languages": "Idiomas", diff --git a/kirby/i18n/translations/fa.json b/kirby/i18n/translations/fa.json index 6086cd0..3009ca6 100644 --- a/kirby/i18n/translations/fa.json +++ b/kirby/i18n/translations/fa.json @@ -3,64 +3,82 @@ "account.delete": "Delete your account", "account.delete.confirm": "Do you really want to delete your account? You will be logged out immediately. Your account cannot be recovered.", - "add": "افزودن", + "activate": "Activate", + "add": "\u0627\u0641\u0632\u0648\u062f\u0646", + "alpha": "Alpha", "author": "Author", - "avatar": "تصویر پروفایل", + "avatar": "\u062a\u0635\u0648\u06cc\u0631 \u067e\u0631\u0648\u0641\u0627\u06cc\u0644", "back": "بازگشت", - "cancel": "انصراف", - "change": "اصلاح", - "close": "بستن", + "cancel": "\u0627\u0646\u0635\u0631\u0627\u0641", + "change": "\u0627\u0635\u0644\u0627\u062d", + "close": "\u0628\u0633\u062a\u0646", + "changes": "Changes", "confirm": "تایید", "collapse": "Collapse", "collapse.all": "Collapse All", + "color": "Color", + "coordinates": "Coordinates", "copy": "کپی", "copy.all": "Copy all", + "copy.success": "{count} copied!", + "copy.success.multiple": "{count} copied!", + "copy.url": "Copy URL", "create": "ایجاد", + "custom": "Custom", "date": "تاریخ", "date.select": "یک تاریخ را انتخاب کنید", "day": "روز", - "days.fri": "جمعه", - "days.mon": "دوشنبه", - "days.sat": "شنبه", - "days.sun": "یکشنبه", - "days.thu": "پنجشنبه", - "days.tue": "سه شنبه", - "days.wed": "چهارشنبه", + "days.fri": "\u062c\u0645\u0639\u0647", + "days.mon": "\u062f\u0648\u0634\u0646\u0628\u0647", + "days.sat": "\u0634\u0646\u0628\u0647", + "days.sun": "\u06cc\u06a9\u0634\u0646\u0628\u0647", + "days.thu": "\u067e\u0646\u062c\u0634\u0646\u0628\u0647", + "days.tue": "\u0633\u0647 \u0634\u0646\u0628\u0647", + "days.wed": "\u0686\u0647\u0627\u0631\u0634\u0646\u0628\u0647", "debugging": "Debugging", - "delete": "حذف", + "delete": "\u062d\u0630\u0641", "delete.all": "Delete all", + "dialog.fields.empty": "This dialog has no fields", "dialog.files.empty": "No files to select", "dialog.pages.empty": "No pages to select", + "dialog.text.empty": "This dialog does not define any text", "dialog.users.empty": "No users to select", "dimensions": "ابعاد", + "disable": "Disable", "disabled": "Disabled", - "discard": "انصراف", + "discard": "\u0627\u0646\u0635\u0631\u0627\u0641", + + "drawer.fields.empty": "This drawer has no fields", + + "domain": "Domain", "download": "Download", "duplicate": "Duplicate", - "edit": "ویرایش", + "edit": "\u0648\u06cc\u0631\u0627\u06cc\u0634", - "email": "پست الکترونیک", + "email": "\u067e\u0633\u062a \u0627\u0644\u06a9\u062a\u0631\u0648\u0646\u06cc\u06a9", "email.placeholder": "mail@example.com", + "enter": "Enter", "entries": "Entries", "entry": "Entry", "environment": "Environment", + "error": "Error", "error.access.code": "Invalid code", "error.access.login": "اطلاعات ورودی نامعتبر است", "error.access.panel": "شما اجازه دسترسی به پانل را ندارید", "error.access.view": "You are not allowed to access this part of the panel", "error.avatar.create.fail": "بارگزاری تصویر پروفایل موفق نبود", - "error.avatar.delete.fail": "تصویر پروفایل را نمیتوان حذف کرد", + "error.avatar.delete.fail": "\u062a\u0635\u0648\u06cc\u0631 \u067e\u0631\u0648\u0641\u0627\u06cc\u0644 \u0631\u0627 \u0646\u0645\u06cc\u062a\u0648\u0627\u0646 \u062d\u0630\u0641 \u06a9\u0631\u062f", "error.avatar.dimensions.invalid": "لطفا طول و عرض تصویر پروفایل را زیر 3000 پیکسل انتخاب کنید", "error.avatar.mime.forbidden": "تصویر پروفایل باید از نوع JPEG یا PNG باشد", @@ -74,17 +92,35 @@ "error.cache.type.invalid": "Invalid cache type \"{type}\"", + "error.content.lock.delete": "The version is locked and cannot be deleted", + "error.content.lock.move": "The source version is locked and cannot be moved", + "error.content.lock.publish": "This version is already published", + "error.content.lock.replace": "The version is locked and cannot be replaced", + "error.content.lock.update": "The version is locked and cannot be updated", + + "error.entries.max.plural": "You must not add more than {max} entries", + "error.entries.max.singular": "You must not add more than one entry", + "error.entries.min.plural": "You must add at least {min} entries", + "error.entries.min.singular": "You must add at least one entry", + "error.entries.supports": "\"{type}\" field type is not supported for the entries field", + "error.entries.validation": "There's an error on the \"{field}\" field in row {index}", + "error.email.preset.notFound": "قالب ایمیل «{name}» پیدا نشد", "error.field.converter.invalid": "مبدل «{converter}» نامعتبر است", + "error.field.link.options": "Invalid options: {options}", "error.field.type.missing": "Field \"{ name }\": The field type \"{ type }\" does not exist", "error.file.changeName.empty": "The name must not be empty", "error.file.changeName.permission": "شما اجازه تنغییر نام فایل «{filename}» را ندارید", + "error.file.changeTemplate.invalid": "The template for the file \"{id}\" cannot be changed to \"{template}\" (valid: \"{blueprints}\")", + "error.file.changeTemplate.permission": "You are not allowed to change the template for the file \"{id}\"", + + "error.file.delete.multiple": "Not all files could be deleted. Try each remaining file individually to see the specific error that prevents deletion.", "error.file.duplicate": "فایلی هم نام با «{filename}» هم اکنون موجود است", "error.file.extension.forbidden": "پسوند فایل «{extension}» غیرمجاز است", "error.file.extension.invalid": "Invalid extension: {extension}", - "error.file.extension.missing": "شما نمی‌توانید فایل‌های بدون پسوند را آپلود کنید", + "error.file.extension.missing": "\u0634\u0645\u0627 \u0646\u0645\u06cc\u200c\u062a\u0648\u0627\u0646\u06cc\u062f \u0641\u0627\u06cc\u0644\u200c\u0647\u0627\u06cc \u0628\u062f\u0648\u0646 \u067e\u0633\u0648\u0646\u062f \u0631\u0627 \u0622\u067e\u0644\u0648\u062f \u06a9\u0646\u06cc\u062f", "error.file.maxheight": "The height of the image must not exceed {height} pixels", "error.file.maxsize": "The file is too large", "error.file.maxwidth": "The width of the image must not exceed {width} pixels", @@ -95,33 +131,43 @@ "error.file.minheight": "The height of the image must be at least {height} pixels", "error.file.minsize": "The file is too small", "error.file.minwidth": "The width of the image must be at least {width} pixels", + "error.file.name.unique": "The filename must be unique", "error.file.name.missing": "نام فایل اجباری است", "error.file.notFound": "فایل «{filename}» پیدا نشد.", "error.file.orientation": "The orientation of the image must be \"{orientation}\"", + "error.file.sort.permission": "You are not allowed to change the sorting of \"{filename}\"", "error.file.type.forbidden": "شما اجازه بارگذاری فایلهای «{type}» را ندارید", "error.file.type.invalid": "Invalid file type: {type}", - "error.file.undefined": "فایل مورد نظر پیدا نشد.", + "error.file.undefined": "\u0641\u0627\u06cc\u0644 \u0645\u0648\u0631\u062f \u0646\u0638\u0631 \u067e\u06cc\u062f\u0627 \u0646\u0634\u062f.", "error.form.incomplete": "لطفا کلیه خطاهای فرم را برطرف کنید", "error.form.notSaved": "امکان دخیره فرم وجود ندارد", "error.language.code": "Please enter a valid code for the language", + "error.language.create.permission": "You are not allowed to create a language", + "error.language.delete.permission": "You are not allowed to delete the language", "error.language.duplicate": "The language already exists", "error.language.name": "Please enter a valid name for the language", "error.language.notFound": "The language could not be found", + "error.language.update.permission": "You are not allowed to update the language", "error.layout.validation.block": "There's an error on the \"{field}\" field in block {blockIndex} using the \"{fieldset}\" block type in layout {layoutIndex}", "error.layout.validation.settings": "There's an error in layout {index} settings", - "error.license.format": "Please enter a valid license key", - "error.license.email": "لطفا یک ایمیل معتبر وارد کنید", + "error.license.domain": "The domain for the license is missing", + "error.license.email": "لطفا ایمیل صحیحی وارد کنید", + "error.license.format": "Please enter a valid license code", "error.license.verification": "The license could not be verified", + "error.login.totp.confirm.invalid": "Invalid code", + "error.login.totp.confirm.missing": "Please enter the current code", + "error.object.validation": "There’s an error in the \"{label}\" field:\n{message}", "error.offline": "The Panel is currently offline", "error.page.changeSlug.permission": "شما امکان تغییر پسوند Url صفحه «{slug}» را ندارید", + "error.page.changeSlug.reserved": "The path of top-level pages must not start with \"{path}\"", "error.page.changeStatus.incomplete": "صفحه حاوی خطا است و قابل انتشار نیست", "error.page.changeStatus.permission": "وضعیت صفحه جاری قابل تغییر نیست", "error.page.changeStatus.toDraft.invalid": "صفحه «{slug}» قابل تبدیل به پیش نویس نیست", @@ -133,10 +179,18 @@ "error.page.delete": "حذف صفحه «{slug}» ممکن نیست", "error.page.delete.confirm": "جهت ادامه عنوان صفحه را وارد کنید", "error.page.delete.hasChildren": "این صفحه جاوی زیرصفحه است و نمی تواند حذف شود", + "error.page.delete.multiple": "Not all pages could be deleted. Try each remaining page individually to see the specific error that prevents deletion.", "error.page.delete.permission": "شما اجازه حذف «{slug}» را ندارید", "error.page.draft.duplicate": "صفحه پیش‌نویسی با پسوند Url مشابه «{slug}» هم اکنون موجود است", "error.page.duplicate": "صفحه‌ای با آدرس Url مشابه «{slug}» هم اکنون موجود است", "error.page.duplicate.permission": "You are not allowed to duplicate \"{slug}\"", + "error.page.move.ancestor": "The page cannot be moved into itself", + "error.page.move.directory": "The page directory cannot be moved", + "error.page.move.duplicate": "A sub page with the URL appendix \"{slug}\" already exists", + "error.page.move.noSections": "The page \"{parent}\" cannot be a parent of any page because it lacks any pages sections in its blueprint", + "error.page.move.notFound": "The moved page could not be found", + "error.page.move.permission": "You are not allowed to move \"{slug}\"", + "error.page.move.template": "The \"{template}\" template is not accepted as a subpage of \"{parent}\"", "error.page.notFound": "صفحه مورد نظر با آدرس «{slug}» پیدا نشد.", "error.page.num.invalid": "لطفا شماره ترتیب را بدرستی وارد نمایید. اعداد نباید منفی باشند.", "error.page.slug.invalid": "Please enter a valid URL appendix", @@ -163,6 +217,8 @@ "error.site.changeTitle.permission": "شما اجازه تغییر عنوان سایت را ندارید", "error.site.update.permission": "شما اجازه بروزرسانی سایت را ندارید", + "error.structure.validation": "There's an error on the \"{field}\" field in row {index}", + "error.template.default.notFound": "قالب پیش فرض موجود نیست", "error.unexpected": "An unexpected error occurred! Enable debug mode for more info: https://getkirby.com/docs/reference/system/options/debug", @@ -176,7 +232,7 @@ "error.user.changeRole.toAdmin": "You are not allowed to promote someone to the admin role", "error.user.create.permission": "شما اجازه ایجاد این کاربر را ندارید", "error.user.delete": "کاربر «{name}» نمی تواند حذف شود", - "error.user.delete.lastAdmin": "حذف آخرین مدیر سیستم ممکن نیست", + "error.user.delete.lastAdmin": "\u062d\u0630\u0641 \u0622\u062e\u0631\u06cc\u0646 \u0645\u062f\u06cc\u0631 \u0633\u06cc\u0633\u062a\u0645 \u0645\u0645\u06a9\u0646 \u0646\u06cc\u0633\u062a", "error.user.delete.lastUser": "حذف آخرین کاربر ممکن نیست", "error.user.delete.permission": "شما اجازه حذف کاربر «{name}» را ندارید", "error.user.duplicate": "کاربری با ایمیل «{email}» هم اکنون موجود است", @@ -185,7 +241,7 @@ "error.user.notFound": "کاربر «{name}» پیدا نشد", "error.user.password.excessive": "Please enter a valid password. Passwords must not be longer than 1000 characters.", "error.user.password.invalid": "لطفا گذرواژه صحیحی با حداقل طول 8 حرف وارد کنید. ", - "error.user.password.notSame": "لطفا تکرار گذرواژه را وارد نمایید", + "error.user.password.notSame": "\u0644\u0637\u0641\u0627 \u062a\u06a9\u0631\u0627\u0631 \u06af\u0630\u0631\u0648\u0627\u0698\u0647 \u0631\u0627 \u0648\u0627\u0631\u062f \u0646\u0645\u0627\u06cc\u06cc\u062f", "error.user.password.undefined": "کاربر فاقد گذرواژه است", "error.user.password.wrong": "Wrong password", "error.user.role.invalid": "لطفا نقش صحیحی وارد نمایید", @@ -195,8 +251,10 @@ "error.validation.accepted": "لطفا تایید کنید", "error.validation.alpha": "لطفا تنها از بین حروف a-z انتخاب کنید", "error.validation.alphanum": "لطفا تنها از بین حروف a-z و اعداد 0-9 انتخاب کنید", + "error.validation.anchor": "Please enter a correct link anchor", "error.validation.between": "لطفا مقداری مابین «{min}» و «{max}» وارد کنید", "error.validation.boolean": "لطفا تایید یا رد کنید", + "error.validation.color": "Please enter a valid color in the {format} format", "error.validation.contains": "لطفا مقداری شامل «{needle}» وارد کنید", "error.validation.date": "لطفا تاریخ معتبری وارد کنید", "error.validation.date.after": "Please enter a date after {date}", @@ -204,13 +262,14 @@ "error.validation.date.between": "Please enter a date between {min} and {max}", "error.validation.denied": "لطفا رد کنید", "error.validation.different": "مقدار نباید مساوی «{other}» باشد", - "error.validation.email": "لطفا یک ایمیل معتبر وارد کنید", + "error.validation.email": "لطفا ایمیل صحیحی وارد کنید", "error.validation.endswith": "مقدار باید با «{end}» ختم شود", "error.validation.filename": "لطفا نام فایل صحیحی وارد کنید", "error.validation.in": "لطفا یکی از مقادیر روبرو را وارد کنید: ({in})", "error.validation.integer": "لطفا عدد صحیحی وارد کنید", "error.validation.ip": "لطفا IP آدرس صحیحی وارد کنید", "error.validation.less": "لطفا مقداری کمتر از {max} وارد کنید", + "error.validation.linkType": "The link type is not allowed", "error.validation.match": "مقدار وارد شده با الگوی مورد نظر همخوانی ندارد", "error.validation.max": "لطفا مقداری کوچکتر یا مساوی {min} وارد کنید", "error.validation.maxlength": "لطفا عبارت کوتاه‌تری وارد کنید. (حداکثر {max} حرف)", @@ -227,15 +286,18 @@ "error.validation.same": "لطفا مقدار «{other}» را وارد کنید", "error.validation.size": "اندازه ورودی باید معادل «{size}» باشد", "error.validation.startswith": "مقدار باید با «{start}» شروع شود", + "error.validation.tel": "Please enter an unformatted phone number", "error.validation.time": "لطفا زمان معتبری وارد کنید", "error.validation.time.after": "Please enter a time after {time}", "error.validation.time.before": "Please enter a time before {time}", "error.validation.time.between": "Please enter a time between {min} and {max}", + "error.validation.uuid": "Please enter a valid UUID", "error.validation.url": "لطفا آدرس URL صحیح وارد کنید", "expand": "Expand", "expand.all": "Expand All", + "field.invalid": "The field is invalid", "field.required": "The field is required", "field.blocks.changeType": "Change type", "field.blocks.code.name": "کد", @@ -245,8 +307,9 @@ "field.blocks.delete.confirm.all": "Do you really want to delete all blocks?", "field.blocks.delete.confirm.selected": "Do you really want to delete the selected blocks?", "field.blocks.empty": "No blocks yet", + "field.blocks.fieldsets.empty": "No fieldsets yet", "field.blocks.fieldsets.label": "Please select a block type …", - "field.blocks.fieldsets.paste": "Press {{ shortcut }} to paste/import blocks from your clipboard", + "field.blocks.fieldsets.paste": "Press {{ shortcut }} to import layouts/blocks from your clipboard Only those allowed in the current field will get inserted.", "field.blocks.gallery.name": "Gallery", "field.blocks.gallery.images.empty": "No images yet", "field.blocks.gallery.images.label": "Images", @@ -254,11 +317,16 @@ "field.blocks.heading.name": "Heading", "field.blocks.heading.text": "Text", "field.blocks.heading.placeholder": "Heading …", + "field.blocks.figure.back.plain": "Plain", + "field.blocks.figure.back.pattern.light": "Pattern (light)", + "field.blocks.figure.back.pattern.dark": "Pattern (dark)", "field.blocks.image.alt": "Alternative text", "field.blocks.image.caption": "Caption", "field.blocks.image.crop": "Crop", "field.blocks.image.link": "پیوند", "field.blocks.image.location": "Location", + "field.blocks.image.location.internal": "This website", + "field.blocks.image.location.external": "External source", "field.blocks.image.name": "تصویر", "field.blocks.image.placeholder": "Select an image", "field.blocks.image.ratio": "Ratio", @@ -275,41 +343,75 @@ "field.blocks.quote.citation.placeholder": "by …", "field.blocks.text.name": "Text", "field.blocks.text.placeholder": "Text …", + "field.blocks.video.autoplay": "Autoplay", "field.blocks.video.caption": "Caption", + "field.blocks.video.controls": "Controls", + "field.blocks.video.location": "Location", + "field.blocks.video.loop": "Loop", + "field.blocks.video.muted": "Muted", "field.blocks.video.name": "Video", "field.blocks.video.placeholder": "Enter a video URL", + "field.blocks.video.poster": "Poster", + "field.blocks.video.preload": "Preload", "field.blocks.video.url.label": "Video-URL", "field.blocks.video.url.placeholder": "https://youtube.com/?v=", - "field.files.empty": "فایلی انتخاب نشده است", + "field.entries.delete.confirm.all": "Do you really want to delete all entries?", + "field.entries.empty": "موردی وجود ندارد.", + "field.files.empty": "فایلی انتخاب نشده است", + "field.files.empty.single": "No file selected yet", + + "field.layout.change": "Change layout", "field.layout.delete": "Delete layout", "field.layout.delete.confirm": "Do you really want to delete this layout?", + "field.layout.delete.confirm.all": "Do you really want to delete all layouts?", "field.layout.empty": "No rows yet", "field.layout.select": "Select a layout", "field.object.empty": "No information yet", "field.pages.empty": "صفحه‌ای انتخاب نشده است", + "field.pages.empty.single": "No page selected yet", - "field.structure.delete.confirm": "مدخل جاری حذف شود؟", + "field.structure.delete.confirm": "\u0645\u062f\u062e\u0644 \u062c\u0627\u0631\u06cc \u062d\u0630\u0641 \u0634\u0648\u062f\u061f", "field.structure.delete.confirm.all": "Do you really want to delete all entries?", - "field.structure.empty": "موردی وجود ندارد.", + "field.structure.empty": "\u0645\u0648\u0631\u062f\u06cc \u0648\u062c\u0648\u062f \u0646\u062f\u0627\u0631\u062f.", "field.users.empty": "کاربری انتخاب نشده است", + "field.users.empty.single": "No user selected yet", + "fields.empty": "No fields yet", + + "file": "File", "file.blueprint": "This file has no blueprint yet. You can define the setup in /site/blueprints/files/{blueprint}.yml", + "file.changeTemplate": "تغییر قالب", + "file.changeTemplate.notice": "Changing the file's template will remove content for fields that don't match in type. If the new template defines certain rules, e.g. image dimensions, those will also be applied irreversibly. Use with caution.", "file.delete.confirm": "آیا واقعا می خواهید این فایل را حذف کنید؟
{filename}", + "file.focus.placeholder": "Set focal point", + "file.focus.reset": "Remove focal point", + "file.focus.title": "Focus", "file.sort": "Change position", "files": "فایل‌ها", + "files.delete.confirm.selected": "Do you really want to delete the selected files? This action cannot be undone.", "files.empty": "فایلی موجود نیست", + "filter": "Filter", + + "form.discard": "Discard changes", + "form.discard.confirm": "Do you really want to discard all your changes?", + "form.locked": "This content is disabled for you as it is currently edited by another user", + "form.unsaved": "The current changes have not yet been saved", + "form.preview": "Preview changes", + "form.preview.draft": "Preview draft", + "hide": "Hide", "hour": "ساعت", + "hue": "Hue", "import": "Import", "info": "Info", - "insert": "درج", + "insert": "\u062f\u0631\u062c", "insert.after": "Insert after", "insert.before": "Insert before", "install": "نصب", @@ -324,14 +426,14 @@ "installation.issues.mbstring": "افزونه MB String مورد نیاز است", "installation.issues.media": "پوشه /media موجود نیست یا قابل نوشتن نیست", "installation.issues.php": "لطفا از پی‌اچ‌پی 8 یا بالاتر استفاده کنید", - "installation.issues.server": "کربی نیاز به Apache، Nginx یا Caddy دارد", "installation.issues.sessions": "پوشه /site/sessions وجود ندارد یا قابل نوشتن نیست", - "language": "زبان", + "language": "\u0632\u0628\u0627\u0646", "language.code": "کد", "language.convert": "پیش‌فرض شود", "language.convert.confirm": "

آیا واقعا میخواهید {name} را به زبان پیشفرض تبدیل کنید؟ این عمل برگشت ناپذیر است.

اگر {name} دارای محتوای غیر ترجمه شده باشد، جایگزین معتبر دیگری نخواهد بود و ممکن است بخش‌هایی از سایت شما خالی باشد.

", "language.create": "افزودن زبان جدید", + "language.default": "زبان پیش‌فرض", "language.delete.confirm": "آیا واقعا میخواهید زبان {name} را به همراه تمام ترجمه‌ها حذف کنید؟ این عمل قابل بازگشت نخواهد بود!", "language.deleted": "زبان مورد نظر حذف شد", "language.direction": "rtl", @@ -339,8 +441,17 @@ "language.direction.rtl": "راست به چپ", "language.locale": "PHP locale string", "language.locale.warning": "You are using a custom locale set up. Please modify it in the language file in /site/languages", - "language.name": "نام", + "language.name": "پارسی", + "language.secondary": "Secondary language", + "language.settings": "Language settings", "language.updated": "زبان به روز شد", + "language.variables": "Language variables", + "language.variables.empty": "No translations yet", + + "language.variable.delete.confirm": "Do you really want to delete the variable for {key}?", + "language.variable.key": "Key", + "language.variable.notFound": "The variable could not be found", + "language.variable.value": "Value", "languages": "زبان‌ها", "languages.default": "زبان پیش‌فرض", @@ -348,36 +459,57 @@ "languages.secondary": "زبان‌های ثانویه", "languages.secondary.empty": "هنوز هیچ زبان ثانویه‌ای موجود نیست", - "license": "مجوز", + "license": "\u0645\u062c\u0648\u0632", + "license.activate": "Activate it now", + "license.activate.label": "Please activate your license", + "license.activate.domain": "Your license will be activated for {host}.", + "license.activate.local": "You are about to activate your Kirby license for your local domain {host}. If this site will be deployed to a public domain, please activate it there instead. If {host} is the domain you want to use your license for, please continue.", + "license.activated": "Activated", "license.buy": "خرید مجوز", - "license.register": "ثبت", + "license.code": "کد", + "license.code.help": "You received your license code after the purchase via email. Please copy and paste it here.", + "license.code.label": "لطفا کد مجوز خود را وارد کنید", + "license.status.active.info": "Includes new major versions until {date}", + "license.status.active.label": "Valid license", + "license.status.demo.info": "This is a demo installation", + "license.status.demo.label": "Demo", + "license.status.inactive.info": "Renew license to update to new major versions", + "license.status.inactive.label": "No new major versions", + "license.status.legacy.bubble": "Ready to renew your license?", + "license.status.legacy.info": "Your license does not cover this version", + "license.status.legacy.label": "Please renew your license", + "license.status.missing.bubble": "Ready to launch your site?", + "license.status.missing.info": "No valid license", + "license.status.missing.label": "Please activate your license", + "license.status.unknown.info": "The license status is unknown", + "license.status.unknown.label": "Unknown", "license.manage": "Manage your licenses", - "license.register.help": "پس از خرید از طریق ایمیل، کد مجوز خود را دریافت کردید. لطفا برای ثبت‌نام آن را کپی و اینجا پیست کنید.", - "license.register.label": "لطفا کد مجوز خود را وارد کنید", - "license.register.domain": "Your license will be registered to {host}.", - "license.register.local": "You are about to register your license for your local domain {host}. If this site will be deployed to a public domain, please register it there instead. If {host} is the domain you want to license Kirby to, please continue.", - "license.register.success": "با تشکر از شما برای حمایت از کربی", - "license.unregistered": "این یک نسخه آزمایشی ثبت نشده از کربی است", + "license.purchased": "Purchased", + "license.success": "با تشکر از شما برای حمایت از کربی", "license.unregistered.label": "Unregistered", - "link": "پیوند", - "link.text": "متن پیوند", + "link": "\u067e\u06cc\u0648\u0646\u062f", + "link.text": "\u0645\u062a\u0646 \u067e\u06cc\u0648\u0646\u062f", "loading": "بارگزاری", "lock.unsaved": "Unsaved changes", "lock.unsaved.empty": "There are no more unsaved changes", - "lock.isLocked": "Unsaved changes by {email}", - "lock.file.isLocked": "The file is currently being edited by {email} and cannot be changed.", - "lock.page.isLocked": "The page is currently being edited by {email} and cannot be changed.", + "lock.unsaved.files": "Unsaved files", + "lock.unsaved.pages": "Unsaved pages", + "lock.unsaved.users": "Unsaved accounts", + "lock.isLocked": "Unsaved changes by {email}", "lock.unlock": "Unlock", - "lock.isUnlocked": "Your unsaved changes have been overwritten by another user. You can download your changes to merge them manually.", + "lock.unlock.submit": "Unlock and overwrite unsaved changes by {email}", + "lock.isUnlocked": "Was unlocked by another user", "login": "ورود", "login.code.label.login": "Login code", "login.code.label.password-reset": "Password reset code", "login.code.placeholder.email": "000 000", + "login.code.placeholder.totp": "000000", "login.code.text.email": "If your email address is registered, the requested code was sent via email.", + "login.code.text.totp": "Please enter the one‑time code from your authenticator app.", "login.email.login.body": "Hi {user.nameOrEmail},\n\nYou recently requested a login code for the Panel of {site}.\nThe following login code will be valid for {timeout} minutes:\n\n{code}\n\nIf you did not request a login code, please ignore this email or contact your administrator if you have questions.\nFor security, please DO NOT forward this email.", "login.email.login.subject": "Your login code", "login.email.password-reset.body": "Hi {user.nameOrEmail},\n\nYou recently requested a password reset code for the Panel of {site}.\nThe following password reset code will be valid for {timeout} minutes:\n\n{code}\n\nIf you did not request a password reset code, please ignore this email or contact your administrator if you have questions.\nFor security, please DO NOT forward this email.", @@ -388,58 +520,80 @@ "login.toggleText.code.email-password": "Login with password", "login.toggleText.password-reset.email": "Forgot your password?", "login.toggleText.password-reset.email-password": "← Back to login", + "login.totp.enable.option": "Set up one‑time codes", + "login.totp.enable.intro": "Authenticator apps can generate one‑time codes that are used as a second factor when signing into your account.", + "login.totp.enable.qr.label": "1. Scan this QR code", + "login.totp.enable.qr.help": "Unable to scan? Add the setup key {secret} manually to your authenticator app.", + "login.totp.enable.confirm.headline": "2. Confirm with generated code", + "login.totp.enable.confirm.text": "Your app generates a new one‑time code every 30 seconds. Enter the current code to complete the setup:", + "login.totp.enable.confirm.label": "Current code", + "login.totp.enable.confirm.help": "After this setup, we will ask you for a one‑time code every time you log in.", + "login.totp.enable.success": "One‑time codes enabled", + "login.totp.disable.option": "Disable one‑time codes", + "login.totp.disable.label": "Enter your password to disable one‑time codes", + "login.totp.disable.help": "In the future, a different second factor like a login code sent via email will be requested when you log in. You can always set up one‑time codes again later.", + "login.totp.disable.admin": "

This will disable one‑time codes for {user}.

In the future, a different second factor like a login code sent via email will be requested when they log in. {user} can set up one‑time codes again after their next login.

", + "login.totp.disable.success": "One‑time codes disabled", "logout": "خروج", + "merge": "Merge", "menu": "منو", "meridiem": "ق.ظ/ب.ظ", "mime": "نوع رسانه", "minutes": "دقیقه", "month": "ماه", - "months.april": "آوریل", - "months.august": "اوت", - "months.december": "دسامبر", + "months.april": "\u0622\u0648\u0631\u06cc\u0644", + "months.august": "\u0627\u0648\u062a", + "months.december": "\u062f\u0633\u0627\u0645\u0628\u0631", "months.february": "فوریه", - "months.january": "ژانویه", - "months.july": "ژوئیه", - "months.june": "ژوئن", - "months.march": "مارس", - "months.may": "می", - "months.november": "نوامبر", - "months.october": "اکتبر", - "months.september": "سپتامبر", + "months.january": "\u0698\u0627\u0646\u0648\u06cc\u0647", + "months.july": "\u0698\u0648\u0626\u06cc\u0647", + "months.june": "\u0698\u0648\u0626\u0646", + "months.march": "\u0645\u0627\u0631\u0633", + "months.may": "\u0645\u06cc", + "months.november": "\u0646\u0648\u0627\u0645\u0628\u0631", + "months.october": "\u0627\u06a9\u062a\u0628\u0631", + "months.september": "\u0633\u067e\u062a\u0627\u0645\u0628\u0631", "more": "بیشتر", + "move": "Move", "name": "نام", "next": "بعدی", + "night": "Night", "no": "no", "off": "off", "on": "on", "open": "بازکردن", "open.newWindow": "Open in new window", + "option": "Option", "options": "گزینه‌ها", "options.none": "No options", + "options.all": "Show all {count} options", "orientation": "جهت", "orientation.landscape": "افقی", "orientation.portrait": "عمودی", "orientation.square": "مربع", + "page": "صفحه", "page.blueprint": "This page has no blueprint yet. You can define the setup in /site/blueprints/pages/{blueprint}.yml", "page.changeSlug": "تغییر Url صفحه", - "page.changeSlug.fromTitle": "ایجاد از روی عنوان", + "page.changeSlug.fromTitle": "\u0627\u06cc\u062c\u0627\u062f \u0627\u0632 \u0631\u0648\u06cc \u0639\u0646\u0648\u0627\u0646", "page.changeStatus": "تغییر وضعیت", "page.changeStatus.position": "لطفا یک موقعیت را انتخاب کنید", "page.changeStatus.select": "یک وضعیت جدید را انتخاب کنید", "page.changeTemplate": "تغییر قالب", + "page.changeTemplate.notice": "Changing the page's template will remove content for fields that don't match in type. Use with caution.", + "page.create": "Create as {status}", "page.delete.confirm": "صفحه {title} حذف شود؟", "page.delete.confirm.subpages": "این صفحه دارای زیرصفحه است.
تمام زیر صفحات نیز حذف خواهد شد.", "page.delete.confirm.title": "جهت ادامه عنوان صفحه را وارد کنید", - "page.draft.create": "ایجاد پیش‌نویس", "page.duplicate.appendix": "کپی", "page.duplicate.files": "Copy files", "page.duplicate.pages": "Copy pages", + "page.move": "Move page", "page.sort": "Change position", "page.status": "وضعیت", "page.status.draft": "پیش‌نویس", @@ -450,6 +604,7 @@ "page.status.unlisted.description": "این صفحه فقط از طریق URL قابل دسترسی است", "pages": "صفحات", + "pages.delete.confirm.selected": "Do you really want to delete the selected pages? This action cannot be undone.", "pages.empty": "هنوز هیچ صفحه‌ای موجود نیست", "pages.status.draft": "پیش‌نویس‌ها", "pages.status.listed": "منتشر شده", @@ -457,22 +612,29 @@ "pagination.page": "صفحه", - "password": "گذرواژه", + "password": "\u06af\u0630\u0631\u0648\u0627\u0698\u0647", "paste": "Paste", "paste.after": "Paste after", + "paste.success": "{count} pasted!", "pixel": "پیکسل", "plugin": "Plugin", "plugins": "Plugins", "prev": "قبلی", "preview": "Preview", + + "publish": "Publish", + "published": "منتشر شده", + "remove": "حذف", "rename": "تغییر نام", - "replace": "جایگزینی", - "retry": "تلاش مجدد", + "renew": "Renew", + "replace": "\u062c\u0627\u06cc\u06af\u0632\u06cc\u0646\u06cc", + "replace.with": "Replace with", + "retry": "\u062a\u0644\u0627\u0634 \u0645\u062c\u062f\u062f", "revert": "بازگرداندن تغییرات", "revert.confirm": "Do you really want to delete all unsaved changes?", - "role": "نقش", + "role": "\u0646\u0642\u0634", "role.admin.description": "The admin has all rights", "role.admin.title": "Admin", "role.all": "همه", @@ -481,12 +643,15 @@ "role.nobody.description": "This is a fallback role without any permissions", "role.nobody.title": "Nobody", - "save": "ذخیره", + "save": "\u0630\u062e\u06cc\u0631\u0647", + "saved": "Saved", "search": "جستجو", + "searching": "Searching", "search.min": "Enter {min} characters to search", - "search.all": "Show all", + "search.all": "Show all {count} results", "search.results.none": "No results", + "section.invalid": "The section is invalid", "section.required": "The section is required", "security": "Security", @@ -498,16 +663,25 @@ "size": "اندازه", "slug": "پسوند Url", "sort": "ترتیب", + "sort.drag": "Drag to sort …", + "split": "Split", "stats.empty": "No reports", + "status": "وضعیت", + + "system.info.copy": "Copy info", + "system.info.copied": "System info copied", "system.issues.content": "The content folder seems to be exposed", "system.issues.eol.kirby": "Your installed Kirby version has reached end-of-life and will not receive further security updates", "system.issues.eol.plugin": "Your installed version of the { plugin } plugin is has reached end-of-life and will not receive further security updates", + "system.issues.eol.php": "Your installed PHP release { release } has reached end-of-life and will not receive further security updates", "system.issues.debug": "Debugging must be turned off in production", "system.issues.git": "The .git folder seems to be exposed", "system.issues.https": "We recommend HTTPS for all your sites", "system.issues.kirby": "The kirby folder seems to be exposed", + "system.issues.local": "The site is running locally with relaxed security checks", "system.issues.site": "The site folder seems to be exposed", + "system.issues.vue.compiler": "The Vue template compiler is enabled", "system.issues.vulnerability.kirby": "Your installation might be affected by the following vulnerability ({ severity } severity): { description }", "system.issues.vulnerability.plugin": "Your installation might be affected by the following vulnerability in the { plugin } plugin ({ severity } severity): { description }", "system.updateStatus": "Update status", @@ -520,13 +694,22 @@ "system.updateStatus.update": "Free update { version } available", "system.updateStatus.upgrade": "Upgrade { version } available", + "tel": "Phone", + "tel.placeholder": "+49123456789", + "template": "\u0642\u0627\u0644\u0628 \u0635\u0641\u062d\u0647", + + "theme": "Theme", + "theme.light": "Lights on", + "theme.dark": "Lights off", + "theme.automatic": "Match system default", + "title": "عنوان", - "template": "قالب صفحه", "today": "امروز", + "toolbar.button.clear": "Clear formatting", "toolbar.button.code": "کد", - "toolbar.button.bold": "متن با حروف درشت", - "toolbar.button.email": "پست الکترونیک", + "toolbar.button.bold": "\u0645\u062a\u0646 \u0628\u0627 \u062d\u0631\u0648\u0641 \u062f\u0631\u0634\u062a", + "toolbar.button.email": "\u067e\u0633\u062a \u0627\u0644\u06a9\u062a\u0631\u0648\u0646\u06cc\u06a9", "toolbar.button.headings": "عنوان‌ها", "toolbar.button.heading.1": "عنوان 1", "toolbar.button.heading.2": "عنوان 2", @@ -534,13 +717,15 @@ "toolbar.button.heading.4": "Heading 4", "toolbar.button.heading.5": "Heading 5", "toolbar.button.heading.6": "Heading 6", - "toolbar.button.italic": "متن اریب", - "toolbar.button.file": "File", + "toolbar.button.italic": "\u0645\u062a\u0646 \u0627\u0631\u06cc\u0628", + "toolbar.button.file": "فایل", "toolbar.button.file.select": "Select a file", "toolbar.button.file.upload": "Upload a file", - "toolbar.button.link": "پیوند", + "toolbar.button.link": "\u067e\u06cc\u0648\u0646\u062f", "toolbar.button.paragraph": "Paragraph", "toolbar.button.strike": "Strike-through", + "toolbar.button.sub": "Subscript", + "toolbar.button.sup": "Superscript", "toolbar.button.ol": "لیست مرتب", "toolbar.button.underline": "Underline", "toolbar.button.ul": "لیست معمولی", @@ -550,6 +735,8 @@ "translation.name": "انگلیسی", "translation.locale": "fa_IR", + "type": "Type", + "upload": "بارگذاری", "upload.error.cantMove": "The uploaded file could not be moved", "upload.error.cantWrite": "Failed to write file to disk", @@ -562,7 +749,7 @@ "upload.error.noFiles": "No files were uploaded", "upload.error.partial": "The uploaded file was only partially uploaded", "upload.error.tmpDir": "Missing a temporary folder", - "upload.errors": "Error", + "upload.errors": "خطا", "upload.progress": "در حال بارگذاری...", "url": "Url", @@ -574,6 +761,7 @@ "user.changeLanguage": "تغییر زبان", "user.changeName": "تغییر نام این کاربر", "user.changePassword": "تغییر گذرواژه", + "user.changePassword.current": "Your current password", "user.changePassword.new": "گذرواژه جدید", "user.changePassword.new.confirm": "تایید گذرواژه جدید...", "user.changeRole": "تغییر نقش", @@ -584,18 +772,21 @@ "users": "کاربران", - "version": "نسخه نرم افزار", + "version": "\u0646\u0633\u062e\u0647 \u0646\u0631\u0645 \u0627\u0641\u0632\u0627\u0631", + "version.changes": "Changed version", + "version.compare": "Compare versions", "version.current": "Current version", "version.latest": "Latest version", "versionInformation": "Version information", + "view": "View", "view.account": "حساب کاربری شما", - "view.installation": "نصب و راه اندازی", + "view.installation": "\u0646\u0635\u0628 \u0648 \u0631\u0627\u0647 \u0627\u0646\u062f\u0627\u0632\u06cc", "view.languages": "زبان‌ها", "view.resetPassword": "Reset password", "view.site": "سایت", "view.system": "System", - "view.users": "کاربران", + "view.users": "\u06a9\u0627\u0631\u0628\u0631\u0627\u0646", "welcome": "خوش آمدید", "year": "سال", diff --git a/kirby/i18n/translations/fi.json b/kirby/i18n/translations/fi.json index 9f37323..75a75f5 100644 --- a/kirby/i18n/translations/fi.json +++ b/kirby/i18n/translations/fi.json @@ -3,19 +3,28 @@ "account.delete": "Poista tilisi", "account.delete.confirm": "Haluatko varmasti poistaa tilisi? Sinut kirjataan ulos välittömästi, eikä tiliäsi voi palauttaa.", - "add": "Lisää", + "activate": "Activate", + "add": "Lis\u00e4\u00e4", + "alpha": "Alpha", "author": "Tekijä", "avatar": "Profiilikuva", "back": "Takaisin", "cancel": "Peruuta", "change": "Muuta", "close": "Sulje", + "changes": "Changes", "confirm": "Ok", "collapse": "Pienennä", "collapse.all": "Pienennä kaikki", + "color": "Color", + "coordinates": "Coordinates", "copy": "Kopioi", "copy.all": "Kopioi kaikki", + "copy.success": "{count} copied!", + "copy.success.multiple": "{count} copied!", + "copy.url": "Copy URL", "create": "Luo", + "custom": "Custom", "date": "Päivämäärä", "date.select": "Valitse päivämäärä", @@ -34,26 +43,35 @@ "delete": "Poista", "delete.all": "Poista kaikki", + "dialog.fields.empty": "This dialog has no fields", "dialog.files.empty": "Ei valittavissa olevia tiedostoja", "dialog.pages.empty": "Ei valittavissa olevia sivuja", + "dialog.text.empty": "This dialog does not define any text", "dialog.users.empty": "Ei valittavissa olevia käyttäjiä", "dimensions": "Mitat", + "disable": "Disable", "disabled": "Pois käytöstä", "discard": "Hylkää", + + "drawer.fields.empty": "This drawer has no fields", + + "domain": "Domain", "download": "Lataa", "duplicate": "Kahdenna", "edit": "Muokkaa", - "email": "Sähköposti", + "email": "S\u00e4hk\u00f6posti", "email.placeholder": "nimi@osoite.fi", + "enter": "Enter", "entries": "Entries", "entry": "Entry", "environment": "Ympäristö", + "error": "Error", "error.access.code": "Väärä koodi", "error.access.login": "Kirjautumistiedot eivät kelpaa", "error.access.panel": "Sinulla ei ole oikeutta käyttää paneelia", @@ -74,13 +92,31 @@ "error.cache.type.invalid": "Invalid cache type \"{type}\"", + "error.content.lock.delete": "The version is locked and cannot be deleted", + "error.content.lock.move": "The source version is locked and cannot be moved", + "error.content.lock.publish": "This version is already published", + "error.content.lock.replace": "The version is locked and cannot be replaced", + "error.content.lock.update": "The version is locked and cannot be updated", + + "error.entries.max.plural": "You must not add more than {max} entries", + "error.entries.max.singular": "You must not add more than one entry", + "error.entries.min.plural": "You must add at least {min} entries", + "error.entries.min.singular": "You must add at least one entry", + "error.entries.supports": "\"{type}\" field type is not supported for the entries field", + "error.entries.validation": "There's an error on the \"{field}\" field in row {index}", + "error.email.preset.notFound": "Nimellä \"{name}\" ja kyseisellä verkkotunnuksella ei löydy sähköpostiosoitetta", "error.field.converter.invalid": "Muunnin \"{converter}\" ei kelpaa", + "error.field.link.options": "Invalid options: {options}", "error.field.type.missing": "Field \"{ name }\": The field type \"{ type }\" does not exist", "error.file.changeName.empty": "Nimi ei voi olla tyhjä", "error.file.changeName.permission": "Sinulla ei ole oikeutta muuttaa tiedoston \"{filename}\" nimeä", + "error.file.changeTemplate.invalid": "The template for the file \"{id}\" cannot be changed to \"{template}\" (valid: \"{blueprints}\")", + "error.file.changeTemplate.permission": "You are not allowed to change the template for the file \"{id}\"", + + "error.file.delete.multiple": "Not all files could be deleted. Try each remaining file individually to see the specific error that prevents deletion.", "error.file.duplicate": "Tiedosto nimeltä \"{filename}\" on jo olemassa", "error.file.extension.forbidden": "Tiedostopääte \"{extension}\" ei ole sallittu", "error.file.extension.invalid": "Pääte {extension} ei kelpaa", @@ -95,33 +131,43 @@ "error.file.minheight": "Kuvan korkeus täytyy olla vähintään {height} pikseliä", "error.file.minsize": "Tiedosto on liian pieni", "error.file.minwidth": "Kuvan leveys täytyy olla vähintään {width} pikseliä", + "error.file.name.unique": "The filename must be unique", "error.file.name.missing": "Tiedostonimi ei voi olla tyhjä", "error.file.notFound": "Tiedostoa \"{filename}\" ei löytynyt", "error.file.orientation": "Kuvan suuntaus täytyy olla \"{orientation}\"", + "error.file.sort.permission": "You are not allowed to change the sorting of \"{filename}\"", "error.file.type.forbidden": "Sinulla ei ole oikeutta lähettää tiedostoja joiden tyyppi on {type}", "error.file.type.invalid": "Tiedostotyyppi {type} ei kelpaa", - "error.file.undefined": "Tiedostoa ei löytynyt", + "error.file.undefined": "Tiedostoa ei l\u00f6ytynyt", "error.form.incomplete": "Korjaa kaikki lomakkeen virheet…", "error.form.notSaved": "Lomaketta ei voitu tallentaa", "error.language.code": "Anna kielen lyhenne", + "error.language.create.permission": "You are not allowed to create a language", + "error.language.delete.permission": "You are not allowed to delete the language", "error.language.duplicate": "Kieli on jo olemassa", "error.language.name": "Anna kielen nimi", "error.language.notFound": "Kieltä ei löytynyt", + "error.language.update.permission": "You are not allowed to update the language", "error.layout.validation.block": "There's an error on the \"{field}\" field in block {blockIndex} using the \"{fieldset}\" block type in layout {layoutIndex}", "error.layout.validation.settings": "Virhe asetelman {index} asetuksissa", - "error.license.format": "Anna lisenssiavain", - "error.license.email": "Anna kelpaava sähköpostiosoite", + "error.license.domain": "The domain for the license is missing", + "error.license.email": "Anna sähköpostiosoite", + "error.license.format": "Please enter a valid license code", "error.license.verification": "Lisenssiä ei voitu vahvistaa", + "error.login.totp.confirm.invalid": "Väärä koodi", + "error.login.totp.confirm.missing": "Please enter the current code", + "error.object.validation": "There’s an error in the \"{label}\" field:\n{message}", "error.offline": "Paneeli on offline-tilassa", "error.page.changeSlug.permission": "Sinulla ei ole oikeutta muuttaa URL-liitettä sivulle \"{slug}\"", + "error.page.changeSlug.reserved": "The path of top-level pages must not start with \"{path}\"", "error.page.changeStatus.incomplete": "Sivulla on virheitä eikä sitä voitu julkaista", "error.page.changeStatus.permission": "Tämän sivun tilaa ei voi muuttaa", "error.page.changeStatus.toDraft.invalid": "Sivua \"{slug}\" ei voi muuttaa luonnokseksi", @@ -133,17 +179,25 @@ "error.page.delete": "Sivua \"{slug}\" ei voi poistaa", "error.page.delete.confirm": "Anna vahvistuksena sivun nimi", "error.page.delete.hasChildren": "Sivu sisältää alasivuja eikä sitä voida poistaa", + "error.page.delete.multiple": "Not all pages could be deleted. Try each remaining page individually to see the specific error that prevents deletion.", "error.page.delete.permission": "Sinulla ei ole oikeutta poistaa sivua \"{slug}\"", "error.page.draft.duplicate": "Sivuluonnos URL-liitteellä \"{slug}\" on jo olemassa", "error.page.duplicate": "Sivu URL-liitteellä \"{slug}\" on jo olemassa", "error.page.duplicate.permission": "Sinulla ei ole oikeutta kahdentaa sivua \"{slug}\"", + "error.page.move.ancestor": "The page cannot be moved into itself", + "error.page.move.directory": "The page directory cannot be moved", + "error.page.move.duplicate": "A sub page with the URL appendix \"{slug}\" already exists", + "error.page.move.noSections": "The page \"{parent}\" cannot be a parent of any page because it lacks any pages sections in its blueprint", + "error.page.move.notFound": "The moved page could not be found", + "error.page.move.permission": "You are not allowed to move \"{slug}\"", + "error.page.move.template": "The \"{template}\" template is not accepted as a subpage of \"{parent}\"", "error.page.notFound": "Sivua \"{slug}\" ei löytynyt", "error.page.num.invalid": "Anna kelpaava järjestysnumero. Numero ei voi olla negatiivinen.", "error.page.slug.invalid": "Anna kelpaava URL-liite", "error.page.slug.maxlength": "URL-liite täytyy olla vähemmän kuin \"{length}\" merkkiä pitkä", "error.page.sort.permission": "Sivua \"{slug}\" ei voi järjestellä", "error.page.status.invalid": "Aseta kelvollinen sivun tila", - "error.page.undefined": "Sivua ei löytynyt", + "error.page.undefined": "Sivua ei l\u00f6ytynyt", "error.page.update.permission": "Sinulla ei ole oikeutta päivittää sivua \"{slug}\"", "error.section.files.max.plural": "Et voi lisätä enemmän kuin {max} tiedostoa osioon \"{section}\"", @@ -163,6 +217,8 @@ "error.site.changeTitle.permission": "Sinulla ei ole oikeutta päivittää sivuston nimeä", "error.site.update.permission": "Sinulla ei ole oikeutta päivittää sivuston tietoja", + "error.structure.validation": "There's an error on the \"{field}\" field in row {index}", + "error.template.default.notFound": "Oletussivupohjaa ei ole määritetty", "error.unexpected": "Pahus, määrittelemätön virhe! Laita virheenkäsittelytila päälle saadaksesi lisätietoja: https://getkirby.com/docs/reference/system/options/debug", @@ -182,7 +238,7 @@ "error.user.duplicate": "Käyttäjä, jonka sähköpostiosoite on \"{name}\", on jo olemassa", "error.user.email.invalid": "Anna kelpaava sähköpostiosoite", "error.user.language.invalid": "Anna kelpaava kieli", - "error.user.notFound": "Käyttäjää ei löytynyt", + "error.user.notFound": "K\u00e4ytt\u00e4j\u00e4\u00e4 ei l\u00f6ytynyt", "error.user.password.excessive": "Please enter a valid password. Passwords must not be longer than 1000 characters.", "error.user.password.invalid": "Anna kelpaava salasana. Salasanan täytyy olla ainakin 8 merkkiä pitkä.", "error.user.password.notSame": "Salasanat eivät täsmää", @@ -195,8 +251,10 @@ "error.validation.accepted": "Ole hyvä ja vahvista", "error.validation.alpha": "Anna vain merkkejä väliltä a-z", "error.validation.alphanum": "Anna vain merkkejä väliltä a-z tai/ja numeroita väliltä 0-9", + "error.validation.anchor": "Please enter a correct link anchor", "error.validation.between": "Anna arvo väliltä \"{min}\" ja \"{max}\"", "error.validation.boolean": "Vahvista tai peruuta", + "error.validation.color": "Please enter a valid color in the {format} format", "error.validation.contains": "Anna arvo joka sisältää \"{needle}\"", "error.validation.date": "Anna kelpaava päivämäärä", "error.validation.date.after": "Anna päivämäärä {date} jälkeen", @@ -211,6 +269,7 @@ "error.validation.integer": "Anna kelpaava kokonaisluku", "error.validation.ip": "Anna kelpaava IP-osoite", "error.validation.less": "Anna arvo joka on pienempi kuin {max}", + "error.validation.linkType": "The link type is not allowed", "error.validation.match": "Arvo ei vastaa vaadittua kaavaa", "error.validation.max": "Anna arvo joka on enintään {max}", "error.validation.maxlength": "Anna lyhyempi arvo. (enintään {max} merkkiä)", @@ -227,26 +286,30 @@ "error.validation.same": "Anna \"{other}\"", "error.validation.size": "Arvon koko täytyy olla \"{size}\"", "error.validation.startswith": "Arvon alkuosa täytyy olla \"{start}\"", + "error.validation.tel": "Please enter an unformatted phone number", "error.validation.time": "Anna kelpaava aika", "error.validation.time.after": "Anna myöhempi aika kuin {time}", "error.validation.time.before": "Anna aiempi aika kuin {time}", "error.validation.time.between": "Anna aika väliltä {min} ja {max}", + "error.validation.uuid": "Please enter a valid UUID", "error.validation.url": "Anna kelpaava URL", "expand": "Laajenna", "expand.all": "Laajenna kaikki", + "field.invalid": "The field is invalid", "field.required": "Kenttä on pakollinen", "field.blocks.changeType": "Vaihda tyyppiä", - "field.blocks.code.name": "Tunniste", + "field.blocks.code.name": "Koodi", "field.blocks.code.language": "Kieli", "field.blocks.code.placeholder": "Koodisi …", "field.blocks.delete.confirm": "Haluatko varmasti poistaa tämän lohkon?", "field.blocks.delete.confirm.all": "Haluatko varmasti poistaa kaikki lohkot?", "field.blocks.delete.confirm.selected": "Haluatko varmasti poistaa valitut lohkot?", "field.blocks.empty": "Ei lohkoja", + "field.blocks.fieldsets.empty": "No fieldsets yet", "field.blocks.fieldsets.label": "Valitse lohkon tyyppi …", - "field.blocks.fieldsets.paste": "Paina {{ shortcut }} liittääksesi tai tuodaksesi lohkoja leikepöydältä", + "field.blocks.fieldsets.paste": "Press {{ shortcut }} to import layouts/blocks from your clipboard Only those allowed in the current field will get inserted.", "field.blocks.gallery.name": "Galleria", "field.blocks.gallery.images.empty": "Ei kuvia", "field.blocks.gallery.images.label": "Kuvat", @@ -254,11 +317,16 @@ "field.blocks.heading.name": "Otsikko", "field.blocks.heading.text": "Teksti", "field.blocks.heading.placeholder": "Otsikko …", + "field.blocks.figure.back.plain": "Plain", + "field.blocks.figure.back.pattern.light": "Pattern (light)", + "field.blocks.figure.back.pattern.dark": "Pattern (dark)", "field.blocks.image.alt": "Vaihtoehtoinen teksti", "field.blocks.image.caption": "Kuvateksti", "field.blocks.image.crop": "Rajaa", "field.blocks.image.link": "Linkki", "field.blocks.image.location": "Sijainti", + "field.blocks.image.location.internal": "This website", + "field.blocks.image.location.external": "External source", "field.blocks.image.name": "Kuva", "field.blocks.image.placeholder": "Valitse kuva", "field.blocks.image.ratio": "Kuvasuhde", @@ -275,41 +343,75 @@ "field.blocks.quote.citation.placeholder": "Lähde …", "field.blocks.text.name": "Teksti", "field.blocks.text.placeholder": "Teksti …", - "field.blocks.video.caption": "Kuvateksti", + "field.blocks.video.autoplay": "Autoplay", + "field.blocks.video.caption": "Videon teksti", + "field.blocks.video.controls": "Controls", + "field.blocks.video.location": "Sijainti", + "field.blocks.video.loop": "Loop", + "field.blocks.video.muted": "Muted", "field.blocks.video.name": "Video", "field.blocks.video.placeholder": "Anna videon URL", + "field.blocks.video.poster": "Poster", + "field.blocks.video.preload": "Preload", "field.blocks.video.url.label": "Videon URL", "field.blocks.video.url.placeholder": "https://youtube.com/?v=", - "field.files.empty": "Tiedostoja ei ole vielä valittu", + "field.entries.delete.confirm.all": "Do you really want to delete all entries?", + "field.entries.empty": "Rivejä ei ole vielä lisätty", + "field.files.empty": "Tiedostoja ei ole vielä valittu", + "field.files.empty.single": "No file selected yet", + + "field.layout.change": "Change layout", "field.layout.delete": "Poista asettelu", "field.layout.delete.confirm": "Halutako varmasti poistaa tämän asettelun?", + "field.layout.delete.confirm.all": "Do you really want to delete all layouts?", "field.layout.empty": "Ei rivejä", "field.layout.select": "Valitse asettelu", "field.object.empty": "Ei vielä tietoja", "field.pages.empty": " Sivuja ei ole vielä valittu", + "field.pages.empty.single": "No page selected yet", "field.structure.delete.confirm": "Haluatko varmasti poistaa tämän rivin?", "field.structure.delete.confirm.all": "Do you really want to delete all entries?", "field.structure.empty": "Rivejä ei ole vielä lisätty", "field.users.empty": "Käyttäjiä ei ole vielä valittu", + "field.users.empty.single": "No user selected yet", + "fields.empty": "No fields yet", + + "file": "File", "file.blueprint": "Tällä tiedostolla ei ole vielä suunnitelmaa. Voit määrittää suunnitelman tiedostoon /site/blueprints/files/{blueprint}.yml", + "file.changeTemplate": "Vaihda sivupohja", + "file.changeTemplate.notice": "Changing the file's template will remove content for fields that don't match in type. If the new template defines certain rules, e.g. image dimensions, those will also be applied irreversibly. Use with caution.", "file.delete.confirm": "Haluatko varmasti poistaa tiedoston
{filename}?", + "file.focus.placeholder": "Set focal point", + "file.focus.reset": "Remove focal point", + "file.focus.title": "Focus", "file.sort": "Muuta järjestyspaikkaa", "files": "Tiedostot", + "files.delete.confirm.selected": "Do you really want to delete the selected files? This action cannot be undone.", "files.empty": "Tiedostoja ei ole vielä lisätty", + "filter": "Filter", + + "form.discard": "Discard changes", + "form.discard.confirm": "Do you really want to discard all your changes?", + "form.locked": "This content is disabled for you as it is currently edited by another user", + "form.unsaved": "The current changes have not yet been saved", + "form.preview": "Preview changes", + "form.preview.draft": "Preview draft", + "hide": "Piilota", "hour": "Tunti", + "hue": "Hue", "import": "Tuo", "info": "Tietoja", - "insert": "Lisää", + "insert": "Lis\u00e4\u00e4", "insert.after": "Lisää eteen", "insert.before": "Lisää jälkeen", "install": "Asenna", @@ -324,7 +426,6 @@ "installation.issues.mbstring": "MB String-laajennos on pakollinen", "installation.issues.media": "/media -kansio ei ole olemassa tai siihen ei voi kirjoittaa", "installation.issues.php": "Varmista että PHP 8+ on käytössä", - "installation.issues.server": "Kirby tarvitsee jonkun seuraavista: Apache, Nginx tai Caddy", "installation.issues.sessions": "/site/sessions -kansio ei ole olemassa tai siihen ei voi kirjoittaa", "language": "Kieli", @@ -332,6 +433,7 @@ "language.convert": "Muuta oletukseksi", "language.convert.confirm": "

Haluatko varmasti muuttaa kielen {name} oletuskieleksi? Tätä muutosta ei voi peruuttaa.

Jos{name} sisältää kääntämättömiä kohtia, varakäännöstä ei enää ole näille kohdille ja sivustosi saattaa olla osittain tyhjä.

", "language.create": "Lisää uusi kieli", + "language.default": "Oletuskieli", "language.delete.confirm": "Haluatko varmasti poistaa kielen {name}, mukaanlukien kaikki käännökset? Tätä toimintoa ei voi peruuttaa!", "language.deleted": "Kieli on poistettu", "language.direction": "Lukusuunta", @@ -340,7 +442,16 @@ "language.locale": "PHP-aluemäärityksen tunniste", "language.locale.warning": "Käytät mukautettua aluemääritystä. Muokkaa sitä kielitiedostossa /site/languages", "language.name": "Nimi", + "language.secondary": "Secondary language", + "language.settings": "Language settings", "language.updated": "Kieli on päivitetty", + "language.variables": "Language variables", + "language.variables.empty": "No translations yet", + + "language.variable.delete.confirm": "Do you really want to delete the variable for {key}?", + "language.variable.key": "Key", + "language.variable.notFound": "The variable could not be found", + "language.variable.value": "Value", "languages": "Kielet", "languages.default": "Oletuskieli", @@ -349,15 +460,32 @@ "languages.secondary.empty": "Toissijaisia kieliä ei ole vielä määritetty", "license": "Lisenssi", + "license.activate": "Activate it now", + "license.activate.label": "Please activate your license", + "license.activate.domain": "Your license will be activated for {host}.", + "license.activate.local": "You are about to activate your Kirby license for your local domain {host}. If this site will be deployed to a public domain, please activate it there instead. If {host} is the domain you want to use your license for, please continue.", + "license.activated": "Activated", "license.buy": "Osta lisenssi", - "license.register": "Rekisteröi", + "license.code": "Tunniste", + "license.code.help": "You received your license code after the purchase via email. Please copy and paste it here.", + "license.code.label": "Anna lisenssiavain", + "license.status.active.info": "Includes new major versions until {date}", + "license.status.active.label": "Valid license", + "license.status.demo.info": "This is a demo installation", + "license.status.demo.label": "Demo", + "license.status.inactive.info": "Renew license to update to new major versions", + "license.status.inactive.label": "No new major versions", + "license.status.legacy.bubble": "Ready to renew your license?", + "license.status.legacy.info": "Your license does not cover this version", + "license.status.legacy.label": "Please renew your license", + "license.status.missing.bubble": "Ready to launch your site?", + "license.status.missing.info": "No valid license", + "license.status.missing.label": "Please activate your license", + "license.status.unknown.info": "The license status is unknown", + "license.status.unknown.label": "Unknown", "license.manage": "Hallinnoi lisenssejäsi", - "license.register.help": "Lisenssiavain on lähetetty oston jälkeen sähköpostiisi. Kopioi ja liitä avain tähän.", - "license.register.label": "Anna lisenssiavain", - "license.register.domain": "Your license will be registered to {host}.", - "license.register.local": "You are about to register your license for your local domain {host}. If this site will be deployed to a public domain, please register it there instead. If {host} is the domain you want to license Kirby to, please continue.", - "license.register.success": "Kiitos kun tuet Kirbyä", - "license.unregistered": "Tämä on rekisteröimätön demo Kirbystä", + "license.purchased": "Purchased", + "license.success": "Kiitos kun tuet Kirbyä", "license.unregistered.label": "Rekisteröimätön", "link": "Linkki", @@ -367,17 +495,21 @@ "lock.unsaved": "Tallentamattomia muutoksia", "lock.unsaved.empty": "Ei enempää tallentamattomia muutoksia ", - "lock.isLocked": "Käyttäjällä {email} on tallentamattomia muutoksia", - "lock.file.isLocked": "Tiedostoa ei voi muokata juuri nyt, sillä {email} on muokkaamassa tiedostoa.", - "lock.page.isLocked": "Sivua ei voi muokata juuri nyt, sillä {email} on muokkaamassa sivua.", + "lock.unsaved.files": "Unsaved files", + "lock.unsaved.pages": "Unsaved pages", + "lock.unsaved.users": "Unsaved accounts", + "lock.isLocked": "Unsaved changes by {email}", "lock.unlock": "Vapauta", - "lock.isUnlocked": "Toinen käyttäjä ylikirjoitti tallentamattomat muutoksesi. Voit ladata tekemäsi muutokset ja lisätä ne käsin.", + "lock.unlock.submit": "Unlock and overwrite unsaved changes by {email}", + "lock.isUnlocked": "Was unlocked by another user", "login": "Kirjaudu", "login.code.label.login": "Kirjautumiskoodi", "login.code.label.password-reset": "Salasanan asetuskoodi", "login.code.placeholder.email": "000 000", + "login.code.placeholder.totp": "000000", "login.code.text.email": "Jos sähköpostiosoitteesi on rekisteröity, tilaamasi koodi lähetetään tähän osoitteeseen.", + "login.code.text.totp": "Please enter the one‑time code from your authenticator app.", "login.email.login.body": "Hi {user.nameOrEmail},\n\nYou recently requested a login code for the Panel of {site}.\nThe following login code will be valid for {timeout} minutes:\n\n{code}\n\nIf you did not request a login code, please ignore this email or contact your administrator if you have questions.\nFor security, please DO NOT forward this email.", "login.email.login.subject": "Kirjautumiskoodisi", "login.email.password-reset.body": "Hi {user.nameOrEmail},\n\nYou recently requested a password reset code for the Panel of {site}.\nThe following password reset code will be valid for {timeout} minutes:\n\n{code}\n\nIf you did not request a password reset code, please ignore this email or contact your administrator if you have questions.\nFor security, please DO NOT forward this email.", @@ -388,9 +520,24 @@ "login.toggleText.code.email-password": "Kirjaudu salasanalla", "login.toggleText.password-reset.email": "Unohditko salasanasi?", "login.toggleText.password-reset.email-password": "← Takaisin kirjautumiseen", + "login.totp.enable.option": "Set up one‑time codes", + "login.totp.enable.intro": "Authenticator apps can generate one‑time codes that are used as a second factor when signing into your account.", + "login.totp.enable.qr.label": "1. Scan this QR code", + "login.totp.enable.qr.help": "Unable to scan? Add the setup key {secret} manually to your authenticator app.", + "login.totp.enable.confirm.headline": "2. Confirm with generated code", + "login.totp.enable.confirm.text": "Your app generates a new one‑time code every 30 seconds. Enter the current code to complete the setup:", + "login.totp.enable.confirm.label": "Current code", + "login.totp.enable.confirm.help": "After this setup, we will ask you for a one‑time code every time you log in.", + "login.totp.enable.success": "One‑time codes enabled", + "login.totp.disable.option": "Disable one‑time codes", + "login.totp.disable.label": "Enter your password to disable one‑time codes", + "login.totp.disable.help": "In the future, a different second factor like a login code sent via email will be requested when you log in. You can always set up one‑time codes again later.", + "login.totp.disable.admin": "

This will disable one‑time codes for {user}.

In the future, a different second factor like a login code sent via email will be requested when they log in. {user} can set up one‑time codes again after their next login.

", + "login.totp.disable.success": "One‑time codes disabled", "logout": "Kirjaudu ulos", + "merge": "Merge", "menu": "Valikko", "meridiem": "am/pm", "mime": "Median tyyppi", @@ -402,8 +549,8 @@ "months.december": "Joulukuu", "months.february": "Helmikuu", "months.january": "Tammikuu", - "months.july": "Heinäkuu", - "months.june": "Kesäkuu", + "months.july": "Hein\u00e4kuu", + "months.june": "Kes\u00e4kuu", "months.march": "Maaliskuu", "months.may": "Toukokuu", "months.november": "Marraskuu", @@ -411,21 +558,26 @@ "months.september": "Syyskuu", "more": "Lisää", + "move": "Move", "name": "Nimi", "next": "Seuraava", + "night": "Night", "no": "ei", "off": "Pois käytöstä", "on": "Käytössä", "open": "Avaa", "open.newWindow": "Avaa uudessa ikkunassa", + "option": "Option", "options": "Asetukset", "options.none": "Ei valintoja", + "options.all": "Show all {count} options", "orientation": "Suunta", "orientation.landscape": "Vaakasuuntainen", "orientation.portrait": "Pystysuuntainen", "orientation.square": "Neliskulmainen", + "page": "Page", "page.blueprint": "Tällä sivulla ei ole vielä suunnitelmaa. Voit määrittää suunnitelman tiedostoon /site/blueprints/pages/{blueprint}.yml", "page.changeSlug": "Vaihda URL-osoite", "page.changeSlug.fromTitle": "Luo nimen perusteella", @@ -433,13 +585,15 @@ "page.changeStatus.position": "Valitse järjestyspaikka", "page.changeStatus.select": "Valitse uusi tila", "page.changeTemplate": "Vaihda sivupohja", + "page.changeTemplate.notice": "Changing the page's template will remove content for fields that don't match in type. Use with caution.", + "page.create": "Create as {status}", "page.delete.confirm": "Haluatko varmasti poistaa sivun {title}?", "page.delete.confirm.subpages": "Tällä sivulla on alasivuja.
Myös kaikki alasivut poistetaan.", "page.delete.confirm.title": "Anna vahvistuksena sivun nimi", - "page.draft.create": "Uusi luonnos", "page.duplicate.appendix": "Kopioi", "page.duplicate.files": "Kopioi tiedostot", "page.duplicate.pages": "Kopioi sivut", + "page.move": "Move page", "page.sort": "Muuta järjestyspaikkaa", "page.status": "Tila", "page.status.draft": "Luonnos", @@ -450,29 +604,37 @@ "page.status.unlisted.description": "Sivulle pääsee vain URL:n kautta", "pages": "Sivut", + "pages.delete.confirm.selected": "Do you really want to delete the selected pages? This action cannot be undone.", "pages.empty": "Sivuja ei ole vielä lisätty", "pages.status.draft": "Luonnokset", "pages.status.listed": "Julkaistut", "pages.status.unlisted": "Listaamaton", - "pagination.page": "Page", + "pagination.page": "Sivu", "password": "Salasana", "paste": "Liitä", "paste.after": "Liitä jälkeen", + "paste.success": "{count} pasted!", "pixel": "Pikseli", "plugin": "Liitännäinen", "plugins": "Liitännäiset", "prev": "Edellinen", "preview": "Esikatselu", + + "publish": "Publish", + "published": "Julkaistut", + "remove": "Poista", "rename": "Nimeä uudelleen", + "renew": "Renew", "replace": "Korvaa", - "retry": "Yritä uudelleen", + "replace.with": "Replace with", + "retry": "Yrit\u00e4 uudelleen", "revert": "Palauta", "revert.confirm": "Haluatko varmasti poistaa kaikki tallentamattomat muutokset?", - "role": "Käyttäjätaso", + "role": "K\u00e4ytt\u00e4j\u00e4taso", "role.admin.description": "Pääkäyttäjällä on kaikki oikeudet", "role.admin.title": "Pääkäyttäjä", "role.all": "Kaikki", @@ -482,11 +644,14 @@ "role.nobody.title": "Tuntematon", "save": "Tallenna", + "saved": "Saved", "search": "Haku", + "searching": "Searching", "search.min": "Anna vähintään {min} merkkiä hakua varten", - "search.all": "Näytä kaikki", + "search.all": "Show all {count} results", "search.results.none": "Ei tuloksia", + "section.invalid": "The section is invalid", "section.required": "Osio on pakollinen", "security": "Tietoturva", @@ -498,16 +663,25 @@ "size": "Koko", "slug": "URL-tunniste", "sort": "Järjestele", + "sort.drag": "Drag to sort …", + "split": "Split", "stats.empty": "Ei raportteja", + "status": "Tila", + + "system.info.copy": "Copy info", + "system.info.copied": "System info copied", "system.issues.content": "Content-kansio näyttäisi olevan julkinen", "system.issues.eol.kirby": "Your installed Kirby version has reached end-of-life and will not receive further security updates", "system.issues.eol.plugin": "Your installed version of the { plugin } plugin is has reached end-of-life and will not receive further security updates", + "system.issues.eol.php": "Your installed PHP release { release } has reached end-of-life and will not receive further security updates", "system.issues.debug": "Virheenkäsittelytila pitää poistaa käytöstä tuotantoympäristössä", "system.issues.git": ".git-kansio näyttäisi olevan julkinen", "system.issues.https": "Suosittelemme HTTPS:n käyttöä kaikilla sivustoillasi", "system.issues.kirby": "Kirby-kansio näyttäisi olevan julkinen", + "system.issues.local": "The site is running locally with relaxed security checks", "system.issues.site": "Site-kansio näyttäisi olevan julkinen", + "system.issues.vue.compiler": "The Vue template compiler is enabled", "system.issues.vulnerability.kirby": "Asennuksesi voi olla altis seuraaville haavoittuvuuksille ({ severity } vakavuus): { description }", "system.issues.vulnerability.plugin": "Asennuksesi käyttämä liitännäinen { plugin } voi olla altis haavoittuvuudelle ({ severity } vakavuus): { description }", "system.updateStatus": "Päivitysten tilanne", @@ -520,13 +694,22 @@ "system.updateStatus.update": "Ilmainen päivitys { version } saatavilla", "system.updateStatus.upgrade": "Päivitys { version } saatavilla", - "title": "Nimi", + "tel": "Phone", + "tel.placeholder": "+49123456789", "template": "Sivupohja", + + "theme": "Theme", + "theme.light": "Lights on", + "theme.dark": "Lights off", + "theme.automatic": "Match system default", + + "title": "Nimi", "today": "Tänään", - "toolbar.button.code": "Tunniste", + "toolbar.button.clear": "Clear formatting", + "toolbar.button.code": "Koodi", "toolbar.button.bold": "Lihavointi", - "toolbar.button.email": "Sähköposti", + "toolbar.button.email": "S\u00e4hk\u00f6posti", "toolbar.button.headings": "Otsikot", "toolbar.button.heading.1": "Otsikko 1", "toolbar.button.heading.2": "Otsikko 2", @@ -535,12 +718,14 @@ "toolbar.button.heading.5": "Otsikko 5", "toolbar.button.heading.6": "Otsikko 6", "toolbar.button.italic": "Kursivointi", - "toolbar.button.file": "File", + "toolbar.button.file": "Tiedosto", "toolbar.button.file.select": "Valitse tiedosto", "toolbar.button.file.upload": "Lähetä tiedosto", "toolbar.button.link": "Linkki", "toolbar.button.paragraph": "Kappale", "toolbar.button.strike": "Yliviivaus", + "toolbar.button.sub": "Subscript", + "toolbar.button.sup": "Superscript", "toolbar.button.ol": "Järjestetty lista", "toolbar.button.underline": "Alaviiva", "toolbar.button.ul": "Järjestämätön lista", @@ -550,6 +735,8 @@ "translation.name": "Suomi", "translation.locale": "fi_FI", + "type": "Type", + "upload": "Lähetä", "upload.error.cantMove": "Lähetettyä tiedostoa ei voitu siirtää", "upload.error.cantWrite": "Tiedoston kirjoitus levylle epäonnistui", @@ -562,7 +749,7 @@ "upload.error.noFiles": "Tiedostoja ei lähetetty", "upload.error.partial": "Tiedoston lähetys onnistui vain osittain", "upload.error.tmpDir": "Väliaikainen hakemisto puuttuu", - "upload.errors": "Error", + "upload.errors": "Virhe", "upload.progress": "Lähetetään...", "url": "Url", @@ -574,6 +761,7 @@ "user.changeLanguage": "Vaihda kieli", "user.changeName": "Nimeä uudelleen", "user.changePassword": "Vaihda salasana", + "user.changePassword.current": "Your current password", "user.changePassword.new": "Uusi salasana", "user.changePassword.new.confirm": "Vahvista uusi salasana...", "user.changeRole": "Muuta käyttäjätasoa", @@ -585,17 +773,20 @@ "users": "Käyttäjät", "version": "Versio", + "version.changes": "Changed version", + "version.compare": "Compare versions", "version.current": "Nykyinen versio ", "version.latest": "Uusin versio ", "versionInformation": "Version tiedot", + "view": "View", "view.account": "Oma käyttäjätili", "view.installation": "Asennus", "view.languages": "Kielet", "view.resetPassword": "Aseta salasana", "view.site": "Sivusto", "view.system": "Järjestelmä", - "view.users": "Käyttäjät", + "view.users": "K\u00e4ytt\u00e4j\u00e4t", "welcome": "Tervetuloa", "year": "Vuosi", diff --git a/kirby/i18n/translations/fr.json b/kirby/i18n/translations/fr.json index 8cfb56d..769a184 100644 --- a/kirby/i18n/translations/fr.json +++ b/kirby/i18n/translations/fr.json @@ -3,19 +3,28 @@ "account.delete": "Supprimer votre compte", "account.delete.confirm": "Voulez-vous vraiment supprimer votre compte ? Vous serez déconnecté immédiatement. Votre compte ne pourra pas être récupéré.", + "activate": "Activer", "add": "Ajouter", + "alpha": "Alpha", "author": "Auteur", - "avatar": "Photo de profil", + "avatar": "Image de profil", "back": "Retour", "cancel": "Annuler", "change": "Changer", "close": "Fermer", + "changes": "Modifications", "confirm": "Ok", "collapse": "Replier", "collapse.all": "Tout replier", + "color": "Couleur", + "coordinates": "Coordonnées", "copy": "Copier", "copy.all": "Tout copier", + "copy.success": "Copié", + "copy.success.multiple": "Copié : {count}", + "copy.url": "Copier l’URL", "create": "Créer", + "custom": "Personnalisé", "date": "Date", "date.select": "Choisir une date", @@ -34,13 +43,20 @@ "delete": "Supprimer", "delete.all": "Tout supprimer", + "dialog.fields.empty": "Ce dialogue ne comporte aucun champ", "dialog.files.empty": "Aucun fichier à sélectionner", "dialog.pages.empty": "Aucune page à sélectionner", + "dialog.text.empty": "Ce dialogue ne définit aucun texte", "dialog.users.empty": "Aucun utilisateur à sélectionner", "dimensions": "Dimensions", + "disable": "Désactiver", "disabled": "Désactivé", "discard": "Supprimer", + + "drawer.fields.empty": "Ce tiroir ne comporte aucun champ", + + "domain": "Domaine", "download": "Télécharger", "duplicate": "Dupliquer", @@ -49,18 +65,20 @@ "email": "Courriel", "email.placeholder": "mail@example.com", + "enter": "Entrer", "entries": "Entrées", "entry": "Entrée", "environment": "Environnement", + "error": "Erreur", "error.access.code": "Code incorrect", "error.access.login": "Identifiant incorrect", "error.access.panel": "Vous n’êtes pas autorisé à accéder au Panel", "error.access.view": "Vous n’êtes pas autorisé à accéder à cette section du Panel", "error.avatar.create.fail": "L’image du profil n’a pu être transférée", - "error.avatar.delete.fail": "La photo de profil n‘a pu être supprimée", + "error.avatar.delete.fail": "L’image du profil n’a pu être supprimée", "error.avatar.dimensions.invalid": "Veuillez choisir une image de profil de largeur et hauteur inférieures à 3000 pixels", "error.avatar.mime.forbidden": "L'image du profil utilisateur doit être un fichier JPEG ou PNG", @@ -74,30 +92,50 @@ "error.cache.type.invalid": "Type de cache invalide « {type} »", + "error.content.lock.delete": "Cette version est verrouillée et ne peut être supprimée", + "error.content.lock.move": "Cette version de la source est verrouillée et ne peut être supprimée", + "error.content.lock.publish": "Cette version est déjà publiée", + "error.content.lock.replace": "Cette version est verrouillée et ne peut être remplacée", + "error.content.lock.update": "Cette version est verrouillée et ne peut être mise à jour", + + "error.entries.max.plural": "Vous ne devez pas ajouter plus de {max} entrées", + "error.entries.max.singular": "Vous ne devez pas ajouter plus d’une entrée", + "error.entries.min.plural": "Vous devez ajouter au moins {min} entrées", + "error.entries.min.singular": "Vous devez ajouter au moins une entrée", + "error.entries.supports": "Le champ de type « {type} » n’est pas pris en charge par le champ entrées", + "error.entries.validation": "Il y a une erreur dans le champ « {field} » de la rangée {index}", + "error.email.preset.notFound": "La configuration de courriel « {name} » n’a pu être trouvé ", "error.field.converter.invalid": "Convertisseur « {converter} » invalide", + "error.field.link.options": "Options invalides : {options}", "error.field.type.missing": "Champ « { name } » : Le type de champ « { type } » n’existe pas", "error.file.changeName.empty": "Le nom ne peut être vide", "error.file.changeName.permission": "Vous n’êtes pas autorisé à modifier le nom de « {filename} »", + "error.file.changeTemplate.invalid": "Le modèle du fichier « {id} » ne peut être modifié en « {template} » (valide : « {blueprints} »)", + "error.file.changeTemplate.permission": "Vous n’êtes pas autorisé à changer le modèle du fichier « {id} »", + + "error.file.delete.multiple": "Tous les fichiers n’ont pu être supprimés. Essayez avec chaque fichier restant individuellement pour voir quelle erreur empêche sa suppression.", "error.file.duplicate": "Un fichier nommé « {filename} » existe déjà", "error.file.extension.forbidden": "L’extension « {extension} » n’est pas autorisée", "error.file.extension.invalid": "Extension incorrecte : {extension}", "error.file.extension.missing": "L’extension pour « {filename} » est manquante", "error.file.maxheight": "La hauteur de l'image ne doit pas excéder {height} pixels", "error.file.maxsize": "Le fichier est trop volumineux", - "error.file.maxwidth": "La largeur de l'image ne doit pas excéder {width} pixels", + "error.file.maxwidth": "La largeur de l’image ne doit pas excéder {width} pixels", "error.file.mime.differs": "Le fichier transféré doit être du même type de média « {mime} »", "error.file.mime.forbidden": "Le type de média « {mime} » n’est pas autorisé", "error.file.mime.invalid": "Type de média invalide : {mime}", "error.file.mime.missing": "Le type de média de « {filename} » n’a pu être détecté", - "error.file.minheight": "La hauteur de l'image doit être au moins {height} pixels", - "error.file.minsize": "Le fichier n'est pas assez volumineux", - "error.file.minwidth": "La largeur de l'image doit être au moins {width} pixels", + "error.file.minheight": "La hauteur de l’image doit être au moins {height} pixels", + "error.file.minsize": "Le fichier n’est pas assez volumineux", + "error.file.minwidth": "La largeur de l’image doit être au moins {width} pixels", + "error.file.name.unique": "Le nom de fichier doit être unique", "error.file.name.missing": "Veuillez entrer un titre", "error.file.notFound": "Le fichier « {filename} » n’a pu être trouvé", - "error.file.orientation": "L'orientation de l'image doit être « {orientation} »", + "error.file.orientation": "L’orientation de l'image doit être « {orientation} »", + "error.file.sort.permission": "Vous n’êtes pas autorisé à modifier le tri de « {filename} »", "error.file.type.forbidden": "Vous n’êtes pas autorisé à transférer des fichiers {type}", "error.file.type.invalid": "Type de fichier invalide : {type}", "error.file.undefined": "Le fichier n’a pu être trouvé", @@ -105,23 +143,31 @@ "error.form.incomplete": "Veuillez corriger toutes les erreurs du formulaire…", "error.form.notSaved": "Le formulaire n’a pu être enregistré", - "error.language.code": "Veuillez saisir un code correct pour cette langue", + "error.language.code": "Veuillez saisir un code correct pour la langue", + "error.language.create.permission": "Vous n’êtes pas autorisé à créer une langue", + "error.language.delete.permission": "Vous n’êtes pas autorisé à supprimer la langue", "error.language.duplicate": "Cette langue existe déjà", - "error.language.name": "Veuillez saisir un nom correct pour cette langue", + "error.language.name": "Veuillez saisir un nom correct pour la langue", "error.language.notFound": "La langue n’a pu être trouvée", + "error.language.update.permission": "Vous n’êtes pas autorisé à modifier la langue", "error.layout.validation.block": "Il y a une erreur sur le champ « {field} » du bloc {blockIndex} utilisant le type de bloc « {fieldset} » dans le layout {layoutIndex}.", "error.layout.validation.settings": "Il y a une erreur dans les paramètres de la disposition {index}", - "error.license.format": "Veuillez saisir un numéro de licence correct", + "error.license.domain": "Le domaine de la licence est manquant", "error.license.email": "Veuillez saisir un courriel correct", + "error.license.format": "Veuillez saisir un numéro de licence valide", "error.license.verification": "La licence n’a pu être vérifiée", + "error.login.totp.confirm.invalid": "Code invalide", + "error.login.totp.confirm.missing": "Veuillez saisir le code actuel", + "error.object.validation": "Il y a une erreur dans le champ « {label} » :\n{message}", "error.offline": "Le Panel est actuellement hors ligne", "error.page.changeSlug.permission": "Vous n’êtes pas autorisé à modifier l’identifiant d’URL pour « {slug} »", + "error.page.changeSlug.reserved": "Le chemin des pages de premier niveau ne doit pas commencer par « {path} »", "error.page.changeStatus.incomplete": "La page comporte des erreurs et ne peut pas être publiée", "error.page.changeStatus.permission": "Le statut de cette page ne peut être modifié", "error.page.changeStatus.toDraft.invalid": "La page « {slug} » ne peut être convertie en brouillon", @@ -133,13 +179,21 @@ "error.page.delete": "La page « {slug} » ne peut être supprimée", "error.page.delete.confirm": "Veuillez saisir le titre de la page pour confirmer", "error.page.delete.hasChildren": "La page comporte des sous-pages et ne peut pas être supprimée", + "error.page.delete.multiple": "Toutes les pages n’ont pu être supprimées. Essayez avec chaque page restante individuellement pour voir quelle erreur empêche sa suppression.", "error.page.delete.permission": "Vous n’êtes pas autorisé à supprimer « {slug} »", "error.page.draft.duplicate": "Un brouillon avec l’identifiant d’URL « {slug} » existe déjà", "error.page.duplicate": "Une page avec l’identifiant d’URL « {slug} » existe déjà", - "error.page.duplicate.permission": "Vous n'êtes pas autorisé à dupliquer « {slug} »", + "error.page.duplicate.permission": "Vous n’êtes pas autorisé à dupliquer « {slug} »", + "error.page.move.ancestor": "La page ne peut être déplacée à l’intérieur d’elle-même", + "error.page.move.directory": "Le répertoire de la page ne peut être déplacé", + "error.page.move.duplicate": "Une sous-page possédant l’identifiant d’URL « {slug} » existe déjà", + "error.page.move.noSections": "La page « {parent} » ne peut être parente d'autres pages car elle ne comporte pas de section de pages dans son blueprint", + "error.page.move.notFound": "La page déplacée n’a pu être trouvée", + "error.page.move.permission": "Vous n’êtes pas autorisé à déplacer « {slug} » ", + "error.page.move.template": "Le modèle « {template} » n’est pas accepté en tant que sous-page de « {parent} »", "error.page.notFound": "La page « {slug} » n’a pu être trouvée", "error.page.num.invalid": "Veuillez saisir un numéro de position correct. Les numéros ne doivent pas être négatifs.", - "error.page.slug.invalid": "Veuillez saisir un identifiant d’URL correct", + "error.page.slug.invalid": "Veuillez entrer un identifiant d’URL correct", "error.page.slug.maxlength": "L’identifiant d’URL doit faire moins de « {length} » caractères", "error.page.sort.permission": "La page « {slug} » ne peut être réordonnée", "error.page.status.invalid": "Veuillez choisir un statut de page correct", @@ -163,9 +217,11 @@ "error.site.changeTitle.permission": "Vous n’êtes pas autorisé à modifier le titre du site", "error.site.update.permission": "Vous n’êtes pas autorisé à modifier le contenu global du site", + "error.structure.validation": "Il y a une erreur dans le champ « {field} » de la rangée {index}", + "error.template.default.notFound": "Le modèle par défaut n’existe pas", - "error.unexpected": "Une erreur inattendue est survenue ! Activez le mode de débogage pour plus d'informations : https://getkirby.com/docs/reference/system/options/debug", + "error.unexpected": "Une erreur inattendue est survenue ! Activez le mode de débogage pour plus d’informations : https://getkirby.com/docs/reference/system/options/debug", "error.user.changeEmail.permission": "Vous n’êtes pas autorisé à modifier le courriel de l’utilisateur « {name} »", "error.user.changeLanguage.permission": "Vous n’êtes pas autorisé à changer la langue de l’utilisateur « {name} »", @@ -195,8 +251,10 @@ "error.validation.accepted": "Veuillez confirmer", "error.validation.alpha": "Veuillez saisir uniquement des caractères alphabétiques minuscules", "error.validation.alphanum": "Veuillez ne saisir que des minuscules de a à z et des chiffres de 0 à 9", + "error.validation.anchor": "Veuillez entrer un lien ancré correct", "error.validation.between": "Veuillez saisir une valeur entre « {min} » et « {max} »", "error.validation.boolean": "Veuillez confirmer ou refuser", + "error.validation.color": "Veuillez entrer une couleur valide dans le format {format}", "error.validation.contains": "Veuillez saisir une valeur contenant « {needle} »", "error.validation.date": "Veuillez saisir une date correcte", "error.validation.date.after": "Veuillez saisir une date après {date}", @@ -211,6 +269,7 @@ "error.validation.integer": "Veuillez saisir un entier correct", "error.validation.ip": "Veuillez saisir une adresse IP correcte", "error.validation.less": "Veuillez saisir une valeur inférieure à {max}", + "error.validation.linkType": "Le type de lien n’est pas autorisé", "error.validation.match": "La valeur ne correspond pas au modèle attendu", "error.validation.max": "Veuillez saisir une valeur inférieure ou égale à {max}", "error.validation.maxlength": "Veuillez saisir une valeur plus courte (max. {max} caractères)", @@ -227,15 +286,18 @@ "error.validation.same": "Veuillez saisir « {other} »", "error.validation.size": "La grandeur de la valeur doit être « {size} »", "error.validation.startswith": "La valeur doit commencer par « {start} »", + "error.validation.tel": "Veuillez saisir un numéro de téléphone non formaté", "error.validation.time": "Veuillez saisir une heure correcte", "error.validation.time.after": "Veuillez saisir une heure après {time}", "error.validation.time.before": "Veuillez saisir une heure avant {time}", "error.validation.time.between": "Veuillez saisir une heure entre {min} et {max}", + "error.validation.uuid": "Veuillez saisir un UUID valide", "error.validation.url": "Veuillez saisir une URL correcte", "expand": "Déplier", "expand.all": "Tout déplier", + "field.invalid": "Le champ est invalide", "field.required": "Le champ est obligatoire", "field.blocks.changeType": "Changer le type", "field.blocks.code.name": "Code", @@ -245,8 +307,9 @@ "field.blocks.delete.confirm.all": "Voulez-vous vraiment supprimer tous les blocs ?", "field.blocks.delete.confirm.selected": "Voulez-vous vraiment supprimer les blocs sélectionnés ?", "field.blocks.empty": "Pas encore de blocs", + "field.blocks.fieldsets.empty": "Pas encore d‘ensembles de champs", "field.blocks.fieldsets.label": "Veuillez sélectionner un type de bloc…", - "field.blocks.fieldsets.paste": "Presser {{ shortcut }} pour coller/importer des blocks depuis votre presse-papier", + "field.blocks.fieldsets.paste": "Pressez {{ shortcut }} pour importer des dispositions ou blocs depuis votre presse-papier Seuls ceux autorisés dans le champ actuel seront insérés.", "field.blocks.gallery.name": "Galerie", "field.blocks.gallery.images.empty": "Pas encore d’images", "field.blocks.gallery.images.label": "Images", @@ -254,15 +317,20 @@ "field.blocks.heading.name": "Titre", "field.blocks.heading.text": "Texte", "field.blocks.heading.placeholder": "Titre…", + "field.blocks.figure.back.plain": "Brut", + "field.blocks.figure.back.pattern.light": "Motif (clair)", + "field.blocks.figure.back.pattern.dark": "Motif (sombre)", "field.blocks.image.alt": "Texte alternatif", "field.blocks.image.caption": "Légende", "field.blocks.image.crop": "Recadrer", "field.blocks.image.link": "Lien", "field.blocks.image.location": "Emplacement", + "field.blocks.image.location.internal": "Ce site web", + "field.blocks.image.location.external": "Source externe", "field.blocks.image.name": "Image", "field.blocks.image.placeholder": "Sélectionnez une image", "field.blocks.image.ratio": "Proportions", - "field.blocks.image.url": "URL de l'image", + "field.blocks.image.url": "URL de l’image", "field.blocks.line.name": "Ligne", "field.blocks.list.name": "Liste", "field.blocks.markdown.name": "Markdown", @@ -275,38 +343,72 @@ "field.blocks.quote.citation.placeholder": "par…", "field.blocks.text.name": "Texte", "field.blocks.text.placeholder": "Texte…", + "field.blocks.video.autoplay": "Lecture automatique", "field.blocks.video.caption": "Légende", + "field.blocks.video.controls": "Contrôles", + "field.blocks.video.location": "Emplacement", + "field.blocks.video.loop": "Boucle", + "field.blocks.video.muted": "Muet", "field.blocks.video.name": "Vidéo", "field.blocks.video.placeholder": "Saisissez l’URL d’une vidéo", + "field.blocks.video.poster": "Vignette", + "field.blocks.video.preload": "Préchargement", "field.blocks.video.url.label": "URL de la vidéo", "field.blocks.video.url.placeholder": "https://youtube.com/?v=", - "field.files.empty": "Pas encore de fichier sélectionné", + "field.entries.delete.confirm.all": "Voulez-vous vraiment supprimer toutes les entrées ?", + "field.entries.empty": "Pas encore d’entrée", + "field.files.empty": "Pas encore de fichier sélectionné", + "field.files.empty.single": "Pas encore de fichier sélectionné", + + "field.layout.change": "Changer de disposition", "field.layout.delete": "Supprimer cette disposition", "field.layout.delete.confirm": "Voulez-vous vraiment supprimer cette disposition ?", + "field.layout.delete.confirm.all": "Voulez-vous vraiment supprimer toutes les dispositions ?", "field.layout.empty": "Pas encore de rangées", "field.layout.select": "Choisir une disposition", "field.object.empty": "Pas encore d‘information", "field.pages.empty": "Pas encore de page sélectionnée", + "field.pages.empty.single": "Pas encore de page sélectionnée", "field.structure.delete.confirm": "Voulez-vous vraiment supprimer cette ligne ?", "field.structure.delete.confirm.all": "Voulez-vous vraiment supprimer toutes les entrées ?", "field.structure.empty": "Pas encore d’entrée", "field.users.empty": "Pas encore d’utilisateur sélectionné", + "field.users.empty.single": "Pas encore d’utilisateur sélectionné", + "fields.empty": "Pas encore de champs", + + "file": "Fichier", "file.blueprint": "Ce fichier n’a pas encore de blueprint. Vous pouvez en définir les paramètres dans /site/blueprints/files/{blueprint}.yml", + "file.changeTemplate": "Changer de modèle", + "file.changeTemplate.notice": "Modifier le modèle du fichier supprimera le contenu des champs dont le type ne correspond pas. Si le nouveau modèle définit certaines règles, par exemple les dimensions des images, celles-ci seront également appliquées de manière irréversible. Utilisez avec précaution.", "file.delete.confirm": "Voulez-vous vraiment supprimer
{filename} ?", + "file.focus.placeholder": "Définir le point focal", + "file.focus.reset": "Supprimer le point focal", + "file.focus.title": "Point focal", "file.sort": "Modifier la position", "files": "Fichiers", + "files.delete.confirm.selected": "Voulez-vous vraiment supprimer le fichier sélectionné ? Cette action ne peut être annulée.", "files.empty": "Pas encore de fichier", + "filter": "Filtrer", + + "form.discard": "Annuler les modifications", + "form.discard.confirm": "Voulez-vous vraiment annuler toutes les modifications ?", + "form.locked": "Ce contenu est désactivé pour vous car il est actuellement édité par un autre utilisateur.", + "form.unsaved": "Les modifications actuelles n’ont pas encore été enregistrées", + "form.preview": "Prévisualiser les modifications", + "form.preview.draft": "Prévisualiser le brouillon", + "hide": "Masquer", "hour": "Heure", + "hue": "Teinte", "import": "Importer", "info": "Info", "insert": "Insérer", @@ -316,7 +418,7 @@ "installation": "Installation", "installation.completed": "Le Panel a été installé", - "installation.disabled": "L'installation du Panel est désactivée par défaut sur les serveurs publics. Veuillez lancer l'installation sur un serveur local, ou activez-la avec l'option panel.install.", + "installation.disabled": "L’installation du Panel est désactivée par défaut sur les serveurs publics. Veuillez lancer l’installation sur un serveur local, ou activez-la avec l’option panel.install.", "installation.issues.accounts": "Le dossier /site/accounts n’existe pas ou n’est pas accessible en écriture", "installation.issues.content": "Le dossier /content n’existe pas ou n’est pas accessible en écriture", "installation.issues.curl": "L’extension CURL est requise", @@ -324,7 +426,6 @@ "installation.issues.mbstring": "L’extension MB String est requise", "installation.issues.media": "Le dossier /media n’existe pas ou n’est pas accessible en écriture", "installation.issues.php": "Veuillez utiliser PHP 8+", - "installation.issues.server": "Kirby requiert Apache, Nginx ou Caddy", "installation.issues.sessions": "Le dossier /site/sessions n’existe pas ou n’est pas accessible en écriture", "language": "Langue", @@ -332,6 +433,7 @@ "language.convert": "Choisir comme langue par défaut", "language.convert.confirm": "

Souhaitez-vous vraiment convertir {name} vers la langue par défaut ? Cette action ne peut pas être annulée.

Si {name} a un contenu non traduit, il n’y aura plus de solution de secours possible et certaines parties de votre site pourraient être vides.

", "language.create": "Ajouter une nouvelle langue", + "language.default": "Langue par défaut", "language.delete.confirm": "Voulez-vous vraiment supprimer la langue {name}, ainsi que toutes ses traductions ? Cette action ne peut être annulée !", "language.deleted": "La langue a été supprimée", "language.direction": "Sens de lecture", @@ -340,24 +442,50 @@ "language.locale": "Locales PHP", "language.locale.warning": "Vous utilisez une Locale PHP personnalisée. Veuillez la modifier dans le fichier de langue situé dans /site/languages", "language.name": "Nom", + "language.secondary": "Langue secondaire", + "language.settings": "Préférences de langue", "language.updated": "La langue a été mise à jour", + "language.variables": "Variables de langue", + "language.variables.empty": "Pas encore de traductions", - "languages": "Langues", + "language.variable.delete.confirm": "Voulez-vous vraiment supprimer la variable pour {key} ?", + "language.variable.key": "Clé", + "language.variable.notFound": "La variable n’a pu être trouvée", + "language.variable.value": "Valeur", + + "languages": "Langages", "languages.default": "Langue par défaut", "languages.empty": "Il n’y a pas encore de langues", "languages.secondary": "Langues secondaires", "languages.secondary.empty": "Il n’y a pas encore de langues secondaires", "license": "Licence", + "license.activate": "Activer maintenant", + "license.activate.label": "Veuillez activer votre licence", + "license.activate.domain": "Votre licence sera activée pour {host}.", + "license.activate.local": "Vous êtes sur le point d‘activer votre licence de Kirby pour votre domaine local {host}. Si ce site doit être activé sur un domaine publique, veuillez plutôt l‘activer là-bas. Si {host} est bien le domaine pour lequel vous voulez activer votre licence, veuillez continuer.", + "license.activated": "Activée", "license.buy": "Acheter une licence", - "license.register": "S’enregistrer", + "license.code": "Code", + "license.code.help": "Vous avez reçu votre code de licence par courriel après l‘achat. Veuillez le copier et le coller ici.", + "license.code.label": "Veuillez saisir votre numéro de licence", + "license.status.active.info": "Inclut les nouvelles versions majeures jusqu’au {date}", + "license.status.active.label": "Licence valide", + "license.status.demo.info": "Ceci est une installation de démonstration", + "license.status.demo.label": "Démonstration", + "license.status.inactive.info": "Renouveler la licence pour mettre à jour vers les nouvelles versions majeures", + "license.status.inactive.label": "Pas de nouvelles versions majeures", + "license.status.legacy.bubble": "Prêt à renouveler votre licence ?", + "license.status.legacy.info": "Votre licence ne couvre pas cette version", + "license.status.legacy.label": "Veuillez renouveler votre licence", + "license.status.missing.bubble": "Prêt à lancer votre site ?", + "license.status.missing.info": "Pas de licence valide", + "license.status.missing.label": "Veuillez activer votre licence", + "license.status.unknown.info": "Le statut de la licence est inconnu", + "license.status.unknown.label": "Inconnu", "license.manage": "Gérer vos licences", - "license.register.help": "Vous avez reçu votre numéro de licence par courriel après l'achat. Veuillez le copier et le coller ici pour l'enregistrer.", - "license.register.label": "Veuillez saisir votre numéro de licence", - "license.register.domain": "Votre licence sera enregistrée pour {host}.", - "license.register.local": "Vous êtes sur le point d’enregistrer votre licence pour votre domaine local {host}. Si votre site sera déployé sur un domaine publique, veuillez plutôt l’y l’enregistrer. Si {host} est le domaine pour lequel vous voulez enregistrer Kirby, veuillez continuer.", - "license.register.success": "Merci pour votre soutien à Kirby", - "license.unregistered": "Ceci est une démo non enregistrée de Kirby", + "license.purchased": "Achetée", + "license.success": "Merci pour votre soutien à Kirby", "license.unregistered.label": "Non enregistré", "link": "Lien", @@ -366,31 +494,50 @@ "loading": "Chargement", "lock.unsaved": "Modifications non enregistrées", - "lock.unsaved.empty": "Il n’y a plus de modifications non enregistrées", - "lock.isLocked": "Modifications non enregistrées par {email}", - "lock.file.isLocked": "Le fichier est actuellement édité par {email} et ne peut être modifié.", - "lock.page.isLocked": "La page est actuellement éditée par {email} et ne peut être modifiée.", + "lock.unsaved.empty": "Il n’y a pas de modifications non enregistrées", + "lock.unsaved.files": "Fichiers non enregistrés", + "lock.unsaved.pages": "Pages non enregistrées", + "lock.unsaved.users": "Comptes non enregistrés", + "lock.isLocked": "Modifications non enregistrées par {email}", "lock.unlock": "Déverrouiller", - "lock.isUnlocked": "Vos modifications non enregistrées ont été écrasées pas un autre utilisateur. Vous pouvez télécharger vos modifications pour les fusionner manuellement.", + "lock.unlock.submit": "Déverrouiller et écraser les modifications non enregistrées par {email}", + "lock.isUnlocked": "A été déverrouillé par un autre utilisateur", "login": "Connexion", "login.code.label.login": "Code de connexion", "login.code.label.password-reset": "Code de réinitialisation du mot de passe", "login.code.placeholder.email": "000 000", + "login.code.placeholder.totp": "000000", "login.code.text.email": "Si votre adresse de courriel est enregistrée, le code demandé vous sera envoyé par courriel.", + "login.code.text.totp": "Veuillez saisir le code à usage unique de votre application d‘authentification", "login.email.login.body": "Bonjour {user.nameOrEmail},\n\nVous avez récemment demandé un code de connexion pour le Panel de {site}.\nLe code de connexion suivant sera valable pendant {timeout} minutes :\n\n{code}\n\nSi vous n’avez pas demandé de code de connexion, veuillez ignorer cet email ou contacter votre administrateur si vous avez des questions.\nPar sécurité, merci de ne PAS faire suivre cet email.", "login.email.login.subject": "Votre code de connexion", - "login.email.password-reset.body": "Bonjour {user.nameOrEmail},\n\nVous avez récemment demandé un code de réinitialisation de mot de passe pour le Panel de {site}.\nLe code de réinitialisation de mot de passe suivant sera valable pendant {timeout} minutes :\n\n{code}\n\nSi vous n’avez pas demandé de code de réinitialisation de mot de passe, veuillez ignorer cet email ou contacter votre administrateur si vous avez des questions.\nPar sécurité, merci de ne PAS faire suivre cet email.", + "login.email.password-reset.body": "Bonjour {user.nameOrEmail},\n\nVous avez récemment demandé un code de réinitialisation de mot de passe pour le Panel de {site}.\nLe code de réinitialisation de mot de passe suivant sera valable pendant {timeout} minutes :\n\n{code}\n\nSi vous n’avez pas demandé de code de réinitialisation de mot de passe, veuillez ignorer cet email ou contacter votre administrateur si vous avez des questions.\nPar sécurité, merci de ne PAS faire suivre ce courriel.", "login.email.password-reset.subject": "Votre code de réinitialisation du mot de passe", "login.remember": "Rester connecté", - "login.reset": "Réinitialiser le mot de passe", + "login.reset": "Réinitialiser", "login.toggleText.code.email": "Se connecter par courriel", "login.toggleText.code.email-password": "Se connecter avec un mot de passe", "login.toggleText.password-reset.email": "Mot de passe oublié ?", "login.toggleText.password-reset.email-password": "← Retour à la connexion", + "login.totp.enable.option": "Configurer les codes à usage unique", + "login.totp.enable.intro": "Les applications d’authentification peuvent générer des codes à usage unique qui sont utilisés comme second facteur lors de la connexion à votre compte.", + "login.totp.enable.qr.label": "1. Scannez ce QR code", + "login.totp.enable.qr.help": "Impossible de scanner ? Ajoutez la clé de configuration {secret} manuellement à votre application d’authentification..", + "login.totp.enable.confirm.headline": "2. Confirmez avec le code généré", + "login.totp.enable.confirm.text": "Votre application génère un nouveau code à usage unique toutes les 30 secondes. Saisissez le code actuel pour terminer la configuration :", + "login.totp.enable.confirm.label": "Code actuel", + "login.totp.enable.confirm.help": "Après cette configuration, nous vous demanderons un code à usage unique à chaque connexion.", + "login.totp.enable.success": "Codes à usage unique activés", + "login.totp.disable.option": "Désactiver les codes à usage unique", + "login.totp.disable.label": "Saisissez votre mot de passe pour désactiver les codes à usage unique.", + "login.totp.disable.help": "Un second facteur différent, par exemple un code de connexion envoyé par courriel, vous sera demandé à la connexion. Vous pourrez à nouveau configurer les codes à usage unique ultérieurement.", + "login.totp.disable.admin": "

Cela désactivera les codes à usage unique pour {user}.

Un second facteur différent, par exemple un code de connexion envoyé par courriel lui sera demandé à la connexion. {user} pourra à nouveau configurer les codes à usage unique ultérieurement.

", + "login.totp.disable.success": "Codes à usage unique désactivés", "logout": "Déconnexion", + "merge": "Fusionner", "menu": "Menu", "meridiem": "AM/PM", "mime": "Type de médias", @@ -411,21 +558,26 @@ "months.september": "Septembre", "more": "Plus", + "move": "Déplacer", "name": "Nom", "next": "Suivant", + "night": "Nuit", "no": "non", "off": "off", "on": "on", "open": "Ouvrir", "open.newWindow": "Ouvrir dans une nouvelle fenêtre", + "option": "Option", "options": "Options", "options.none": "Pas d’options", + "options.all": "Afficher toutes les options de {count}", "orientation": "Orientation", "orientation.landscape": "Paysage", "orientation.portrait": "Portrait", "orientation.square": "Carré", + "page": "Page", "page.blueprint": "Cette page n’a pas encore de blueprint. Vous pouvez en définir les paramètres dans /site/blueprints/pages/{blueprint}.yml", "page.changeSlug": "Modifier l’URL", "page.changeSlug.fromTitle": "Créer à partir du titre", @@ -433,13 +585,15 @@ "page.changeStatus.position": "Veuillez sélectionner une position", "page.changeStatus.select": "Sélectionner un nouveau statut", "page.changeTemplate": "Changer de modèle", + "page.changeTemplate.notice": "Modifier le modèle de la page supprimera le contenu des champs dont le type ne correspond pas. Utilisez avec précaution.", + "page.create": "Créer en tant que {status}", "page.delete.confirm": "Voulez-vous vraiment supprimer {title} ?", "page.delete.confirm.subpages": "Cette page contient des sous-pages.
Toutes les sous-pages seront également supprimées.", "page.delete.confirm.title": "Veuillez saisir le titre de la page pour confirmer", - "page.draft.create": "Créer un brouillon", "page.duplicate.appendix": "Copier", "page.duplicate.files": "Copier les fichiers", "page.duplicate.pages": "Copier les pages", + "page.move": "Déplacer la page", "page.sort": "Modifier la position", "page.status": "Statut", "page.status.draft": "Brouillon", @@ -450,6 +604,7 @@ "page.status.unlisted.description": "La page est accessible uniquement par son URL", "pages": "Pages", + "pages.delete.confirm.selected": "Voulez-vous vraiment supprimer la page sélectionnée ? Cette action ne peut être annulée.", "pages.empty": "Pas encore de pages", "pages.status.draft": "Brouillons", "pages.status.listed": "Publié", @@ -460,14 +615,21 @@ "password": "Mot de passe", "paste": "Coller", "paste.after": "Coller après", + "paste.success": "Copié : {count}", "pixel": "Pixel", "plugin": "Plugin", "plugins": "Plugins", "prev": "Précédent", "preview": "Prévisualiser", + + "publish": "Publier", + "published": "Publié", + "remove": "Supprimer", "rename": "Renommer", + "renew": "Renouveler", "replace": "Remplacer", + "replace.with": "Remplacer par", "retry": "Essayer à nouveau", "revert": "Revenir", "revert.confirm": "Voulez-vous vraiment supprimer toutes les modifications non enregistrées ?", @@ -482,11 +644,14 @@ "role.nobody.title": "Personne", "save": "Enregistrer", + "saved": "Enregistré", "search": "Rechercher", + "searching": "Recherche en cours", "search.min": "Saisissez {min} caractères pour rechercher", - "search.all": "Tout afficher", + "search.all": "Afficher tous les résultats de {count}", "search.results.none": "Pas de résultats", + "section.invalid": "La section est invalide", "section.required": "Cette section est obligatoire", "security": "Sécurité", @@ -498,20 +663,29 @@ "size": "Poids", "slug": "Identifiant de l’URL", "sort": "Trier", + "sort.drag": "Déplacer pour réordonner…", + "split": "Diviser", "stats.empty": "Aucun rapport", + "status": "Statut", + + "system.info.copy": "Copier les informations", + "system.info.copied": "Informations système copiées", "system.issues.content": "Le dossier content semble exposé", "system.issues.eol.kirby": "La version de Kirby installée a atteint la fin de son cycle de vie et ne recevra plus de mises à jour de sécurité", "system.issues.eol.plugin": "La version du plugin { plugin } installée a atteint la fin de son cycle de vie et ne recevra plus de mises à jour de sécurité", + "system.issues.eol.php": "Votre version de PHP installée { release } a atteint la fin de son cycle de vie et ne recevra plus de mises à jour de sécurité", "system.issues.debug": "Le débogage doit être désactivé en production", "system.issues.git": "Le dossier .git semble exposé", "system.issues.https": "Nous recommandons HTTPS pour tous vos sites", "system.issues.kirby": "Le dossier kirby semble exposé", + "system.issues.local": "Le site fonctionne localement avec des contrôles de sécurité allégés.", "system.issues.site": "Le dossier site semble exposé", + "system.issues.vue.compiler": "Le compileur de templates de Vue est activé", "system.issues.vulnerability.kirby": "Votre installation pourrait être affectée par la vulnérabilité suivante ({ severity } gravité) : { description }", "system.issues.vulnerability.plugin": "Votre installation pourrait être affectée par la vulnérabilité suivante du plugin { plugin } ({ severity } gravité) : { description }", "system.updateStatus": "Statut des mises à jour", - "system.updateStatus.error": "Les mises à jour n'ont pu être vérifiées", + "system.updateStatus.error": "Les mises à jour n’ont pu être vérifiées", "system.updateStatus.not-vulnerable": "Aucune vulnérabilité connue", "system.updateStatus.security-update": "Mise à jour gratuite { version } disponible", "system.updateStatus.security-upgrade": "Mise à jour { version } avec correctifs de sécurité disponible", @@ -520,10 +694,19 @@ "system.updateStatus.update": "Mise à jour gratuite { version } disponible", "system.updateStatus.upgrade": "Mise à jour { version } disponible", - "title": "Titre", + "tel": "Téléphone", + "tel.placeholder": "+33123456789", "template": "Modèle", + + "theme": "Thème", + "theme.light": "Allumer", + "theme.dark": "Éteindre", + "theme.automatic": "Suivre le réglage système", + + "title": "Titre", "today": "Aujourd’hui", + "toolbar.button.clear": "Supprimer la mise en forme", "toolbar.button.code": "Code", "toolbar.button.bold": "Gras", "toolbar.button.email": "Courriel", @@ -541,6 +724,8 @@ "toolbar.button.link": "Lien", "toolbar.button.paragraph": "Paragraphe", "toolbar.button.strike": "Barré", + "toolbar.button.sub": "Indice", + "toolbar.button.sup": "Exposant", "toolbar.button.ol": "Liste ordonnée", "toolbar.button.underline": "Souligné", "toolbar.button.ul": "Liste non-ordonnée", @@ -550,6 +735,8 @@ "translation.name": "Français", "translation.locale": "fr_FR", + "type": "Type", + "upload": "Transférer", "upload.error.cantMove": "Le fichier transféré n’a pu être déplacé", "upload.error.cantWrite": "Le fichier n’a pu être écrit sur le disque", @@ -569,11 +756,12 @@ "url.placeholder": "https://example.com", "user": "Utilisateur", - "user.blueprint": "Vous pouvez définir de nouvelles sections et champs de formulaires pour ce rôle d'utilisateur dans /site/blueprints/users/{blueprint}.yml", + "user.blueprint": "Vous pouvez définir de nouvelles sections et champs de formulaires pour ce rôle d’utilisateur dans /site/blueprints/users/{blueprint}.yml", "user.changeEmail": "Modifier le courriel", "user.changeLanguage": "Modifier la langue", "user.changeName": "Renommer cet utilisateur", "user.changePassword": "Modifier le mot de passe", + "user.changePassword.current": "Votre mot de passe actuel", "user.changePassword.new": "Nouveau mot de passe", "user.changePassword.new.confirm": "Confirmer le nouveau mot de passe…", "user.changeRole": "Modifier le rôle", @@ -585,10 +773,13 @@ "users": "Utilisateurs", "version": "Version", + "version.changes": "Version modifiée", + "version.compare": "Comparer les versions", "version.current": "Version actuelle", "version.latest": "Dernière version", "versionInformation": "Informations de version", + "view": "Visualiser", "view.account": "Votre compte", "view.installation": "Installation", "view.languages": "Langues", diff --git a/kirby/i18n/translations/hu.json b/kirby/i18n/translations/hu.json index 929ae64..56424ac 100644 --- a/kirby/i18n/translations/hu.json +++ b/kirby/i18n/translations/hu.json @@ -3,57 +3,75 @@ "account.delete": "Fiók törlése", "account.delete.confirm": "Tényleg törölni szeretnéd a fiókodat? Azonnal kijelentkeztetünk és ez a folyamat visszavonhatatlan.", - "add": "Hozzáad", + "activate": "Activate", + "add": "Hozz\u00e1ad", + "alpha": "Alpha", "author": "Szerző", "avatar": "Profilkép", "back": "Vissza", - "cancel": "Mégsem", - "change": "Módosítás", - "close": "Bezár", + "cancel": "M\u00e9gsem", + "change": "M\u00f3dos\u00edt\u00e1s", + "close": "Bez\u00e1r", + "changes": "Changes", "confirm": "Mentés", "collapse": "Bezárás", "collapse.all": "Összes bezárása", + "color": "Color", + "coordinates": "Coordinates", "copy": "Másol", "copy.all": "Összes másolása", + "copy.success": "Copied", + "copy.success.multiple": "{count} copied!", + "copy.url": "Copy URL", "create": "Létrehoz", + "custom": "Custom", "date": "Dátum", "date.select": "Dátum kiválasztása", "day": "Nap", - "days.fri": "pé", - "days.mon": "hé", + "days.fri": "p\u00e9", + "days.mon": "h\u00e9", "days.sat": "szo", "days.sun": "va", - "days.thu": "csü", + "days.thu": "cs\u00fc", "days.tue": "ke", "days.wed": "sze", "debugging": "Hibakeresés", - "delete": "Törlés", + "delete": "T\u00f6rl\u00e9s", "delete.all": "Összes törlése", + "dialog.fields.empty": "This dialog has no fields", "dialog.files.empty": "Nincsenek fájlok kiválasztva", "dialog.pages.empty": "Nincsenek oldalak kiválasztva", + "dialog.text.empty": "This dialog does not define any text", "dialog.users.empty": "Nincsenek felhasználók kiválasztva", "dimensions": "Méretek", + "disable": "Disable", "disabled": "Inaktív", - "discard": "Visszavonás", + "discard": "Visszavon\u00e1s", + + "drawer.fields.empty": "This drawer has no fields", + + "domain": "Domain", "download": "Letöltés", "duplicate": "Másolat", - "edit": "Aloldal szerkesztése", + "edit": "Aloldal szerkeszt\u00e9se", "email": "Email", "email.placeholder": "mail@pelda.hu", + "enter": "Enter", "entries": "Entries", "entry": "Entry", "environment": "Környezet", + "error": "Error", "error.access.code": "Érvénytelen kód", "error.access.login": "Érvénytelen bejelentkezés", "error.access.panel": "Nincs jogosultságod megnyitni a panelt", @@ -74,17 +92,35 @@ "error.cache.type.invalid": "Invalid cache type \"{type}\"", + "error.content.lock.delete": "The version is locked and cannot be deleted", + "error.content.lock.move": "The source version is locked and cannot be moved", + "error.content.lock.publish": "This version is already published", + "error.content.lock.replace": "The version is locked and cannot be replaced", + "error.content.lock.update": "The version is locked and cannot be updated", + + "error.entries.max.plural": "You must not add more than {max} entries", + "error.entries.max.singular": "You must not add more than one entry", + "error.entries.min.plural": "You must add at least {min} entries", + "error.entries.min.singular": "You must add at least one entry", + "error.entries.supports": "\"{type}\" field type is not supported for the entries field", + "error.entries.validation": "There's an error on the \"{field}\" field in row {index}", + "error.email.preset.notFound": "A \"{name}\" email-beállítás nem található", "error.field.converter.invalid": "Érvénytelen konverter: \"{converter}\"", + "error.field.link.options": "Invalid options: {options}", "error.field.type.missing": "Field \"{ name }\": The field type \"{ type }\" does not exist", "error.file.changeName.empty": "A név nem lehet üres", "error.file.changeName.permission": "Nincs jogosultságod megváltoztatni a \"{filename}\" fájl nevét", + "error.file.changeTemplate.invalid": "The template for the file \"{id}\" cannot be changed to \"{template}\" (valid: \"{blueprints}\")", + "error.file.changeTemplate.permission": "You are not allowed to change the template for the file \"{id}\"", + + "error.file.delete.multiple": "Not all files could be deleted. Try each remaining file individually to see the specific error that prevents deletion.", "error.file.duplicate": "Már létezik \"{filename}\" nevű fájl", - "error.file.extension.forbidden": "Tiltott kiterjesztésű fájl", + "error.file.extension.forbidden": "Tiltott kiterjeszt\u00e9s\u0171 f\u00e1jl", "error.file.extension.invalid": "Érvénytelen kiterjesztés: {extension}", - "error.file.extension.missing": "Kiterjesztés nélküli fájl nem tölthető fel", + "error.file.extension.missing": "Kiterjeszt\u00e9s n\u00e9lk\u00fcli f\u00e1jl nem t\u00f6lthet\u0151 fel", "error.file.maxheight": "A kép nem lehet magasabb {height} pixelnél", "error.file.maxsize": "A fájl túl nagy", "error.file.maxwidth": "A kép nem lehet szélesebb {width} pixelnél", @@ -95,33 +131,43 @@ "error.file.minheight": "A képnek legalább {height} pixel magasnak kell lennie", "error.file.minsize": "A fájl túl kicsi", "error.file.minwidth": "A képnek legalább {width} pixel szélesnek kell lennie", + "error.file.name.unique": "The filename must be unique", "error.file.name.missing": "A fálj neve nem lehet üres", "error.file.notFound": "A \"{filename}\" fájl nem található", "error.file.orientation": "A képnek \"{orientation}\" tájolásúnak kell lennie", + "error.file.sort.permission": "You are not allowed to change the sorting of \"{filename}\"", "error.file.type.forbidden": "Nem tölthetsz fel \"{type}\" típusú fájlokat", "error.file.type.invalid": "Érvénytelen fájltípus: {type}", - "error.file.undefined": "A fájl nem található", + "error.file.undefined": "A f\u00e1jl nem tal\u00e1lhat\u00f3", "error.form.incomplete": "Kérlek javítsd ki az összes hibát az űrlapon", "error.form.notSaved": "Az űrlap nem menthető", "error.language.code": "Kérlek, add meg a nyelv érvényes kódját", + "error.language.create.permission": "You are not allowed to create a language", + "error.language.delete.permission": "You are not allowed to delete the language", "error.language.duplicate": "A nyelv már létezik", "error.language.name": "Kérlek, add meg a nyelv érvényes nevét", "error.language.notFound": "A nyelv nem található", + "error.language.update.permission": "You are not allowed to update the language", "error.layout.validation.block": "There's an error on the \"{field}\" field in block {blockIndex} using the \"{fieldset}\" block type in layout {layoutIndex}", "error.layout.validation.settings": "Hibát találtunk a(z) {index} elrendezés beállításaiban", - "error.license.format": "Kérlek, add meg az évényes lincensz kulcsot", + "error.license.domain": "The domain for the license is missing", "error.license.email": "Kérlek adj meg egy valós email-címet", + "error.license.format": "Please enter a valid license code", "error.license.verification": "A licensz nem ellenőrizhető", + "error.login.totp.confirm.invalid": "Érvénytelen kód", + "error.login.totp.confirm.missing": "Please enter the current code", + "error.object.validation": "There’s an error in the \"{label}\" field:\n{message}", "error.offline": "A Panel jelenleg nem elérhető", "error.page.changeSlug.permission": "Nem változtathatod meg az URL-előtagot: \"{slug}\"", + "error.page.changeSlug.reserved": "The path of top-level pages must not start with \"{path}\"", "error.page.changeStatus.incomplete": "Az oldal hibákat tartalmaz és nem publikálható", "error.page.changeStatus.permission": "Az oldal státusza nem változtatható meg", "error.page.changeStatus.toDraft.invalid": "A(z) \"{slug}\" oldalt nem lehet piszkozattá alakítani", @@ -133,17 +179,25 @@ "error.page.delete": "A(z) \"{slug}\" oldal nem törölhető", "error.page.delete.confirm": "Megerősítéshez add meg az oldal címét", "error.page.delete.hasChildren": "Az oldalnak vannak aloldalai és nem törölhető", + "error.page.delete.multiple": "Not all pages could be deleted. Try each remaining page individually to see the specific error that prevents deletion.", "error.page.delete.permission": "Nincs jogosultságod a(z) \"{slug}\" oldal törléséhez", "error.page.draft.duplicate": "Van már egy másik oldal ezzel az URL-lel: \"{slug}\"", "error.page.duplicate": "Van már egy másik oldal ezzel az URL-lel: \"{slug}\"", "error.page.duplicate.permission": "Nincs engedélyed a(z) \"{slug}\" másolat keszítéséhez", - "error.page.notFound": "Az oldal nem található", + "error.page.move.ancestor": "The page cannot be moved into itself", + "error.page.move.directory": "The page directory cannot be moved", + "error.page.move.duplicate": "A sub page with the URL appendix \"{slug}\" already exists", + "error.page.move.noSections": "The page \"{parent}\" cannot be a parent of any page because it lacks any pages sections in its blueprint", + "error.page.move.notFound": "The moved page could not be found", + "error.page.move.permission": "You are not allowed to move \"{slug}\"", + "error.page.move.template": "The \"{template}\" template is not accepted as a subpage of \"{parent}\"", + "error.page.notFound": "Az oldal nem tal\u00e1lhat\u00f3", "error.page.num.invalid": "Kérlek megfelelő oldalszámozást adj meg. Negatív szám itt nem használható.", "error.page.slug.invalid": "Kérlek érvényes URL-kiterjesztést adj meg", "error.page.slug.maxlength": "Az URL maximum \"{length}\" karakter hosszúságú lehet", "error.page.sort.permission": "A(z) \"{slug}\" oldal nem illeszthető a sorrendbe", "error.page.status.invalid": "Kérlek add meg a megfelelő oldalstátuszt", - "error.page.undefined": "Az oldal nem található", + "error.page.undefined": "Az oldal nem tal\u00e1lhat\u00f3", "error.page.update.permission": "Nincs jogosultságod a(z) \"{slug}\" oldal frissítéséhez", "error.section.files.max.plural": "Maximum {max} fájlt adhatsz hozzá a(z) \"{section}\" szekcióhoz", @@ -163,6 +217,8 @@ "error.site.changeTitle.permission": "Nincs jogosultságod megváltoztatni az honlap címét", "error.site.update.permission": "Nincs jogosultságod frissíteni a honlapot", + "error.structure.validation": "There's an error on the \"{field}\" field in row {index}", + "error.template.default.notFound": "Az alapértelmezett sablon nem létezik", "error.unexpected": "Váratlan hiba történt! További információért engedélyezd a hibakeresés módot: https://getkirby.com/docs/reference/system/options/debug", @@ -175,17 +231,17 @@ "error.user.changeRole.permission": "Nincs jogosultságod megváltoztatni \"{name}\" felhasználó szerepkörét", "error.user.changeRole.toAdmin": "Nincs jogosultságod előléptetni a felhasználót adminisztrátorrá", "error.user.create.permission": "Nincs jogosultságod létrehozni ezt a felhasználót", - "error.user.delete": "A felhasználó nem törölhető", - "error.user.delete.lastAdmin": "Nem törölheted az egyetlen adminisztrátort", + "error.user.delete": "A felhaszn\u00e1l\u00f3 nem t\u00f6r\u00f6lhet\u0151", + "error.user.delete.lastAdmin": "Nem t\u00f6r\u00f6lheted az egyetlen adminisztr\u00e1tort", "error.user.delete.lastUser": "Nem törölheted az egyetlen felhasználót", - "error.user.delete.permission": "Nincs jogosultságod törölni ezt a felhasználót", + "error.user.delete.permission": "Nincs jogosults\u00e1god t\u00f6r\u00f6lni ezt a felhaszn\u00e1l\u00f3t", "error.user.duplicate": "Már létezik felhasználó \"{email}\" email-címmel", "error.user.email.invalid": "Kérlek adj meg egy valós email-címet", "error.user.language.invalid": "Kérlek add meg a megfelelő nyelvi beállítást", - "error.user.notFound": "A felhasználó nem található", + "error.user.notFound": "A felhaszn\u00e1l\u00f3 nem tal\u00e1lhat\u00f3", "error.user.password.excessive": "Please enter a valid password. Passwords must not be longer than 1000 characters.", "error.user.password.invalid": "Kérlek adj meg egy megfelelő jelszót. A jelszónak legalább 8 karakter hosszúságúnak kell lennie.", - "error.user.password.notSame": "Kérlek erősítsd meg a jelszót", + "error.user.password.notSame": "K\u00e9rlek er\u0151s\u00edtsd meg a jelsz\u00f3t", "error.user.password.undefined": "A felhasználónak nincs jelszó megadva", "error.user.password.wrong": "Hibás jelszó", "error.user.role.invalid": "Kérlek adj meg egy megfelelő szerepkört", @@ -195,8 +251,10 @@ "error.validation.accepted": "Kérlek erősítsd meg", "error.validation.alpha": "Kérlek csak kis betűket használj (a-z)", "error.validation.alphanum": "Kérlek csak kis betűket és számjegyeket használj (a-z, 0-9)", + "error.validation.anchor": "Please enter a correct link anchor", "error.validation.between": "Kérlek egy \"{min}\" és \"{max}\" közötti értéket adj meg", "error.validation.boolean": "Kérlek erősítsd meg vagy vesd el", + "error.validation.color": "Please enter a valid color in the {format} format", "error.validation.contains": "Kérlek olyan értéket adj meg, amely tartalmazza ezt: \"{needle}\"", "error.validation.date": "Kérlek megfelelő dátumot adj meg", "error.validation.date.after": "Kérlek olyan dátumot adj meg, amely későbbi ennél: {date}", @@ -211,6 +269,7 @@ "error.validation.integer": "Kérlek valós számot adj meg", "error.validation.ip": "Kérlek megfelelő IP-címet adj meg", "error.validation.less": "A megadott érték kevesebb legyen, mint {max}", + "error.validation.linkType": "The link type is not allowed", "error.validation.match": "A megadott érték nem felel meg az elvárt struktúrának", "error.validation.max": "A megadott érték egyenlő vagy kevesebb legyen, mint {max}", "error.validation.maxlength": "Kérlek rövidebb értéket adj meg (legfeljebb {max} karakter)", @@ -227,15 +286,18 @@ "error.validation.same": "Kérlek írd be: \"{other}\"", "error.validation.size": "Az értéknek az alábbi méretűnek kell lennie: \"{size}\"", "error.validation.startswith": "Az értéknek ezzel kell kezdődnie: \"{start}\"", + "error.validation.tel": "Please enter an unformatted phone number", "error.validation.time": "Kérlek megfelelő időt adj meg", "error.validation.time.after": "Kérlek olyan időpontot adj meg, amely későbbi ennél: {time}", "error.validation.time.before": "Kérlek olyan időpontot adj meg, amely korábbi ennél: {time}", "error.validation.time.between": "Kérlek {min} és {max} közötti időpontot adj meg", + "error.validation.uuid": "Please enter a valid UUID", "error.validation.url": "Kérlek megfelelő URL-t adj meg", "expand": "Kinyitás", "expand.all": "Összes kinyitása", + "field.invalid": "The field is invalid", "field.required": "Kötelező mező", "field.blocks.changeType": "Típus megváltoztatása", "field.blocks.code.name": "Kód", @@ -245,8 +307,9 @@ "field.blocks.delete.confirm.all": "Tényleg minden blokkot törölni szeretnél?", "field.blocks.delete.confirm.selected": "Tényleg törölni szeretnéd a kijelölt blokkokat?", "field.blocks.empty": "Még nincsenek blokkok", + "field.blocks.fieldsets.empty": "No fieldsets yet", "field.blocks.fieldsets.label": "Kérlek válassz blokktípust …", - "field.blocks.fieldsets.paste": "Blokk beszúrásához a vágólapról használd a {{ shortcut }} billentyűkombinációt", + "field.blocks.fieldsets.paste": "Press {{ shortcut }} to import layouts/blocks from your clipboard Only those allowed in the current field will get inserted.", "field.blocks.gallery.name": "Galéria", "field.blocks.gallery.images.empty": "Még nincsenek képek", "field.blocks.gallery.images.label": "Képek", @@ -254,11 +317,16 @@ "field.blocks.heading.name": "Címsor", "field.blocks.heading.text": "Szöveg", "field.blocks.heading.placeholder": "Címsor …", + "field.blocks.figure.back.plain": "Plain", + "field.blocks.figure.back.pattern.light": "Pattern (light)", + "field.blocks.figure.back.pattern.dark": "Pattern (dark)", "field.blocks.image.alt": "Alternatív szöveg", "field.blocks.image.caption": "Képaláírás", "field.blocks.image.crop": "Körülvágás", "field.blocks.image.link": "Link", "field.blocks.image.location": "A kép helye", + "field.blocks.image.location.internal": "This website", + "field.blocks.image.location.external": "External source", "field.blocks.image.name": "Kép", "field.blocks.image.placeholder": "Kép kiválasztása", "field.blocks.image.ratio": "Képarány", @@ -275,38 +343,72 @@ "field.blocks.quote.citation.placeholder": "Szerző …", "field.blocks.text.name": "Szöveg", "field.blocks.text.placeholder": "Szöveg …", + "field.blocks.video.autoplay": "Autoplay", "field.blocks.video.caption": "Képaláírás", + "field.blocks.video.controls": "Controls", + "field.blocks.video.location": "A kép helye", + "field.blocks.video.loop": "Loop", + "field.blocks.video.muted": "Muted", "field.blocks.video.name": "Videó", "field.blocks.video.placeholder": "Videó URL-jének megadása", + "field.blocks.video.poster": "Poster", + "field.blocks.video.preload": "Preload", "field.blocks.video.url.label": "Videó URL", "field.blocks.video.url.placeholder": "https://youtube.com/?v=", - "field.files.empty": "Nincs fálj kiválasztva", + "field.entries.delete.confirm.all": "Do you really want to delete all entries?", + "field.entries.empty": "Nincs még bejegyzés", + "field.files.empty": "Nincs fálj kiválasztva", + "field.files.empty.single": "No file selected yet", + + "field.layout.change": "Change layout", "field.layout.delete": "Elrendezés törlése", "field.layout.delete.confirm": "Tényleg törölni szeretnéd ezt az elrendezést?", + "field.layout.delete.confirm.all": "Do you really want to delete all layouts?", "field.layout.empty": "Még nincsenek sorok", "field.layout.select": "Válassz elrendezést", "field.object.empty": "No information yet", "field.pages.empty": "Nincs oldal kiválasztva", + "field.pages.empty.single": "No page selected yet", - "field.structure.delete.confirm": "Biztos törölni szeretnéd ezt a bejegyzést?", + "field.structure.delete.confirm": "Biztos t\u00f6r\u00f6lni szeretn\u00e9d ezt a bejegyz\u00e9st?", "field.structure.delete.confirm.all": "Do you really want to delete all entries?", - "field.structure.empty": "Nincs még bejegyzés", + "field.structure.empty": "Nincs m\u00e9g bejegyz\u00e9s", "field.users.empty": "Nincs felhasználó kiválasztva", + "field.users.empty.single": "No user selected yet", + "fields.empty": "No fields yet", + + "file": "Fájl", "file.blueprint": "Ehhez a fájlhoz még nem tartozik oldalsablon. Itt hozhatod létre: /site/blueprints/files/{blueprint}.yml", + "file.changeTemplate": "Sablon módosítása", + "file.changeTemplate.notice": "Changing the file's template will remove content for fields that don't match in type. If the new template defines certain rules, e.g. image dimensions, those will also be applied irreversibly. Use with caution.", "file.delete.confirm": "Biztos törölni akarod ezt a fájlt:
{filename}?", + "file.focus.placeholder": "Set focal point", + "file.focus.reset": "Remove focal point", + "file.focus.title": "Focus", "file.sort": "Sorrend megváltoztatása", "files": "Fájlok", + "files.delete.confirm.selected": "Do you really want to delete the selected files? This action cannot be undone.", "files.empty": "Még nincsenek fájlok", + "filter": "Filter", + + "form.discard": "Discard changes", + "form.discard.confirm": "Do you really want to discard all your changes?", + "form.locked": "This content is disabled for you as it is currently edited by another user", + "form.unsaved": "The current changes have not yet been saved", + "form.preview": "Preview changes", + "form.preview.draft": "Preview draft", + "hide": "Elrejtés", "hour": "Óra", + "hue": "Hue", "import": "Importálás", "info": "Info", "insert": "Beilleszt", @@ -324,7 +426,6 @@ "installation.issues.mbstring": "Az MB String bővítmény engedélyezése szükséges", "installation.issues.media": "A /media mappa nem létezik vagy nem írható", "installation.issues.php": "Bizonyosodj meg róla, hogy az általad használt PHP-verzió PHP 8+", - "installation.issues.server": "A Kirby az alábbi szervereken futtatható: Apache, Nginx vagy Caddy", "installation.issues.sessions": "A /site/sessions könyvtár nem létezik vagy nem írható", "language": "Nyelv", @@ -332,6 +433,7 @@ "language.convert": "Alapértelmezettnek jelölés", "language.convert.confirm": "

Tényleg az alaőértelmezett nyelvre szeretnéd konvertálni ezt: {name}? Ez a művelet nem vonható vissza.

Ha{name} olyat is tartalmaz, amelynek nincs megfelelő fordítása, a honlapod egyes részei az új alapértelmezett nyelv hiányosságai miatt üresek maradhatnak.

", "language.create": "Új nyelv hozzáadása", + "language.default": "Alapértelmezett nyelv", "language.delete.confirm": "Tényleg törölni szeretnéd a(z) {name} nyelvet, annak minden fordításával együtt? Ez a művelet nem vonható vissza!", "language.deleted": "A nyelv törölve lett", "language.direction": "Olvasási irány", @@ -340,7 +442,16 @@ "language.locale": "PHP locale sztring", "language.locale.warning": "Egyedi nyelvi készletet használsz. Kérlek módosítsd a nyelvhez tartozó fájlt az alábbi mappában: /site/languages", "language.name": "Név", + "language.secondary": "Secondary language", + "language.settings": "Language settings", "language.updated": "A nyelv frissítve lett", + "language.variables": "Language variables", + "language.variables.empty": "No translations yet", + + "language.variable.delete.confirm": "Do you really want to delete the variable for {key}?", + "language.variable.key": "Key", + "language.variable.notFound": "The variable could not be found", + "language.variable.value": "Value", "languages": "Nyelvek", "languages.default": "Alapértelmezett nyelv", @@ -349,15 +460,32 @@ "languages.secondary.empty": "Nincsnek még másodlagos nyelvek", "license": "Kirby licenc", + "license.activate": "Activate it now", + "license.activate.label": "Please activate your license", + "license.activate.domain": "Your license will be activated for {host}.", + "license.activate.local": "You are about to activate your Kirby license for your local domain {host}. If this site will be deployed to a public domain, please activate it there instead. If {host} is the domain you want to use your license for, please continue.", + "license.activated": "Activated", "license.buy": "Licenc vásárlása", - "license.register": "Regisztráció", + "license.code": "Kód", + "license.code.help": "You received your license code after the purchase via email. Please copy and paste it here.", + "license.code.label": "Kérlek írd be a licenc-kódot", + "license.status.active.info": "Includes new major versions until {date}", + "license.status.active.label": "Valid license", + "license.status.demo.info": "This is a demo installation", + "license.status.demo.label": "Demo", + "license.status.inactive.info": "Renew license to update to new major versions", + "license.status.inactive.label": "No new major versions", + "license.status.legacy.bubble": "Ready to renew your license?", + "license.status.legacy.info": "Your license does not cover this version", + "license.status.legacy.label": "Please renew your license", + "license.status.missing.bubble": "Ready to launch your site?", + "license.status.missing.info": "No valid license", + "license.status.missing.label": "Please activate your license", + "license.status.unknown.info": "The license status is unknown", + "license.status.unknown.label": "Unknown", "license.manage": "Manage your licenses", - "license.register.help": "A vásárlás után emailben küldjük el a licenc-kódot. Regisztrációhoz másold ide a kapott kódot.", - "license.register.label": "Kérlek írd be a licenc-kódot", - "license.register.domain": "Your license will be registered to {host}.", - "license.register.local": "You are about to register your license for your local domain {host}. If this site will be deployed to a public domain, please register it there instead. If {host} is the domain you want to license Kirby to, please continue.", - "license.register.success": "Köszönjük, hogy támogatod a Kirby-t", - "license.unregistered": "Jelenleg a Kirby nem regisztrált próbaverzióját használod", + "license.purchased": "Purchased", + "license.success": "Köszönjük, hogy támogatod a Kirby-t", "license.unregistered.label": "Unregistered", "link": "Link", @@ -366,18 +494,22 @@ "loading": "Betöltés", "lock.unsaved": "Nem mentett változások", - "lock.unsaved.empty": "Nincsenek nem mentett változások", - "lock.isLocked": "Nem mentett {email} változások", - "lock.file.isLocked": "A fájlt jelenleg {email} szerkeszti és nem módosítható.", - "lock.page.isLocked": "Az oldalt jelenleg {email} szerkeszti és nem módosítható.", + "lock.unsaved.empty": "There are no unsaved changes", + "lock.unsaved.files": "Unsaved files", + "lock.unsaved.pages": "Unsaved pages", + "lock.unsaved.users": "Unsaved accounts", + "lock.isLocked": "Unsaved changes by {email}", "lock.unlock": "Kinyit", - "lock.isUnlocked": "A nem mentett módosításokat egy másik felhasználó felülírta. A módosításokat manuálisan egyesítheted.", + "lock.unlock.submit": "Unlock and overwrite unsaved changes by {email}", + "lock.isUnlocked": "Was unlocked by another user", "login": "Bejelentkezés", "login.code.label.login": "Bejelentkezéshez szükséges kód", "login.code.label.password-reset": "Jelszóvisszaállításhoz szükséges kód", "login.code.placeholder.email": "000 000", + "login.code.placeholder.totp": "000000", "login.code.text.email": "Amennyiben az email-címed létezik a rendszerben, a kódot oda küldjük el.", + "login.code.text.totp": "Please enter the one‑time code from your authenticator app.", "login.email.login.body": "Helló {user.nameOrEmail},\n\nNemrégiben bejelentkezési kódot igényeltél a(z) {site} Paneljéhez.\nAz alábbi kód {timeout} percig lesz érvényes:\n\n{code}\n\nHa nem te igényelted a kódot, kérlek hagyd figyelmen kívül ezt az emailt, kérdések esetén pedig vedd fel a kapcsolatot az oldal Adminisztrátorával.\nBiztonsági okokból kérjük NE továbbítsd ezt az emailt.", "login.email.login.subject": "Bejelentkezési kódod", "login.email.password-reset.body": "Helló {user.nameOrEmail},\n\nNemrégiben jelszóvisszaállítási kódot igényeltél a(z) {site} Paneljéhez.\nAz alábbi jelszóvisszaállítási kód {timeout} percig lesz érvényes:\n\n{code}\n\nHa nem te igényelted a jelszóvisszaállítási kódot, kérlek hagyd figyelmen kívül ezt az emailt, kérdések esetén pedig vedd fel a kapcsolatot az oldal Adminisztrátorával.\nBiztonsági okokból kérjük NE továbbítsd ezt az emailt.", @@ -388,58 +520,80 @@ "login.toggleText.code.email-password": "Bejelentkezés jelszóval", "login.toggleText.password-reset.email": "Elfelejtetted a jelszavad?", "login.toggleText.password-reset.email-password": "← Vissza a bejelentkezéshez", + "login.totp.enable.option": "Set up one‑time codes", + "login.totp.enable.intro": "Authenticator apps can generate one‑time codes that are used as a second factor when signing into your account.", + "login.totp.enable.qr.label": "1. Scan this QR code", + "login.totp.enable.qr.help": "Unable to scan? Add the setup key {secret} manually to your authenticator app.", + "login.totp.enable.confirm.headline": "2. Confirm with generated code", + "login.totp.enable.confirm.text": "Your app generates a new one‑time code every 30 seconds. Enter the current code to complete the setup:", + "login.totp.enable.confirm.label": "Current code", + "login.totp.enable.confirm.help": "After this setup, we will ask you for a one‑time code every time you log in.", + "login.totp.enable.success": "One‑time codes enabled", + "login.totp.disable.option": "Disable one‑time codes", + "login.totp.disable.label": "Enter your password to disable one‑time codes", + "login.totp.disable.help": "In the future, a different second factor like a login code sent via email will be requested when you log in. You can always set up one‑time codes again later.", + "login.totp.disable.admin": "

This will disable one‑time codes for {user}.

In the future, a different second factor like a login code sent via email will be requested when they log in. {user} can set up one‑time codes again after their next login.

", + "login.totp.disable.success": "One‑time codes disabled", "logout": "Kijelentkezés", + "merge": "Merge", "menu": "Menü", "meridiem": "DE/DU", "mime": "Média-típus", "minutes": "Perc", "month": "Hónap", - "months.april": "április", + "months.april": "\u00e1prilis", "months.august": "augusztus", "months.december": "december", "months.february": "február", - "months.january": "január", - "months.july": "július", - "months.june": "június", - "months.march": "március", - "months.may": "május", + "months.january": "janu\u00e1r", + "months.july": "j\u00falius", + "months.june": "j\u00fanius", + "months.march": "m\u00e1rcius", + "months.may": "m\u00e1jus", "months.november": "november", - "months.october": "október", + "months.october": "okt\u00f3ber", "months.september": "szeptember", "more": "Több", + "move": "Move", "name": "Név", "next": "Következő", + "night": "Night", "no": "nem", "off": "ki", "on": "be", "open": "Megnyitás", "open.newWindow": "Megnyitás új ablakban", + "option": "Option", "options": "Beállítások", "options.none": "Nincsnek beállítások", + "options.all": "Show all {count} options", "orientation": "Tájolás", "orientation.landscape": "Fekvő", "orientation.portrait": "Álló", "orientation.square": "Négyzetes", + "page": "Oldal", "page.blueprint": "Ehhez az oldalhoz még nem tartozik oldalsablon. Itt hozhatod létre: /site/blueprints/pages/{blueprint}.yml", - "page.changeSlug": "URL változtatása", - "page.changeSlug.fromTitle": "Létrehozás címből", + "page.changeSlug": "URL v\u00e1ltoztat\u00e1sa", + "page.changeSlug.fromTitle": "L\u00e9trehoz\u00e1s c\u00edmb\u0151l", "page.changeStatus": "Állapot módosítása", "page.changeStatus.position": "Kérlek válaszd ki a pozíciót", "page.changeStatus.select": "Új állapot kiválasztása", "page.changeTemplate": "Sablon módosítása", + "page.changeTemplate.notice": "Changing the page's template will remove content for fields that don't match in type. Use with caution.", + "page.create": "Create as {status}", "page.delete.confirm": "Biztos vagy benne, hogy törlöd az alábbi oldalt: {title}?", "page.delete.confirm.subpages": "Ehhez az oldalhoz aloldalak tartoznak.
Az oldal törlésekor a hozzá tartozó aloldalak is törlődnek.", "page.delete.confirm.title": "Megerősítéshez add meg az oldal címét", - "page.draft.create": "Piszkozat létrehozása", "page.duplicate.appendix": "Másol", "page.duplicate.files": "Fájlok másolása", "page.duplicate.pages": "Oldalak másolása", + "page.move": "Move page", "page.sort": "Sorrend megváltoztatása", "page.status": "Állapot", "page.status.draft": "Piszkozat", @@ -450,6 +604,7 @@ "page.status.unlisted.description": "Az oldal csak URL-en keresztül érhető el", "pages": "Oldalak", + "pages.delete.confirm.selected": "Do you really want to delete the selected pages? This action cannot be undone.", "pages.empty": "Nincs még bejegyzés", "pages.status.draft": "Piszkozatok", "pages.status.listed": "Publikálva", @@ -457,19 +612,26 @@ "pagination.page": "Oldal", - "password": "Jelszó", + "password": "Jelsz\u00f3", "paste": "Beillesztés", "paste.after": "Beillesztés utána", + "paste.success": "{count} pasted!", "pixel": "Pixel", "plugin": "Plugin", "plugins": "Pluginek", "prev": "Előző", "preview": "Előnézet", + + "publish": "Publish", + "published": "Publikálva", + "remove": "Eltávolítás", "rename": "Átnevezés", - "replace": "Cserél", + "renew": "Renew", + "replace": "Cser\u00e9l", + "replace.with": "Replace with", "retry": "Próbáld újra", - "revert": "Visszavonás", + "revert": "Visszavon\u00e1s", "revert.confirm": "Tényleg törölni szeretnél minden nem mentett változtatást?", "role": "Szerepkör", @@ -481,12 +643,15 @@ "role.nobody.description": "Ez a visszatérő szabály a nem rendelkező jogosultsághoz", "role.nobody.title": "Senki", - "save": "Mentés", + "save": "Ment\u00e9s", + "saved": "Saved", "search": "Keresés", + "searching": "Searching", "search.min": "A kereséshez írj be minimum {min} karaktert", - "search.all": "Összes mutatása", + "search.all": "Show all {count} results", "search.results.none": "Nincs találat", + "section.invalid": "The section is invalid", "section.required": "Ez a szakasz kötelező", "security": "Security", @@ -496,18 +661,27 @@ "show": "Mutat", "site.blueprint": "Ehhez a weblaphoz még nem tartozik oldalsablon. Itt hozhatod létre: /site/blueprints/site.yml", "size": "Méret", - "slug": "URL név", + "slug": "URL n\u00e9v", "sort": "Rendezés", + "sort.drag": "Drag to sort …", + "split": "Split", "stats.empty": "No reports", + "status": "Állapot", + + "system.info.copy": "Copy info", + "system.info.copied": "System info copied", "system.issues.content": "The content folder seems to be exposed", "system.issues.eol.kirby": "Your installed Kirby version has reached end-of-life and will not receive further security updates", "system.issues.eol.plugin": "Your installed version of the { plugin } plugin is has reached end-of-life and will not receive further security updates", + "system.issues.eol.php": "Your installed PHP release { release } has reached end-of-life and will not receive further security updates", "system.issues.debug": "Debugging must be turned off in production", "system.issues.git": "The .git folder seems to be exposed", "system.issues.https": "We recommend HTTPS for all your sites", "system.issues.kirby": "The kirby folder seems to be exposed", + "system.issues.local": "The site is running locally with relaxed security checks", "system.issues.site": "The site folder seems to be exposed", + "system.issues.vue.compiler": "The Vue template compiler is enabled", "system.issues.vulnerability.kirby": "Your installation might be affected by the following vulnerability ({ severity } severity): { description }", "system.issues.vulnerability.plugin": "Your installation might be affected by the following vulnerability in the { plugin } plugin ({ severity } severity): { description }", "system.updateStatus": "Update status", @@ -520,12 +694,21 @@ "system.updateStatus.update": "Free update { version } available", "system.updateStatus.upgrade": "Upgrade { version } available", - "title": "Cím", + "tel": "Phone", + "tel.placeholder": "+49123456789", "template": "Sablon", + + "theme": "Theme", + "theme.light": "Lights on", + "theme.dark": "Lights off", + "theme.automatic": "Match system default", + + "title": "Cím", "today": "Ma", + "toolbar.button.clear": "Clear formatting", "toolbar.button.code": "Kód", - "toolbar.button.bold": "Félkövér szöveg", + "toolbar.button.bold": "F\u00e9lk\u00f6v\u00e9r sz\u00f6veg", "toolbar.button.email": "Email", "toolbar.button.headings": "Címsor", "toolbar.button.heading.1": "Címsor 1", @@ -541,6 +724,8 @@ "toolbar.button.link": "Link", "toolbar.button.paragraph": "Bekezdés", "toolbar.button.strike": "Áthúzott szöveg", + "toolbar.button.sub": "Subscript", + "toolbar.button.sup": "Superscript", "toolbar.button.ol": "Rendezett lista", "toolbar.button.underline": "Aláhúzott szöveg", "toolbar.button.ul": "Rendezetlen lista", @@ -550,6 +735,8 @@ "translation.name": "Magyar", "translation.locale": "hu_HU", + "type": "Type", + "upload": "Feltöltés", "upload.error.cantMove": "A feltöltött fájlt nem sikerült áthelyezni", "upload.error.cantWrite": "Hiba a fájl lemezre írása közben", @@ -562,7 +749,7 @@ "upload.error.noFiles": "Nem lettek fájlok feltöltve", "upload.error.partial": "A fájl feltöltése csak részben sikerült", "upload.error.tmpDir": "Hiányzik egy átmeneti mappa", - "upload.errors": "Error", + "upload.errors": "Hiba", "upload.progress": "Feltöltés...", "url": "Url", @@ -574,6 +761,7 @@ "user.changeLanguage": "Nyelv módosítása", "user.changeName": "Felhasználó átnevezése", "user.changePassword": "Jelszó módosítása", + "user.changePassword.current": "Your current password", "user.changePassword.new": "Új jelszó", "user.changePassword.new.confirm": "Az új jelszó megerősítése", "user.changeRole": "Szerepkör módosítása", @@ -584,18 +772,21 @@ "users": "Felhasználók", - "version": "Kirby verzió", + "version": "Kirby verzi\u00f3", + "version.changes": "Changed version", + "version.compare": "Compare versions", "version.current": "Current version", "version.latest": "Latest version", "versionInformation": "Version information", - "view.account": "Fiókod", - "view.installation": "Telepítés", + "view": "View", + "view.account": "Fi\u00f3kod", + "view.installation": "Telep\u00edt\u00e9s", "view.languages": "Nyelvek", "view.resetPassword": "Jelszó visszaállítása", "view.site": "Weboldal", "view.system": "Rendszer", - "view.users": "Felhasználók", + "view.users": "Felhaszn\u00e1l\u00f3k", "welcome": "Üdvözlünk", "year": "Év", diff --git a/kirby/i18n/translations/id.json b/kirby/i18n/translations/id.json index f226111..b6f0e0f 100644 --- a/kirby/i18n/translations/id.json +++ b/kirby/i18n/translations/id.json @@ -3,19 +3,28 @@ "account.delete": "Hapus akun Anda", "account.delete.confirm": "Anda yakin menghapus akun? Anda akan dikeluarkan segera. Akun Anda tidak dapat dipulihkan.", + "activate": "Activate", "add": "Tambah", + "alpha": "Alpha", "author": "Penulis", "avatar": "Gambar profil", "back": "Kembali", "cancel": "Batal", "change": "Ubah", "close": "Tutup", + "changes": "Perubahan", "confirm": "Oke", "collapse": "Lipat", "collapse.all": "Lipat Semua", + "color": "Warna", + "coordinates": "Koordinat", "copy": "Salin", "copy.all": "Salin semua", + "copy.success": "{count} disalin!", + "copy.success.multiple": "{count} copied!", + "copy.url": "Copy URL", "create": "Buat", + "custom": "Kustom", "date": "Tanggal", "date.select": "Pilih tanggal", @@ -34,13 +43,20 @@ "delete": "Hapus", "delete.all": "Hapus semua", + "dialog.fields.empty": "Dialog ini tidak memiliki bidang", "dialog.files.empty": "Tidak ada berkas untuk dipilih", "dialog.pages.empty": "Tidak ada halaman untuk dipilih", + "dialog.text.empty": "Dialog ini tidak mendefinisikan teks apa pun", "dialog.users.empty": "Tidak ada pengguna untuk dipilih", "dimensions": "Dimensi", + "disable": "Disable", "disabled": "Dimatikan", "discard": "Buang", + + "drawer.fields.empty": "Drawer ini tidak memiliki bidang", + + "domain": "Domain", "download": "Unduh", "duplicate": "Duplikasi", @@ -49,11 +65,13 @@ "email": "Surel", "email.placeholder": "surel@contoh.com", - "entries": "Entries", - "entry": "Entry", + "enter": "Masuk", + "entries": "Entri", + "entry": "Entri", - "environment": "Environment", + "environment": "Lingkungan", + "error": "Kesalahan", "error.access.code": "Kode tidak valid", "error.access.login": "Upaya masuk tidak valid", "error.access.panel": "Anda tidak diizinkan mengakses panel", @@ -70,17 +88,35 @@ "error.blocks.max.singular": "Anda tidak boleh menambahkan lebih dari satu blok", "error.blocks.min.plural": "Anda setidaknya menambahkan {min} blok", "error.blocks.min.singular": "Anda setidaknya menambahkan satu blok", - "error.blocks.validation": "There's an error on the \"{field}\" field in block {index} using the \"{fieldset}\" block type", + "error.blocks.validation": "Ada kesalahan di bidang \"{field}\" di blok {index} menggunakan \"{fieldset}\" tipe blok", - "error.cache.type.invalid": "Invalid cache type \"{type}\"", + "error.cache.type.invalid": "Tipe tembolok tidak valid \"{type}\"", + + "error.content.lock.delete": "The version is locked and cannot be deleted", + "error.content.lock.move": "The source version is locked and cannot be moved", + "error.content.lock.publish": "This version is already published", + "error.content.lock.replace": "The version is locked and cannot be replaced", + "error.content.lock.update": "The version is locked and cannot be updated", + + "error.entries.max.plural": "You must not add more than {max} entries", + "error.entries.max.singular": "You must not add more than one entry", + "error.entries.min.plural": "You must add at least {min} entries", + "error.entries.min.singular": "You must add at least one entry", + "error.entries.supports": "\"{type}\" field type is not supported for the entries field", + "error.entries.validation": "Ada kesalahan pada bidang \"{field}\" di baris {index}", "error.email.preset.notFound": "Surel \"{name}\" tidak dapat ditemukan", "error.field.converter.invalid": "Konverter \"{converter}\" tidak valid", - "error.field.type.missing": "Field \"{ name }\": The field type \"{ type }\" does not exist", + "error.field.link.options": "Invalid options: {options}", + "error.field.type.missing": "Bidang \"{ name }\": Tipe bidang \"{ type }\" tidak ada", "error.file.changeName.empty": "Nama harus diisi", "error.file.changeName.permission": "Anda tidak diizinkan mengubah nama berkas \"{filename}\"", + "error.file.changeTemplate.invalid": "Templat untuk berkas \"{id}\" tidak dapat diubah menjadi \"{template}\" (valid: \"{blueprints}\")", + "error.file.changeTemplate.permission": "Anda tidak diizinkan mengubah templat untuk berkas \"{id}\"", + + "error.file.delete.multiple": "Not all files could be deleted. Try each remaining file individually to see the specific error that prevents deletion.", "error.file.duplicate": "Berkas dengan nama \"{filename}\" sudah ada", "error.file.extension.forbidden": "Ekstensi \"{extension}\" tidak diizinkan", "error.file.extension.invalid": "Ekstensi tidak valid: {extension}", @@ -95,9 +131,11 @@ "error.file.minheight": "Tinggi gambar setidaknya {height} piksel", "error.file.minsize": "Berkas terlalu kecil", "error.file.minwidth": "Lebar gambar setidaknya {width} piksel", + "error.file.name.unique": "Nama berkas harus unik", "error.file.name.missing": "Nama berkas harus diisi", "error.file.notFound": "Berkas \"{filename}\" tidak dapat ditemukan", "error.file.orientation": "Orientasi gambar harus \"{orientation}\"", + "error.file.sort.permission": "You are not allowed to change the sorting of \"{filename}\"", "error.file.type.forbidden": "Anda tidak diizinkan mengunggah berkas dengan tipe {type}", "error.file.type.invalid": "Tipe berkas tidak valid: {type}", "error.file.undefined": "Berkas tidak dapat ditemukan", @@ -106,22 +144,30 @@ "error.form.notSaved": "Formulir tidak dapat disimpan", "error.language.code": "Masukkan kode bahasa yang valid", + "error.language.create.permission": "You are not allowed to create a language", + "error.language.delete.permission": "You are not allowed to delete the language", "error.language.duplicate": "Bahasa sudah ada", "error.language.name": "Masukkan nama bahasa yang valid", - "error.language.notFound": "The language could not be found", + "error.language.notFound": "Bahasa tidak ditemukan", + "error.language.update.permission": "You are not allowed to update the language", - "error.layout.validation.block": "There's an error on the \"{field}\" field in block {blockIndex} using the \"{fieldset}\" block type in layout {layoutIndex}", + "error.layout.validation.block": "Ada kesalahan pada bidang \"{field}\" di blok {blockIndex} menggunakan tipe blok \"{fieldset}\" di tata letak {layoutIndex}", "error.layout.validation.settings": "Ada kesalahan di pengaturan tata letak {index}", - "error.license.format": "Masukkan kode lisensi yang valid", + "error.license.domain": "The domain for the license is missing", "error.license.email": "Masukkan surel yang valid", + "error.license.format": "Please enter a valid license code", "error.license.verification": "Lisensi tidak dapat diverifikasi", - "error.object.validation": "There’s an error in the \"{label}\" field:\n{message}", + "error.login.totp.confirm.invalid": "Kode tidak valid", + "error.login.totp.confirm.missing": "Please enter the current code", - "error.offline": "The Panel is currently offline", + "error.object.validation": "Ada kesalahan di bidang \"{label}\":\n{message}", + + "error.offline": "Panel saat ini luring", "error.page.changeSlug.permission": "Anda tidak diizinkan mengubah akhiran URL untuk \"{slug}\"", + "error.page.changeSlug.reserved": "Alur halaman-halaman level atas tidak boleh diawali dengan \"{path}\"", "error.page.changeStatus.incomplete": "Halaman memiliki kesalahan dan tidak dapat diterbitkan", "error.page.changeStatus.permission": "Status halaman ini tidak dapat diubah", "error.page.changeStatus.toDraft.invalid": "Halaman \"{slug}\" tidak dapat dikonversi menjadi draf", @@ -133,10 +179,18 @@ "error.page.delete": "Halaman \"{slug}\" tidak dapat dihapus", "error.page.delete.confirm": "Masukkan judul halaman untuk mengonfirmasi", "error.page.delete.hasChildren": "Halaman ini memiliki sub-halaman dan tidak dapat dihapus", + "error.page.delete.multiple": "Not all pages could be deleted. Try each remaining page individually to see the specific error that prevents deletion.", "error.page.delete.permission": "Anda tidak diizinkan menghapus \"{slug}\"", "error.page.draft.duplicate": "Draf halaman dengan akhiran URL \"{slug}\" sudah ada", "error.page.duplicate": "Halaman dengan akhiran URL \"{slug}\" sudah ada", "error.page.duplicate.permission": "Anda tidak diizinkan menduplikasi \"{slug}\"", + "error.page.move.ancestor": "Halaman tidak dapat dipindahkan ke dirinya sendiri", + "error.page.move.directory": "Direktori halaman tidak dapat dipindahkan", + "error.page.move.duplicate": "Suatu sub halaman dengan akhiran URL \"{slug}\" sudah ada", + "error.page.move.noSections": "The page \"{parent}\" cannot be a parent of any page because it lacks any pages sections in its blueprint", + "error.page.move.notFound": "Halaman yang dipindahkan tidak dapat ditemukan", + "error.page.move.permission": "Anda tidak diizinkan memindahkan \"{slug}\"", + "error.page.move.template": "Templat \"{template}\" tidak dapat diterima sebagai sub halaman dari \"{parent}\"", "error.page.notFound": "Halaman \"{slug}\" tidak dapat ditemukan", "error.page.num.invalid": "Masukkan nomor urut yang valid. Nomor tidak boleh negatif.", "error.page.slug.invalid": "Masukkan akhiran URL yang valid", @@ -163,9 +217,11 @@ "error.site.changeTitle.permission": "Anda tidak diizinkan mengubah judul situs", "error.site.update.permission": "Anda tidak diizinkan memperbaharui situs", + "error.structure.validation": "Ada kesalahan pada bidang \"{field}\" di baris {index}", + "error.template.default.notFound": "Templat bawaan tidak ada", - "error.unexpected": "An unexpected error occurred! Enable debug mode for more info: https://getkirby.com/docs/reference/system/options/debug", + "error.unexpected": "Kesalahan tidak terduga terjadi! Hidupkan mode debug untuk informasi lebih lanjut: https://getkirby.com/docs/reference/system/options/debug", "error.user.changeEmail.permission": "Anda tidak diizinkan mengubah surel dari pengguna \"{name}\"", "error.user.changeLanguage.permission": "Anda tidak diizinkan mengubah bahasa dari pengguna \"{name}\"", @@ -183,7 +239,7 @@ "error.user.email.invalid": "Masukkan surel yang valid", "error.user.language.invalid": "Masukkan bahasa yang valid", "error.user.notFound": "Pengguna \"{name}\" tidak dapat ditemukan", - "error.user.password.excessive": "Please enter a valid password. Passwords must not be longer than 1000 characters.", + "error.user.password.excessive": "Masukkan sandi yang valid. Sandi tidak boleh lebih dari 1000 karakter.", "error.user.password.invalid": "Masukkan sandi yang valid. Sandi setidaknya mengandung 8 karakter.", "error.user.password.notSame": "Sandi tidak cocok", "error.user.password.undefined": "Pengguna tidak memiliki sandi", @@ -195,8 +251,10 @@ "error.validation.accepted": "Mohon konfirmasi", "error.validation.alpha": "Masukkan hanya karakter a-z", "error.validation.alphanum": "Masukkan hanya karakter a-z atau 0-9", + "error.validation.anchor": "Masukkan tautan yang valid", "error.validation.between": "Masukkan nilai antara \"{min}\" dan \"{max}\"", "error.validation.boolean": "Mohon konfirmasi atau tolak", + "error.validation.color": "Masukkan warna yang valid dalam format {format}", "error.validation.contains": "Masukkan nilai yang mengandung \"{needle}\"", "error.validation.date": "Masukkan tanggal yang valid", "error.validation.date.after": "Masukkan tanggal setelah {date}", @@ -211,6 +269,7 @@ "error.validation.integer": "Masukkan bilangan bulat yang valid", "error.validation.ip": "Masukkan IP yang valid", "error.validation.less": "Masukkan nilai kurang dari {max}", + "error.validation.linkType": "Tipe tautan tidak diizinkan", "error.validation.match": "Nilai tidak cocok dengan pola yang semestinya", "error.validation.max": "Masukkan nilai yang sama dengan atau kurang dari {max}", "error.validation.maxlength": "Masukkan nilai yang lebih pendek. (maksimal {max} karakter)", @@ -227,15 +286,18 @@ "error.validation.same": "Masukkan \"{other}\"", "error.validation.size": "Ukuran dari nilai harus \"{size}\"", "error.validation.startswith": "Nilai harus diawali dengan \"{start}\"", + "error.validation.tel": "Masukkan nomor telepon tanpa format", "error.validation.time": "Masukkan waktu yang valid", "error.validation.time.after": "Masukkan waktu setelah {time}", "error.validation.time.before": "Masukkan waktu sebelum {time}", "error.validation.time.between": "Masukkan waktu antara {min} dan {max}", + "error.validation.uuid": "Masukkan UUID yang valid", "error.validation.url": "Masukkan URL yang valid", "expand": "Luaskan", "expand.all": "Luaskan Semua", + "field.invalid": "Bidang tidak valid", "field.required": "Bidang ini wajib", "field.blocks.changeType": "Ubah tipe", "field.blocks.code.name": "Kode", @@ -245,8 +307,9 @@ "field.blocks.delete.confirm.all": "Anda yakin menghapus semua blok?", "field.blocks.delete.confirm.selected": "Anda yakin menghapus blok yang dipilih?", "field.blocks.empty": "Belum ada blok", + "field.blocks.fieldsets.empty": "Belum ada set bidang", "field.blocks.fieldsets.label": "Pilih tipe blok …", - "field.blocks.fieldsets.paste": "Press {{ shortcut }} to paste/import blocks from your clipboard", + "field.blocks.fieldsets.paste": "Press {{ shortcut }} to import layouts/blocks from your clipboard Only those allowed in the current field will get inserted.", "field.blocks.gallery.name": "Galeri", "field.blocks.gallery.images.empty": "Belum ada gambar", "field.blocks.gallery.images.label": "Gambar", @@ -254,11 +317,16 @@ "field.blocks.heading.name": "Penajukan", "field.blocks.heading.text": "Teks", "field.blocks.heading.placeholder": "Penajukan …", + "field.blocks.figure.back.plain": "Plain", + "field.blocks.figure.back.pattern.light": "Pattern (light)", + "field.blocks.figure.back.pattern.dark": "Pattern (dark)", "field.blocks.image.alt": "Teks alternatif", - "field.blocks.image.caption": "Deskripsi", + "field.blocks.image.caption": "Keterangan", "field.blocks.image.crop": "Pangkas", "field.blocks.image.link": "Tautan", "field.blocks.image.location": "Lokasi", + "field.blocks.image.location.internal": "This website", + "field.blocks.image.location.external": "External source", "field.blocks.image.name": "Gambar", "field.blocks.image.placeholder": "Pilih gambar", "field.blocks.image.ratio": "Rasio", @@ -275,38 +343,72 @@ "field.blocks.quote.citation.placeholder": "oleh …", "field.blocks.text.name": "Teks", "field.blocks.text.placeholder": "Teks …", + "field.blocks.video.autoplay": "Autoplay", "field.blocks.video.caption": "Deskripsi", + "field.blocks.video.controls": "Controls", + "field.blocks.video.location": "Lokasi", + "field.blocks.video.loop": "Loop", + "field.blocks.video.muted": "Muted", "field.blocks.video.name": "Video", "field.blocks.video.placeholder": "Masukkan URL video", + "field.blocks.video.poster": "Poster", + "field.blocks.video.preload": "Preload", "field.blocks.video.url.label": "URL Video", "field.blocks.video.url.placeholder": "https://youtube.com/?v=", - "field.files.empty": "Belum ada berkas yang dipilih", + "field.entries.delete.confirm.all": "Do you really want to delete all entries?", + "field.entries.empty": "Belum ada entri", + "field.files.empty": "Belum ada berkas yang dipilih", + "field.files.empty.single": "No file selected yet", + + "field.layout.change": "Change layout", "field.layout.delete": "Hapus tata letak", "field.layout.delete.confirm": "Anda yakin menghapus tata letak ini?", + "field.layout.delete.confirm.all": "Do you really want to delete all layouts?", "field.layout.empty": "Belum ada baris", "field.layout.select": "Pilih tata letak", "field.object.empty": "No information yet", "field.pages.empty": "Belum ada halaman yang dipilih", + "field.pages.empty.single": "No page selected yet", "field.structure.delete.confirm": "Anda yakin menghapus baris ini?", "field.structure.delete.confirm.all": "Do you really want to delete all entries?", "field.structure.empty": "Belum ada entri", "field.users.empty": "Belum ada pengguna yang dipilih", + "field.users.empty.single": "No user selected yet", + "fields.empty": "No fields yet", + + "file": "Berkas", "file.blueprint": "This file has no blueprint yet. You can define the setup in /site/blueprints/files/{blueprint}.yml", + "file.changeTemplate": "Ubah templat", + "file.changeTemplate.notice": "Changing the file's template will remove content for fields that don't match in type. If the new template defines certain rules, e.g. image dimensions, those will also be applied irreversibly. Use with caution.", "file.delete.confirm": "Anda yakin menghapus
{filename}?", + "file.focus.placeholder": "Set focal point", + "file.focus.reset": "Remove focal point", + "file.focus.title": "Focus", "file.sort": "Ubah posisi", "files": "Berkas", + "files.delete.confirm.selected": "Do you really want to delete the selected files? This action cannot be undone.", "files.empty": "Belum ada berkas", + "filter": "Filter", + + "form.discard": "Discard changes", + "form.discard.confirm": "Do you really want to discard all your changes?", + "form.locked": "This content is disabled for you as it is currently edited by another user", + "form.unsaved": "The current changes have not yet been saved", + "form.preview": "Preview changes", + "form.preview.draft": "Preview draft", + "hide": "Sembunyikan", "hour": "Jam", + "hue": "Hue", "import": "Import", "info": "Info", "insert": "Sisipkan", @@ -324,7 +426,6 @@ "installation.issues.mbstring": "Ekstensi MB String diperlukan", "installation.issues.media": "Folder /media tidak ada atau tidak dapat ditulis", "installation.issues.php": "Pastikan Anda menggunakan PHP 8+", - "installation.issues.server": "Kirby memerlukan Apache, Nginx, atau Caddy", "installation.issues.sessions": "Folder /site/sessions tidak ada atau tidak dapat ditulis", "language": "Bahasa", @@ -332,6 +433,7 @@ "language.convert": "Atur sebagai bawaan", "language.convert.confirm": "

Anda yakin mengubah {name} menjadi bahasa bawaan? Ini tidak dapat dibatalkan.

Jika {name} memiliki konten yang tidak diterjemahkan, tidak akan ada pengganti yang valid dan dapat menyebabkan beberapa bagian dari situs Anda menjadi kosong.

", "language.create": "Tambah bahasa baru", + "language.default": "Bahasa bawaan", "language.delete.confirm": "Anda yakin menghapus bahasa {name} termasuk semua terjemahannya? Ini tidak dapat dibatalkan!", "language.deleted": "Bahasa sudah dihapus", "language.direction": "Arah baca", @@ -340,7 +442,16 @@ "language.locale": "String \"PHP locale\"", "language.locale.warning": "Anda menggunakan pengaturan lokal ubah suaian. Ubah di berkas bahasa di /site/languages", "language.name": "Nama", + "language.secondary": "Secondary language", + "language.settings": "Language settings", "language.updated": "Bahasa sudah diperbaharui", + "language.variables": "Language variables", + "language.variables.empty": "No translations yet", + + "language.variable.delete.confirm": "Do you really want to delete the variable for {key}?", + "language.variable.key": "Key", + "language.variable.notFound": "The variable could not be found", + "language.variable.value": "Value", "languages": "Bahasa", "languages.default": "Bahasa bawaan", @@ -349,15 +460,32 @@ "languages.secondary.empty": "Belum ada bahasa sekunder", "license": "Lisensi Kirby", + "license.activate": "Activate it now", + "license.activate.label": "Please activate your license", + "license.activate.domain": "Your license will be activated for {host}.", + "license.activate.local": "You are about to activate your Kirby license for your local domain {host}. If this site will be deployed to a public domain, please activate it there instead. If {host} is the domain you want to use your license for, please continue.", + "license.activated": "Activated", "license.buy": "Beli lisensi", - "license.register": "Daftar", + "license.code": "Kode", + "license.code.help": "You received your license code after the purchase via email. Please copy and paste it here.", + "license.code.label": "Masukkan kode lisensi Anda", + "license.status.active.info": "Includes new major versions until {date}", + "license.status.active.label": "Valid license", + "license.status.demo.info": "This is a demo installation", + "license.status.demo.label": "Demo", + "license.status.inactive.info": "Renew license to update to new major versions", + "license.status.inactive.label": "No new major versions", + "license.status.legacy.bubble": "Ready to renew your license?", + "license.status.legacy.info": "Your license does not cover this version", + "license.status.legacy.label": "Please renew your license", + "license.status.missing.bubble": "Ready to launch your site?", + "license.status.missing.info": "No valid license", + "license.status.missing.label": "Please activate your license", + "license.status.unknown.info": "The license status is unknown", + "license.status.unknown.label": "Unknown", "license.manage": "Manage your licenses", - "license.register.help": "Anda menerima kode lisensi via surel setelah pembelian. Salin dan tempel kode tersebut untuk mendaftarkan.", - "license.register.label": "Masukkan kode lisensi Anda", - "license.register.domain": "Your license will be registered to {host}.", - "license.register.local": "You are about to register your license for your local domain {host}. If this site will be deployed to a public domain, please register it there instead. If {host} is the domain you want to license Kirby to, please continue.", - "license.register.success": "Terima kasih atas dukungan untuk Kirby", - "license.unregistered": "Ini adalah demo tidak diregistrasi dari Kirby", + "license.purchased": "Purchased", + "license.success": "Terima kasih atas dukungan untuk Kirby", "license.unregistered.label": "Unregistered", "link": "Tautan", @@ -367,17 +495,21 @@ "lock.unsaved": "Perubahan belum tersimpan", "lock.unsaved.empty": "Tidak ada lagi perubahan belum tersimpan", - "lock.isLocked": "Perubahan belum tersimpan oleh {email}", - "lock.file.isLocked": "Berkas sedang disunting oleh {email} dan tidak dapat diubah.", - "lock.page.isLocked": "Halaman sedang disunting oleh {email} dan tidak dapat diubah.", + "lock.unsaved.files": "Unsaved files", + "lock.unsaved.pages": "Unsaved pages", + "lock.unsaved.users": "Unsaved accounts", + "lock.isLocked": "Unsaved changes by {email}", "lock.unlock": "Buka kunci", - "lock.isUnlocked": "Perubahan Anda yang belum tersimpan telah terubah oleh pengguna lain. Anda dapat mengunduh perubahan Anda untuk menggabungkannya manual.", + "lock.unlock.submit": "Unlock and overwrite unsaved changes by {email}", + "lock.isUnlocked": "Was unlocked by another user", "login": "Masuk", "login.code.label.login": "Kode masuk", "login.code.label.password-reset": "Kode atur ulang sandi", "login.code.placeholder.email": "000 000", + "login.code.placeholder.totp": "000000", "login.code.text.email": "Jika alamat surel terdaftar, kode yang diminta dikirim via surel", + "login.code.text.totp": "Please enter the one‑time code from your authenticator app.", "login.email.login.body": "Hi {user.nameOrEmail},\n\nYou recently requested a login code for the Panel of {site}.\nThe following login code will be valid for {timeout} minutes:\n\n{code}\n\nIf you did not request a login code, please ignore this email or contact your administrator if you have questions.\nFor security, please DO NOT forward this email.", "login.email.login.subject": "Kode masuk Anda", "login.email.password-reset.body": "Hi {user.nameOrEmail},\n\nYou recently requested a password reset code for the Panel of {site}.\nThe following password reset code will be valid for {timeout} minutes:\n\n{code}\n\nIf you did not request a password reset code, please ignore this email or contact your administrator if you have questions.\nFor security, please DO NOT forward this email.", @@ -388,9 +520,24 @@ "login.toggleText.code.email-password": "Masuk dengan sandi", "login.toggleText.password-reset.email": "Lupa sandi Anda?", "login.toggleText.password-reset.email-password": "← Kembali ke masuk", + "login.totp.enable.option": "Set up one‑time codes", + "login.totp.enable.intro": "Authenticator apps can generate one‑time codes that are used as a second factor when signing into your account.", + "login.totp.enable.qr.label": "1. Scan this QR code", + "login.totp.enable.qr.help": "Unable to scan? Add the setup key {secret} manually to your authenticator app.", + "login.totp.enable.confirm.headline": "2. Confirm with generated code", + "login.totp.enable.confirm.text": "Your app generates a new one‑time code every 30 seconds. Enter the current code to complete the setup:", + "login.totp.enable.confirm.label": "Current code", + "login.totp.enable.confirm.help": "After this setup, we will ask you for a one‑time code every time you log in.", + "login.totp.enable.success": "One‑time codes enabled", + "login.totp.disable.option": "Disable one‑time codes", + "login.totp.disable.label": "Enter your password to disable one‑time codes", + "login.totp.disable.help": "In the future, a different second factor like a login code sent via email will be requested when you log in. You can always set up one‑time codes again later.", + "login.totp.disable.admin": "

This will disable one‑time codes for {user}.

In the future, a different second factor like a login code sent via email will be requested when they log in. {user} can set up one‑time codes again after their next login.

", + "login.totp.disable.success": "One‑time codes disabled", "logout": "Keluar", + "merge": "Merge", "menu": "Menu", "meridiem": "AM/PM", "mime": "Tipe Media", @@ -411,21 +558,26 @@ "months.september": "September", "more": "Lebih lanjut", + "move": "Move", "name": "Nama", "next": "Selanjutnya", + "night": "Night", "no": "tidak", "off": "mati", "on": "hidup", "open": "Buka", "open.newWindow": "Buka di jendela baru", + "option": "Option", "options": "Opsi", "options.none": "Tidak ada opsi", + "options.all": "Show all {count} options", "orientation": "Orientasi", "orientation.landscape": "Rebah", "orientation.portrait": "Tegak", "orientation.square": "Persegi", + "page": "Halaman", "page.blueprint": "This page has no blueprint yet. You can define the setup in /site/blueprints/pages/{blueprint}.yml", "page.changeSlug": "Ubah URL", "page.changeSlug.fromTitle": "Buat dari judul", @@ -433,13 +585,15 @@ "page.changeStatus.position": "Pilih posisi", "page.changeStatus.select": "Pilih status baru", "page.changeTemplate": "Ubah templat", + "page.changeTemplate.notice": "Changing the page's template will remove content for fields that don't match in type. Use with caution.", + "page.create": "Create as {status}", "page.delete.confirm": "Anda yakin menghapus {title}?", "page.delete.confirm.subpages": "Halaman ini memiliki sub-halaman.
Semua sub-halaman akan ikut dihapus.", "page.delete.confirm.title": "Masukkan judul halaman untuk mengonfirmasi", - "page.draft.create": "Buat draf", "page.duplicate.appendix": "Salin", "page.duplicate.files": "Salin berkas", "page.duplicate.pages": "Salin halaman", + "page.move": "Move page", "page.sort": "Ubah posisi", "page.status": "Status", "page.status.draft": "Draf", @@ -450,6 +604,7 @@ "page.status.unlisted.description": "Halaman hanya dapat diakses via URL", "pages": "Halaman", + "pages.delete.confirm.selected": "Do you really want to delete the selected pages? This action cannot be undone.", "pages.empty": "Belum ada halaman", "pages.status.draft": "Draf", "pages.status.listed": "Dipublikasikan", @@ -460,14 +615,21 @@ "password": "Sandi", "paste": "Paste", "paste.after": "Paste after", + "paste.success": "{count} pasted!", "pixel": "Piksel", "plugin": "Plugin", "plugins": "Plugins", "prev": "Sebelumnya", "preview": "Pratinjau", + + "publish": "Publish", + "published": "Dipublikasikan", + "remove": "Hapus", "rename": "Ubah nama", + "renew": "Renew", "replace": "Ganti", + "replace.with": "Replace with", "retry": "Coba lagi", "revert": "Kembalikan", "revert.confirm": "Anda yakin menghapus semua perubahan yang belum tersimpan?", @@ -482,34 +644,46 @@ "role.nobody.title": "Tidak siapapun", "save": "Simpan", + "saved": "Saved", "search": "Cari", + "searching": "Searching", "search.min": "Masukkan {min} karakter untuk mencari", - "search.all": "Tampilkan semua", + "search.all": "Show all {count} results", "search.results.none": "Tidak ada hasil", + "section.invalid": "Bagian ini tidak valid", "section.required": "Bagian ini wajib", - "security": "Security", + "security": "Keamanan", "select": "Pilih", - "server": "Server", + "server": "Peladen", "settings": "Pengaturan", "show": "Tampilkan", "site.blueprint": "Situs ini belum memiliki cetak biru. Anda dapat mendefinisikannya di /site/blueprints/site.yml", "size": "Ukuran", "slug": "Akhiran URL", "sort": "Urutkan", + "sort.drag": "Geser untuk mengurutkan …", + "split": "Pisahkan", - "stats.empty": "No reports", - "system.issues.content": "The content folder seems to be exposed", - "system.issues.eol.kirby": "Your installed Kirby version has reached end-of-life and will not receive further security updates", - "system.issues.eol.plugin": "Your installed version of the { plugin } plugin is has reached end-of-life and will not receive further security updates", + "stats.empty": "Tidak ada laporan", + "status": "Status", + + "system.info.copy": "Copy info", + "system.info.copied": "System info copied", + "system.issues.content": "Folder konten nampaknya terekspos", + "system.issues.eol.kirby": "Versi instalasi Kirby Anda sudah mencapai akhir dan tidak akan lagi mendapat pembaruan keamanan", + "system.issues.eol.plugin": "Versi instalasi plugin { plugin } Anda sudah mencapai akhir dan tidak akan lagi mendapatkan pembaruan keamanan", + "system.issues.eol.php": "Your installed PHP release { release } has reached end-of-life and will not receive further security updates", "system.issues.debug": "Debugging must be turned off in production", "system.issues.git": "The .git folder seems to be exposed", "system.issues.https": "We recommend HTTPS for all your sites", "system.issues.kirby": "The kirby folder seems to be exposed", + "system.issues.local": "The site is running locally with relaxed security checks", "system.issues.site": "The site folder seems to be exposed", - "system.issues.vulnerability.kirby": "Your installation might be affected by the following vulnerability ({ severity } severity): { description }", - "system.issues.vulnerability.plugin": "Your installation might be affected by the following vulnerability in the { plugin } plugin ({ severity } severity): { description }", + "system.issues.vue.compiler": "The Vue template compiler is enabled", + "system.issues.vulnerability.kirby": "Instalasi Anda mungkin terpengaruh oleh celah keamanan berikut ({ severity } severity): { description }", + "system.issues.vulnerability.plugin": "Instalasi Anda mungkin terpengaruh oleh celah keamanan di dalam plugin { plugin } ({ severity } severity): { description }", "system.updateStatus": "Update status", "system.updateStatus.error": "Could not check for updates", "system.updateStatus.not-vulnerable": "No known vulnerabilities", @@ -520,10 +694,19 @@ "system.updateStatus.update": "Free update { version } available", "system.updateStatus.upgrade": "Upgrade { version } available", - "title": "Judul", + "tel": "Phone", + "tel.placeholder": "+49123456789", "template": "Templat", + + "theme": "Theme", + "theme.light": "Lights on", + "theme.dark": "Lights off", + "theme.automatic": "Match system default", + + "title": "Judul", "today": "Hari ini", + "toolbar.button.clear": "Clear formatting", "toolbar.button.code": "Kode", "toolbar.button.bold": "Tebal", "toolbar.button.email": "Surel", @@ -541,6 +724,8 @@ "toolbar.button.link": "Tautan", "toolbar.button.paragraph": "Paragraph", "toolbar.button.strike": "Coret", + "toolbar.button.sub": "Subscript", + "toolbar.button.sup": "Superscript", "toolbar.button.ol": "Daftar berurut", "toolbar.button.underline": "Garis bawah", "toolbar.button.ul": "Daftar tidak berurut", @@ -550,6 +735,8 @@ "translation.name": "Bahasa Indonesia", "translation.locale": "id_ID", + "type": "Type", + "upload": "Unggah", "upload.error.cantMove": "Berkas unggahan tidak dapat dipindahkan", "upload.error.cantWrite": "Gagal menyimpan berkas", @@ -574,6 +761,7 @@ "user.changeLanguage": "Ubah bahasa", "user.changeName": "Ubah nama pengguna ini", "user.changePassword": "Ubah sandi", + "user.changePassword.current": "Your current password", "user.changePassword.new": "Sandi baru", "user.changePassword.new.confirm": "Konfirmasi sandi baru…", "user.changeRole": "Ubah peran", @@ -585,10 +773,13 @@ "users": "Pengguna", "version": "Versi", + "version.changes": "Changed version", + "version.compare": "Compare versions", "version.current": "Current version", "version.latest": "Latest version", "versionInformation": "Version information", + "view": "View", "view.account": "Akun Anda", "view.installation": "Pemasangan", "view.languages": "Bahasa", diff --git a/kirby/i18n/translations/is_IS.json b/kirby/i18n/translations/is_IS.json index f6d6259..a8fff49 100644 --- a/kirby/i18n/translations/is_IS.json +++ b/kirby/i18n/translations/is_IS.json @@ -3,19 +3,28 @@ "account.delete": "Eyða notandareikning þínum", "account.delete.confirm": "Ertu alveg viss um að þú viljir endanlega eyða reikningnum þínum? Þú munt verða útskráð/ur án tafar. Ómögulegt verður að endurheimta reikninginn þinn.", + "activate": "Virkja", "add": "Bæta við", + "alpha": "Gagnsæi", "author": "Höfundur", "avatar": "Prófíl mynd", "back": "Til baka", "cancel": "Hætta við", "change": "Breyta", "close": "Loka", + "changes": "Breytingar", "confirm": "OK", "collapse": "Fella", "collapse.all": "Fella allt", + "color": "Litur", + "coordinates": "Hnit", "copy": "Afrita", "copy.all": "Afrita allt", + "copy.success": "Afritaði {count}!", + "copy.success.multiple": "Afritaði {count}!", + "copy.url": "Afrita slóð", "create": "Stofna", + "custom": "Sérstillt", "date": "Dagsetning", "date.select": "Veldu dagsetningu", @@ -34,13 +43,20 @@ "delete": "Eyða", "delete.all": "Eyða hreint öllu", + "dialog.fields.empty": "Þessi valmynd hefur engin svið", "dialog.files.empty": "Engar skrár til að velja úr", "dialog.pages.empty": "Engar síður til að velja úr", + "dialog.text.empty": "þessi valmynd skilgreinir engan texta", "dialog.users.empty": "Engir notendur til að velja úr", "dimensions": "Rýmd", + "disable": "Afvirkja", "disabled": "Óvirkt", "discard": "Hunsa", + + "drawer.fields.empty": "Þessi skúffa hefur engin svið", + + "domain": "Lén", "download": "Hlaða niður", "duplicate": "Klóna", @@ -49,11 +65,13 @@ "email": "Netfang", "email.placeholder": "nafn@netfang.is", + "enter": "Venda", "entries": "Færslur", "entry": "Færsla", "environment": "Umhverfi", + "error": "Villa", "error.access.code": "Ógildur kóði", "error.access.login": "Ógild innskráning", "error.access.panel": "Þú hefur ekkert leyfi til að nota panelinn", @@ -74,13 +92,31 @@ "error.cache.type.invalid": "Ógyld skyndiminnisgerð \"{type}\"", + "error.content.lock.delete": "Þessi útgáfa er læst og henni verður ekki eytt", + "error.content.lock.move": "Þessi útgáfa er læst og hún verður ekki færð", + "error.content.lock.publish": "Þessi útgáfa er núþegar útgefin", + "error.content.lock.replace": "Þessi útfáfa er læst og það verður ekki skipt út", + "error.content.lock.update": "Þessi útgáfa er læst og hún verður ekki uppfærð", + + "error.entries.max.plural": "You must not add more than {max} entries", + "error.entries.max.singular": "You must not add more than one entry", + "error.entries.min.plural": "You must add at least {min} entries", + "error.entries.min.singular": "You must add at least one entry", + "error.entries.supports": "\"{type}\" field type is not supported for the entries field", + "error.entries.validation": "Það er villa í \"{field}\" sviðinu í röð {index}", + "error.email.preset.notFound": "Netfangstillingarnar: \"{name}\" fundust ekki", "error.field.converter.invalid": "Ógildur umbreytari \"{converter}\"", + "error.field.link.options": "Invalid options: {options}", "error.field.type.missing": "Sviðið \"{ name }\": Sviðgerðin er \"{type}\" er alls ekki til.", "error.file.changeName.empty": "Nafn skal fylla út", "error.file.changeName.permission": "Þú mátt ekkert breyta nafninu á skránni \"{filename}\"", + "error.file.changeTemplate.invalid": "Sniðmátinu fyrir skránna \"{id}\" er ekki hægt að breyta í \"{template}\" (gild: \"{blueprints}\")", + "error.file.changeTemplate.permission": "Þú mátt ekkert breyta sniðmátinu fyrir skránna \"{id}\"", + + "error.file.delete.multiple": "Not all files could be deleted. Try each remaining file individually to see the specific error that prevents deletion.", "error.file.duplicate": "Skrá með nafninu \"{filename}\" er nú þegar til", "error.file.extension.forbidden": "Skrárendingin \"{extension}\" er ekki leyfð", "error.file.extension.invalid": "Óleyfilegt skrársnið hér: {extension}", @@ -95,9 +131,11 @@ "error.file.minheight": "Hæð myndarinnar þarf að vera minnst {height} punktar", "error.file.minsize": "Skráin er of smá", "error.file.minwidth": "Breidd myndarinnar þarf að vera minnst {width} punktar", + "error.file.name.unique": "Skrárnafnið þarf að vera einstakt", "error.file.name.missing": "Skrárnafnið má ekki skilja eftir tómt", "error.file.notFound": "Skráin \"{filename}\" fannst ekki", "error.file.orientation": "Snið myndarinnar þarf að vera \"{orientation}\"", + "error.file.sort.permission": "Þú mátt ekkert breyta röðuninni á \"{filename}\"", "error.file.type.forbidden": "Þú mátt ekkert hlaða inn {type} skrám", "error.file.type.invalid": "Ógild skrártegund: {type}", "error.file.undefined": "Skráin fannst ekki", @@ -106,37 +144,53 @@ "error.form.notSaved": "Ekki tókst að vista upplýsingar úr forminu", "error.language.code": "Gófúslega settu inn gildan kóða fyrir tungumál", + "error.language.create.permission": "You are not allowed to create a language", + "error.language.delete.permission": "You are not allowed to delete the language", "error.language.duplicate": "Þetta tungumál er nú þegar skráð", "error.language.name": "Gott og gyllt nafn fyrir tungumálið", "error.language.notFound": "Tungumálið fannst ekkert", + "error.language.update.permission": "You are not allowed to update the language", "error.layout.validation.block": "Það er villa í {field} sviðinu í bálkinum {blockIndex} sem notar {fieldset} bálkgerðina í rammanum {layoutIndex}", "error.layout.validation.settings": "Hér er villa í sitllingum fyrir ramman {index}", - "error.license.format": "Gildur leyfiskóði hér", + "error.license.domain": "Lénið fyrir skráningarleyfið vantar", "error.license.email": "Almennilegt netfang hér", + "error.license.format": "Vinsamlegast og fyrir alla muni settu inn gildan leyfiskóða", "error.license.verification": "Ekki heppnaðist að staðfesta leyfið", + "error.login.totp.confirm.invalid": "Ógildur kóði", + "error.login.totp.confirm.missing": "Settu inn núverandi lykilkóða", + "error.object.validation": "Það er villa í \"{label}\" sviðinu:\n{message}", "error.offline": "Stjórnborðið er óvirkt eins og stendur.", "error.page.changeSlug.permission": "Þú hefur ekkert leyfi til þess að breyta slóðarviðskeytinu fyrir \"{slug}\"", + "error.page.changeSlug.reserved": "Slóð síðna í rótinni verður að byrja með \"{path}\"", "error.page.changeStatus.incomplete": "Það eru villur á síðunni og við getum ekki gefið hana út", "error.page.changeStatus.permission": "Stöðu síðunnar var ekki hægt að breyta", "error.page.changeStatus.toDraft.invalid": "Síðunni \"{slug}\" er ekki hægt að breyta í uppkast", "error.page.changeTemplate.invalid": "Sniðmáti fyrir síðuna \"{slug}\" er ekki hægt að breyta", "error.page.changeTemplate.permission": "Þú hefur engan veginn leyfi til að breyta sniðmáti fyrir síðuna \"{slug}\"", - "error.page.changeTitle.empty": "Ekki skilja titilinn eftir tóman", + "error.page.changeTitle.empty": "Titillinn getur ekki verið óskilgreindur", "error.page.changeTitle.permission": "Þú mátt ekki breyta titlinum fyrir \"{slug}\"", "error.page.create.permission": "Þú hefur ekki leyfi til að stofna \"{slug}\"", "error.page.delete": "Síðunni \"{slug}\" er ekki hægt að eyða", "error.page.delete.confirm": "Ritaðu titil síðunnar til að staðfesta", "error.page.delete.hasChildren": "Síðan hefur undirsíður og er því ekki hægt að eyða", + "error.page.delete.multiple": "Not all pages could be deleted. Try each remaining page individually to see the specific error that prevents deletion.", "error.page.delete.permission": "Þú mátt ekkert eyða \"{slug}\"", "error.page.draft.duplicate": "Uppkast með slóðinni \"{slug}\" er þegar til", "error.page.duplicate": "Síða með slóðinni \"{slug}\" er þegar til", "error.page.duplicate.permission": "Þú mátt ekki klóna \"{slug}\"", + "error.page.move.ancestor": "Það er ekki hægt að færa síðuna á sjálfa sig.", + "error.page.move.directory": "Ekki er reyndist unnt að færa möppu síðunnar.", + "error.page.move.duplicate": "Undirsíða með slóðinni og forskeytinu \"{slug}\" er núþegar til", + "error.page.move.noSections": "Síðan \"{parent}\" getur ekki átt undirsíður þar sem tilskylin svið til umsýslu á undirsíðum vantar", + "error.page.move.notFound": "Síðan sem færð var finnst því miður ekki", + "error.page.move.permission": "Þú mátt ekkert færa \"{slug}\"", + "error.page.move.template": "Sniðmátið \"{template}\" er ekki gillt sem undirsíða af \"{parent}\"", "error.page.notFound": "Síðan \"{slug}\" fannst ekkert", "error.page.num.invalid": "Veldu ákjósanlega raðtölu. Neikvæðar tölur bannaðar.", "error.page.slug.invalid": "Veldu ákjósanlega vefslóð", @@ -163,6 +217,8 @@ "error.site.changeTitle.permission": "Þú mátt ekkert breyta titil vefsvæðisins", "error.site.update.permission": "Þú mátt ekkert uppfæra vefsvæðið", + "error.structure.validation": "Það er villa í \"{field}\" sviðinu í röð {index}", + "error.template.default.notFound": "Ekkert sjálfgefið sniðmát fannst", "error.unexpected": "Það átti sér stað óvænt villa. Notaðu lúsarleitarhaminn (e. debug mode) til að skilja þetta betur. \nFyrir nánari upplýsingar: https://getkirby.com/docs/reference/system/options/debug", @@ -180,7 +236,7 @@ "error.user.delete.lastUser": "Síðasta notandanum er ekki hægt að eyða", "error.user.delete.permission": "Þú mátt ekkert eyða notandanum \"{name}\"", "error.user.duplicate": "Nú þegar finnst notandi með þetta netfang: \"{email}\"", - "error.user.email.invalid": "Almennilegt netfang hér", + "error.user.email.invalid": "Vinsamlegast ákjósanlegt netfang", "error.user.language.invalid": "Vinsamlegast ákjósanlegt tungumál", "error.user.notFound": "Þessi notandi; \"{name}\" fannst ekki", "error.user.password.excessive": "Vinsamlegast settu inn gilt lykilorð. Lykilorð hér meiga ekki vera lengri en 1000 stafabil.", @@ -195,8 +251,10 @@ "error.validation.accepted": "Staðfestu", "error.validation.alpha": "Aðeins stafir úr Enska stafrófinu, a-z", "error.validation.alphanum": "Aðeins stafir úr Enska stafrófinu, a-z eða tölustafir 0-9", + "error.validation.anchor": "Vinsamlegast rétt og gillt merki", "error.validation.between": "Gildi milli \"{min}\" og \"{max}\"", "error.validation.boolean": "Staðfestu eða hafnaðu þessu", + "error.validation.color": "Endilega settu inn gildan lit í sniðinu {format}", "error.validation.contains": "Settu inni gildi er inniheldur \"{needle}\"", "error.validation.date": "Ákjósanlega dagsetningu", "error.validation.date.after": "Dagsetningu eftir {date}", @@ -204,13 +262,14 @@ "error.validation.date.between": "Dagsetningu milli {min} og {max}", "error.validation.denied": "Hafnaðu", "error.validation.different": "Gildið má ekki vera \"{other}\"", - "error.validation.email": "Almennilegt netfang hér", + "error.validation.email": "Ákjósanlegt netfang", "error.validation.endswith": "Gildið verður að enda á \"{end}\"", "error.validation.filename": "Ákjósanlegt skrárnafn", "error.validation.in": "Vinsamlegast skráðu eitt af eftirfarandi: ({in})", "error.validation.integer": "Skráðu heiltölu", "error.validation.ip": "Skráðu ákjósanlega IP tölu", "error.validation.less": "Skráðu gildi lægra en {max}", + "error.validation.linkType": "Þessi tengilsgerð er ekki leyfð hér um slóðir.", "error.validation.match": "Gildið er ekki eftir væntingum", "error.validation.max": "Skráðu gildi sem er ekki hærra en {max}", "error.validation.maxlength": "Veldu eitthvað styttra. (hámark {max} stafir)", @@ -227,38 +286,47 @@ "error.validation.same": "Skráðu \"{other}\"", "error.validation.size": "Gildið þarf að vera \"{size}\"", "error.validation.startswith": "Þetta þarf að byrja á \"{start}\"", + "error.validation.tel": "Vinsamlegast ósniðið símanúmer hér.", "error.validation.time": "Ákjósanlegur tími", "error.validation.time.after": "Veldu tíma eftir {time}", "error.validation.time.before": "Veldu tíma fyrir{time}", "error.validation.time.between": "Veldu tíma milli {min} og {max}", + "error.validation.uuid": "Vinsamlegast gillt UUID (Notandakenni)", "error.validation.url": "Ákjósanleg vefslóð", "expand": "Þenja út", "expand.all": "Þenja allt út", + "field.invalid": "Þetta svið er bara ógillt sem stendur.", "field.required": "Þetta svið er nauðsynlegt", "field.blocks.changeType": "Breyta um bálkagerð", - "field.blocks.code.name": "Kóðasnið", + "field.blocks.code.name": "Kóði", "field.blocks.code.language": "Tungumal", "field.blocks.code.placeholder": "Kóðinn þinn …", "field.blocks.delete.confirm": "Ætlarðu virkilega að eyða þessum bálk?", "field.blocks.delete.confirm.all": "Ertu nú alveg viss um að þú viljir eyða öllum þessum bálkum?", "field.blocks.delete.confirm.selected": "Viltu virkilega eyða völdum bálkum?", "field.blocks.empty": "Öngvir bálkar enn", + "field.blocks.fieldsets.empty": "Engin sviðasett enn", "field.blocks.fieldsets.label": "Veldu bálkagerð …", - "field.blocks.fieldsets.paste": "Notaðu {{ shortcut }} flýtilyklaaðgerðina til að setja bálkinn hér.", + "field.blocks.fieldsets.paste": "Ýttu á {{ shortcut }} til þess að flytja raðir/bálka hingað Aðeins þeir sem eru gildir hér mun verða færðir hingað.", "field.blocks.gallery.name": "Myndasafn", "field.blocks.gallery.images.empty": "Engar myndir enn", "field.blocks.gallery.images.label": "Myndir", "field.blocks.heading.level": "Stig", "field.blocks.heading.name": "Fyrirsögn", - "field.blocks.heading.text": "Prósi", + "field.blocks.heading.text": "Texti/Prósi", "field.blocks.heading.placeholder": "Fyrirsögn …", + "field.blocks.figure.back.plain": "Látlaust", + "field.blocks.figure.back.pattern.light": "Mynstur (ljóst)", + "field.blocks.figure.back.pattern.dark": "Mynstur (dökkt)", "field.blocks.image.alt": "ALT texti", "field.blocks.image.caption": "Myndartexti", "field.blocks.image.crop": "Kroppa", "field.blocks.image.link": "Tengill", "field.blocks.image.location": "Staðsetning", + "field.blocks.image.location.internal": "Þetta vefsvæði", + "field.blocks.image.location.external": "Ytri kelda", "field.blocks.image.name": "Mynd", "field.blocks.image.placeholder": "Veldu mynd", "field.blocks.image.ratio": "Hlutfall", @@ -266,47 +334,81 @@ "field.blocks.line.name": "Lína", "field.blocks.list.name": "Listi", "field.blocks.markdown.name": "Markdown", - "field.blocks.markdown.label": "Prósi", + "field.blocks.markdown.label": "Texti", "field.blocks.markdown.placeholder": "Markdown …", "field.blocks.quote.name": "Tilvitnun", - "field.blocks.quote.text.label": "Prósi", + "field.blocks.quote.text.label": "Innihald tilvitnunar", "field.blocks.quote.text.placeholder": "Þessi tilvitnun …", "field.blocks.quote.citation.label": "Heimild", "field.blocks.quote.citation.placeholder": "eftir …", "field.blocks.text.name": "Prósi", "field.blocks.text.placeholder": "Þessi prósi …", - "field.blocks.video.caption": "Myndartexti", + "field.blocks.video.autoplay": "Sjálfspila", + "field.blocks.video.caption": "Myndskeiðstexti", + "field.blocks.video.controls": "Stjórnhnappar", + "field.blocks.video.location": "Staðsetning", + "field.blocks.video.loop": "Lykkja", + "field.blocks.video.muted": "Þaggað", "field.blocks.video.name": "Myndskeið", "field.blocks.video.placeholder": "Vefslóð myndskeiðs (URL)", + "field.blocks.video.poster": "Plakkat", + "field.blocks.video.preload": "Forhlaða", "field.blocks.video.url.label": "Vefslóð", "field.blocks.video.url.placeholder": "https://youtube.com/?v=", - "field.files.empty": "Engar skrár valdar ennþá", + "field.entries.delete.confirm.all": "Ætlar þú virkilega að eyða öllum færslum?", + "field.entries.empty": "Engar færslur enn", + "field.files.empty": "Engar skrár valdar ennþá", + "field.files.empty.single": "Engin skrá valin enn", + + "field.layout.change": "Breyta uppsetningu ramma", "field.layout.delete": "Eyða ramma", "field.layout.delete.confirm": "Ætlarðu virkilega að eyða þessum ramma?", + "field.layout.delete.confirm.all": "Ætlarðu virkilega að eyða öllum römmum?", "field.layout.empty": "Nei. Engir rammar enn.", "field.layout.select": "Veldu rammategund", "field.object.empty": "Engar upplýsingar enn", "field.pages.empty": "Engar síður valdar ennþá", + "field.pages.empty.single": "Engin síða valin enn", "field.structure.delete.confirm": "Viltu virkilega eyða þessari röð?", "field.structure.delete.confirm.all": "Ætlar þú virkilega að eyða öllum færslum?", "field.structure.empty": "Engar færslur enn", "field.users.empty": "Engir notendur valdir enn", + "field.users.empty.single": "Enginn notandi valinn enn", + "fields.empty": "Hér eru engin svið enn", + + "file": "Skrár", "file.blueprint": "Þessi skrá hefur ekki skipan (e. blueprint) ennþá. Þú mátt skilgreina skipanina í /site/blueprints/{template}.yml", + "file.changeTemplate": "Breyta sniðmáti", + "file.changeTemplate.notice": "Að breyta sniðmáti skránnar mun fjarlæjga efnið er tilheyrir þeim sviðum er ekki passar við viðkomandi gerð. Ef nýja sniðmátið er skilgreint með ákveðnum reglum s.s. stærð mynda þá verða þær breytingar óafturkræfar. Notist með gát.", "file.delete.confirm": "Ætlarðu virkilega að eyða
{filename}?", + "file.focus.placeholder": "Settu brennipunkt", + "file.focus.reset": "Fjarlægðu brennipunkt", + "file.focus.title": "Fókus", "file.sort": "Breyta röðun", "files": "Skrár", + "files.delete.confirm.selected": "Do you really want to delete the selected files? This action cannot be undone.", "files.empty": "Engar skrár enn", + "filter": "Sigta", + + "form.discard": "Hunsa breytingar", + "form.discard.confirm": "Ætlarðu virkilega að hunsa alla breytingar?", + "form.locked": "Efnið er þér ekki aðgengilegt þar sem annar notandi er nú þegar að vinna í því", + "form.unsaved": "Þessar breytingar hafa ekki verið vistaðar", + "form.preview": "Skoða breytingar", + "form.preview.draft": "Skoða uppkast", + "hide": "Fela", "hour": "Klukkustund", + "hue": "Blær", "import": "Hlaða inn", "info": "Upplýsingar", "insert": "Setja inn", @@ -324,14 +426,14 @@ "installation.issues.mbstring": "MB String er hér bráðnauðsynleg", "installation.issues.media": "/media mappan er annaðhvort ekki til eða er ekki skrifanleg", "installation.issues.php": "Notaðu PHP 8+", - "installation.issues.server": "Kirby krefst Apache, Nginx eða Caddy", "installation.issues.sessions": "/site/sessions mappan er annaðhvort ekki til eða er ekki skrifanleg", - "language": "Tungumal", - "language.code": "Kóðasnið", + "language": "Tungumál", + "language.code": "Kóði", "language.convert": "Gera sjálfgefið", "language.convert.confirm": "

Ertu viss um að þú viljir breyta {name} í sjálfgefið (lesist aðal) tungumál? Þessu verður ekki viðsnúið.

Ef {name} hefur innihald sem ekki hefur verið þýtt, þá verða engir möguleikar til þrautarvara og hluti vefsins gæti birtst tómur.

", "language.create": "Bættu við nýju tungumáli", + "language.default": "Aðal tungumál", "language.delete.confirm": "Ertu nú viss um að þú viljir eyða {name} og öllum tilheyrandi þýðingum? Þetta verður ekki tekið til baka!", "language.deleted": "Tungumálinu hefur verið eytt", "language.direction": "Lesátt", @@ -340,7 +442,16 @@ "language.locale": "PHP locale strengur", "language.locale.warning": "Þú ert að nota sérsniðna locale uppsetningu. Vinsamlegast breyttu tungumálaskránni á slóðinni /site/languages", "language.name": "Nafn tungumáls", + "language.secondary": "Auka tungumál", + "language.settings": "Tungumálastillingar", "language.updated": "Tungumálið hefur verið uppfært", + "language.variables": "Tungumálabreytur", + "language.variables.empty": "Engar þýðingar enn", + + "language.variable.delete.confirm": "Ertu viss um að þú viljir nú fjarlægja breytuna fyrir {key}?", + "language.variable.key": "Lykill", + "language.variable.notFound": "Breytan fannst hreint ekki", + "language.variable.value": "Gildi", "languages": "Tungumál", "languages.default": "Aðal tungumál", @@ -349,15 +460,32 @@ "languages.secondary.empty": "Það eru engin auka tungumál skilgreind enn", "license": "Leyfi", + "license.activate": "Virkja þetta nú", + "license.activate.label": "Vinsamlegast virkjaðu leyfið þitt", + "license.activate.domain": "Leyfið þitt verður virkjað fyrir og tengt við {host}.", + "license.activate.local": "Þú ert að fara virkja leyfið þitt fyrir staðbundinn (e. local) vef: {host}. Ef það er meiningin að færa vefinn síðar út á netið þá vinsamlegast virkjaðu leyfið þar. Ef {host} er lénið sem þú vilt tengja leyfið við þá vinsamlegast haltu áfram.", + "license.activated": "Virkjað", "license.buy": "Kaupa leyfi", - "license.register": "Skrá Kirby", + "license.code": "Kóðasnið", + "license.code.help": "Þú fékkst leyfiskóðan sendan í tölvupósti eftir að þú borgaðir fyrir leyfið. Vinsamlegast afritaðu hann hingað.", + "license.code.label": "Vinsamlegast settu inn leyfiskóðan", + "license.status.active.info": "Felur í sér allar útgáfur þar til {date}", + "license.status.active.label": "Gilt skráningarleyfi", + "license.status.demo.info": "Þessi uppsetning er til prófunar.", + "license.status.demo.label": "Prófunarútgáfa", + "license.status.inactive.info": "Endurnýja skráningarleyfi fyrir uppfærslur á nýjum útgáfum", + "license.status.inactive.label": "Engar nýjar útgáfur", + "license.status.legacy.bubble": "Klár í að endurnýja skráningarleyfið?", + "license.status.legacy.info": "Skráningarleyfið þitt og sá kóði sem fylgir gildir ekkert fyrir þessa útgáfu", + "license.status.legacy.label": "Vinsamlegast endurnýjaðu skráningarleyfið þitt", + "license.status.missing.bubble": "Er allt tilbúið til að gefa vefinn út?", + "license.status.missing.info": "Ekkert gilt skráningarleyfi", + "license.status.missing.label": "Vinsamlegast virkjaðu leyfið þitt", + "license.status.unknown.info": "Staða leyfis fyrir hugbúnaðinn er óþekkt", + "license.status.unknown.label": "Unknown", "license.manage": "Sýslaðu með leyfin þín", - "license.register.help": "Þú fékkst sendan tölvupóst með leyfiskóðanum þegar þú keyptir leyfi. Vinsamlegast afritaðu hann og settu hann hingað til að skrá þig.", - "license.register.label": "Vinsamlegast settu inn leyfiskóðan", - "license.register.domain": "Leyfið þitt verður skráð á {host}.", - "license.register.local": "Nú ertu að fara skrá leyfið þitt á staðbundna lénið (e. local domain) {host}. Ef þetta vefsvæði verður fært út á vefinn, vinsamlegast skráðu það frekar þar þegar það hefur verið gefið þar út. Ef {host] er raunverulega lénið sem þú vilt skrá leyfir þitt á, endilega haltu þínu striki.", - "license.register.success": "Þakka þér fyrir að velja Kirby", - "license.unregistered": "Þetta er óskráð prufueintak af Kirby", + "license.purchased": "Verslað", + "license.success": "Þakka þér fyrir að velja Kirby", "license.unregistered.label": "Óskráð", "link": "Tengill", @@ -367,17 +495,21 @@ "lock.unsaved": "Óvistað breytingar", "lock.unsaved.empty": "Það eru öngvar óvistaðar breytingar", - "lock.isLocked": "Óvistaðar breytingar frá {email}", - "lock.file.isLocked": "{email} er að vinna í skránni og þú breytir henni ekki á meðan.", - "lock.page.isLocked": "{email} er að vinna í síðunni og þú breytir henni ekki á meðan.", + "lock.unsaved.files": "Óvistaðar skrár", + "lock.unsaved.pages": "Óvistaðar síður", + "lock.unsaved.users": "Óvistaðir notendareikningar", + "lock.isLocked": "Óvistaðar breytingar framkvæmdar af {email}", "lock.unlock": "Aflæsa", - "lock.isUnlocked": "Þær breytingar sem þú gerðir hafa verið yfirskrifaðar af öðrum notanda. Þú getur sótt þær breytingar og splæst þeim saman við þínar breytingar. Handvirkt.", + "lock.unlock.submit": "Aflæsa og yfirskrifa óvistaðar breytingar framkvæmdar af {email}", + "lock.isUnlocked": "Læsing fjalægð af öðrum notanda", "login": "Innskrá", "login.code.label.login": "Innskráningarkóði", "login.code.label.password-reset": "Kóði fyrir endurstillingu lykilorðs", "login.code.placeholder.email": "000 000", + "login.code.placeholder.totp": "000000", "login.code.text.email": "Ef netfangið þitt er skráð þá bíður þín nýr tölvupóstur.", + "login.code.text.totp": "Settu inn kóðan frá auðkenningar appinu.", "login.email.login.body": "Já halló {user.nameOrEmail},\n\nNýlega baðstu um innskráningarkóða fyrir bakendan á {site}.\nEftirfarandi kóði er virkur í {timeout} mínútur:\n\n{code}\n\nEf þú óskaðir ekki eftir þessu þá hunsaðu þennan tölvupóst eða talaðu við vefstjóran ef þú vilt fræðast nánar.\nAf öryggisástæðum vinsamlegast áframsendu þennan tölvupóst ALLS EKKI.", "login.email.login.subject": "Innskráningarkóðinn þinn", "login.email.password-reset.body": "Nei halló {user.nameOrEmail},\n\nNýverið baðstu um að lykilorði þínu væri endurstillt fyrir bakendan á {site}. \nEftirfarandi kóði er virkur í {timeout} mínútur:\n\n{code}\n\nEf þú óskaðir ekki eftir þessu þá hunsaðu þennan tölvupóst eða talaðu við vefstjóran ef þú vilt fræðast nánar.\nAf öryggisástæðum vinsamlegast áframsendu þennan tölvupóst ALLS EKKI.", @@ -388,9 +520,24 @@ "login.toggleText.code.email-password": "Innskrá með lykilorði", "login.toggleText.password-reset.email": "Mannstu ekki lykilorðið?", "login.toggleText.password-reset.email-password": "← Aftur í innskráningu", + "login.totp.enable.option": "Setja upp einnota kóða.", + "login.totp.enable.intro": "Auðkenningaröpp framleiða einnota sex stafa kóða sem notaður er seinni þáttur þegar þú skráir þig inn.", + "login.totp.enable.qr.label": "Skannaðu QR kóðan.", + "login.totp.enable.qr.help": "Virkar ekki að skanna? Bættu við uppsetningarkóðanum {secret} fyrir auðkenningarappið.", + "login.totp.enable.confirm.headline": "2. Staðfestu með auðkenningar kóða", + "login.totp.enable.confirm.text": "Appið þitt framleiðir nýjan einnota kóða á 30 sekúndna fresti. Setti inn núverandi kóða til að ljúka uppsetningu.", + "login.totp.enable.confirm.label": "Núverandi kóði", + "login.totp.enable.confirm.help": "Eftir uppsetninguna þá munum við biðja um einnota kóða í hvert skipti sem þú skráir þig inn.", + "login.totp.enable.success": "Einnota skráningarkóði virkjaður", + "login.totp.disable.option": "Afvirkjaðir einnota kóðar.", + "login.totp.disable.label": "Sláðu inn lykilorðið þitt til að afvirkja einnota kóða.", + "login.totp.disable.help": "Framveigis þá mun nýr seinniþáttar kóði verða sendur í tölvupósti til þín þegar þú skráir þig inn. Þú munt alltaf geta sett upp einnota kóðana aftur síðar.", + "login.totp.disable.admin": "

Þetta mun afvirkja einnota kóða fyrir {user}.

Framvegis mun nýr seinniþáttarkóði verða sendur í tölvupósti þegar notendur skrá sig inn. {user} getur sett upp einnota kóðana eftir næstu innskráningu.

", + "login.totp.disable.success": "Einnota skráningarkóði afvirkjaður", "logout": "Útskrá", + "merge": "Splæsa", "menu": "Valmynd", "meridiem": "AM/PM", "mime": "Miðilsgerð", @@ -411,21 +558,26 @@ "months.september": "September", "more": "Meira", - "name": "Nafn tungumáls", + "move": "Færa", + "name": "Nafn", "next": "Næst", + "night": "Nótt", "no": "nei", "off": "Af", "on": "Á", "open": "Opna", "open.newWindow": "Opna í nýjum glugga", + "option": "Kostur", "options": "Valmöguleikar", "options.none": "Engir valmöguleikar", + "options.all": "Sýna alla {count} möguleika", "orientation": "Snúningur", "orientation.landscape": "Langsnið", "orientation.portrait": "Skammsnið", "orientation.square": "Ferningur", + "page": "Síða", "page.blueprint": "Þessi síða hefur ekki skipan (e. blueprint) ennþá. Þú mátt skilgreina skipanina í /site/blueprints/{template}.yml", "page.changeSlug": "Breyta vefslóð", "page.changeSlug.fromTitle": "Slóð af titli", @@ -433,41 +585,51 @@ "page.changeStatus.position": "Veldu ákjósanlega röðun", "page.changeStatus.select": "Veldu nýja stöðu", "page.changeTemplate": "Breyta sniðmáti", + "page.changeTemplate.notice": "Að breyta sniðmáti síðunnar mun fjarlægja efni fyrir svið er ekki passa við gerð nýja sniðmátsins. Notist með gát.", + "page.create": "Stofna", "page.delete.confirm": "Viltu virkilega farga {title}?", "page.delete.confirm.subpages": "Þessi síða hefur undirsíður.
Þeim mun verða fargað líka.", "page.delete.confirm.title": "Skráðu síðutitilinn til staðfestingar", - "page.draft.create": "Stofna uppkast", "page.duplicate.appendix": "Afrita", "page.duplicate.files": "Afrita skrár", "page.duplicate.pages": "Afrita síður", + "page.move": "Færa síðu", "page.sort": "Breyta röðun", "page.status": "Staða", "page.status.draft": "Uppkast", "page.status.draft.description": "Þessi síða er uppkast og er aðeins sýnileg vefstjórum eða gegnum laumu tengil.", "page.status.listed": "Útgefin og listuð", "page.status.listed.description": "Síðan er útgefin og listuð.", - "page.status.unlisted": "Útgefin", + "page.status.unlisted": "Útgefin en ólistuð", "page.status.unlisted.description": "Síðan er útgefin en þó ólistuð.", "pages": "Síður", + "pages.delete.confirm.selected": "Do you really want to delete the selected pages? This action cannot be undone.", "pages.empty": "Engar síður enn", "pages.status.draft": "Uppköst", "pages.status.listed": "Útgefnar og listaðar", - "pages.status.unlisted": "Útgefin", + "pages.status.unlisted": "Útgefnar en ólistaðar", "pagination.page": "Síða", "password": "Lykilorð", "paste": "Líma", "paste.after": "Líma eftir", + "paste.success": "{count} límt!", "pixel": "Punkta", "plugin": "Viðbót", "plugins": "Viðbætur", "prev": "Fyrri", "preview": "Forskoða", + + "publish": "Útgefa", + "published": "Útgefnar og listaðar", + "remove": "Fjarlægja", "rename": "Endurnefna", + "renew": "Endurnýja", "replace": "Setja í stað", + "replace.with": "Endursetja með", "retry": "Reyndu aftur", "revert": "Taka upp fyrri siði", "revert.confirm": "Viltu virkilega eyða öllum óvistuðum breytingum?", @@ -482,11 +644,14 @@ "role.nobody.title": "Enginn", "save": "Vista", + "saved": "Vistað", "search": "Leita", + "searching": "Leita ..", "search.min": "Lágmark {min} stafir til að leita", - "search.all": "Sýna allt", + "search.all": "Sýna allar {count} niðurstöður.", "search.results.none": "Engar niðurstöður", + "section.invalid": "Þetta svæði er bara ógillt sem stendur.", "section.required": "Þetta svæði er nauðsynlegt", "security": "Öryggi", @@ -498,17 +663,26 @@ "size": "Stærð", "slug": "Slóðar viðskeyti", "sort": "Raða", + "sort.drag": "Dragðu til að raða", + "split": "Skipta", "stats.empty": "Engar skýrslur", + "status": "Staða", + + "system.info.copy": "Copy info", + "system.info.copied": "System info copied", "system.issues.content": "Efnismappan virðist vera berskjölduð", "system.issues.eol.kirby": "Uppsett Kirby eintak þitt hefur runnið sitt skeið á enda og mun ekki verða uppfært framar", "system.issues.eol.plugin": "Uppsett eintak þitt af viðbótinni { plugin } hefur runnið sitt skeið á enda og mun ekki verða uppfærð framar", + "system.issues.eol.php": "Núverandi PHP útgáfa {release} hefur runnið sitt skeið og mun ekki verða uppfærð með öryggisuppfærslum.", "system.issues.debug": "Aflúsun ætti alltaf að vera óvirk í útgefnum vef", "system.issues.git": ".git mappan virðist vera berskjölduð", "system.issues.https": "Við mælum harðlega með því að þú notir HTTPS fyrir öll þín vefsvæði", "system.issues.kirby": "Kirby mappan virðist vera berskjölduð", + "system.issues.local": "The site is running locally with relaxed security checks", "system.issues.site": "Mappa vefsvæðisins virðist vera berskjölduð", - "system.issues.vulnerability.kirby": "Uppsetningin þín gæti verið berskjölduð gagnvart eftirfarandi veikleika: ({ severity } veikleiki): { description }", + "system.issues.vue.compiler": "The Vue template compiler is enabled", + "system.issues.vulnerability.kirby": "Uppsetningin þín gæti verið berskjölduð gagnvart eftirfarandi veikleika: ({ severity } veikleikinn): { description }", "system.issues.vulnerability.plugin": "Uppsetningin þín gæti verið berskjölduð gagnvart eftirfarandi veikleika í viðbótinni { plugin }: ({ severity } veikleikinn): { description }", "system.updateStatus": "Uppfærslustaða", "system.updateStatus.error": "Gat því miður ekki athugað með uppfærslur", @@ -520,10 +694,19 @@ "system.updateStatus.update": "Ókeypis uppfærsla { version } fáanleg", "system.updateStatus.upgrade": "Uppfærsla fyrir { version } fáanleg", - "title": "Titill", + "tel": "Sími", + "tel.placeholder": "+3548561234", "template": "Sniðmát", + + "theme": "Þema", + "theme.light": "Ljósin kveikt", + "theme.dark": "Ljósin slökkt", + "theme.automatic": "Nota kerfisstillingu", + + "title": "Titill", "today": "Núna", + "toolbar.button.clear": "Hreinsa snið", "toolbar.button.code": "Kóðasnið", "toolbar.button.bold": "Feitletrun", "toolbar.button.email": "Netfang", @@ -541,6 +724,8 @@ "toolbar.button.link": "Tengill", "toolbar.button.paragraph": "Efnisgrein", "toolbar.button.strike": "Gegnumstrika", + "toolbar.button.sub": "Hnéletur", + "toolbar.button.sup": "Höfuðletur", "toolbar.button.ol": "Raðaður listi", "toolbar.button.underline": "Undirstrika", "toolbar.button.ul": "Áherslumerktur listi", @@ -550,6 +735,8 @@ "translation.name": "Íslenska", "translation.locale": "is_IS", + "type": "Gerð", + "upload": "Hlaða inn", "upload.error.cantMove": "Innhlöðnu skránni var ekki haggað", "upload.error.cantWrite": "Það mistókst að skrifa skránna í geymslu", @@ -574,6 +761,7 @@ "user.changeLanguage": "Breyta tungumáli", "user.changeName": "Endurnefna þennan notanda", "user.changePassword": "Breyta lykilorð", + "user.changePassword.current": "Your current password", "user.changePassword.new": "Nýtt lykilorð", "user.changePassword.new.confirm": "Staðfestu nýtt lykilorð…", "user.changeRole": "Breyta hlutverki", @@ -585,14 +773,17 @@ "users": "Notendur", "version": "Útgáfa", + "version.changes": "Breytt útgáfa", + "version.compare": "Bera saman útgáfur", "version.current": "Núverandi útgáfa", "version.latest": "Nýjasta útgáfa", "versionInformation": "Útgáfuupplýsingar", + "view": "Sýn", "view.account": "Þínar stillingar", - "view.installation": "Uppsettning", + "view.installation": "Uppsetning", "view.languages": "Tungumál", - "view.resetPassword": "Endurheimta lykilorð takk", + "view.resetPassword": "Endurstilla lykilorð", "view.site": "Vefsvæðið", "view.system": "Kerfi", "view.users": "Notendur", diff --git a/kirby/i18n/translations/it.json b/kirby/i18n/translations/it.json index dfc9529..9e1c235 100644 --- a/kirby/i18n/translations/it.json +++ b/kirby/i18n/translations/it.json @@ -3,19 +3,28 @@ "account.delete": "Elimina l'account", "account.delete.confirm": "Vuoi davvero eliminare il tuo account? Verrai disconnesso immediatamente. Il tuo account non potrà essere recuperato.", + "activate": "Attiva", "add": "Aggiungi", + "alpha": "Alpha", "author": "Autore", "avatar": "Immagine del profilo", "back": "Indietro", "cancel": "Annulla", "change": "Cambia", "close": "Chiudi", + "changes": "Changes", "confirm": "OK", "collapse": "Comprimi", "collapse.all": "Comprimi tutto", + "color": "Color", + "coordinates": "Coordinates", "copy": "Copia", "copy.all": "Copia tutto", + "copy.success": "{count} copied!", + "copy.success.multiple": "{count} copied!", + "copy.url": "Copy URL", "create": "Crea", + "custom": "Custom", "date": "Data", "date.select": "Scegli una data", @@ -34,13 +43,20 @@ "delete": "Elimina", "delete.all": "Elimina tutti", + "dialog.fields.empty": "This dialog has no fields", "dialog.files.empty": "Nessun file selezionabile", "dialog.pages.empty": "Nessuna pagina selezionabile", + "dialog.text.empty": "This dialog does not define any text", "dialog.users.empty": "Nessuno user selezionabile", "dimensions": "Dimensioni", + "disable": "Disattiva", "disabled": "Disabilitato", "discard": "Abbandona", + + "drawer.fields.empty": "This drawer has no fields", + + "domain": "Dominio", "download": "Scarica", "duplicate": "Duplica", @@ -49,11 +65,13 @@ "email": "Email", "email.placeholder": "mail@esempio.com", + "enter": "Enter", "entries": "Voci", "entry": "Voce", "environment": "Ambiente", + "error": "Error", "error.access.code": "Codice non valido", "error.access.login": "Login invalido", "error.access.panel": "Non ti è permesso accedere al pannello", @@ -74,13 +92,31 @@ "error.cache.type.invalid": "Tipo di cache \"{type}\" non valido", + "error.content.lock.delete": "The version is locked and cannot be deleted", + "error.content.lock.move": "The source version is locked and cannot be moved", + "error.content.lock.publish": "This version is already published", + "error.content.lock.replace": "The version is locked and cannot be replaced", + "error.content.lock.update": "The version is locked and cannot be updated", + + "error.entries.max.plural": "You must not add more than {max} entries", + "error.entries.max.singular": "You must not add more than one entry", + "error.entries.min.plural": "You must add at least {min} entries", + "error.entries.min.singular": "You must add at least one entry", + "error.entries.supports": "\"{type}\" field type is not supported for the entries field", + "error.entries.validation": "C'è un errore nel campo \"{field}\" nella riga {index}", + "error.email.preset.notFound": "Non è stato possibile trovare il preset email \"{name}\"", "error.field.converter.invalid": "Convertitore \"{converter}\" non valido", + "error.field.link.options": "Invalid options: {options}", "error.field.type.missing": "Campo \"{ name }\": il tipo di campo \"{ type }\" non esiste", "error.file.changeName.empty": "Il nome non dev'essere vuoto", "error.file.changeName.permission": "Non ti è permesso modificare il nome di \"{filename}\"", + "error.file.changeTemplate.invalid": "The template for the file \"{id}\" cannot be changed to \"{template}\" (valid: \"{blueprints}\")", + "error.file.changeTemplate.permission": "You are not allowed to change the template for the file \"{id}\"", + + "error.file.delete.multiple": "Not all files could be deleted. Try each remaining file individually to see the specific error that prevents deletion.", "error.file.duplicate": "Un file con il nome \"{filename}\" esiste già", "error.file.extension.forbidden": "L'estensione \"{extension}\" non è consentita", "error.file.extension.invalid": "Estensione non valida: {extension}", @@ -95,33 +131,43 @@ "error.file.minheight": "L'immagine dev'essere alta almeno {height} pixel", "error.file.minsize": "Il file è troppo leggero", "error.file.minwidth": "L'immagine dev'essere larga almeno {width} pixel", + "error.file.name.unique": "The filename must be unique", "error.file.name.missing": "Il nome del file non può essere vuoto", - "error.file.notFound": "Il file non è stato trovato", + "error.file.notFound": "Il file non \u00e8 stato trovato", "error.file.orientation": "L'imaggine dev'essere orientata in \"{orientation}\"", + "error.file.sort.permission": "You are not allowed to change the sorting of \"{filename}\"", "error.file.type.forbidden": "Non ti è permesso caricare file {type}", "error.file.type.invalid": "Tipo di file non valido: {type}", - "error.file.undefined": "Il file non è stato trovato", + "error.file.undefined": "Il file non \u00e8 stato trovato", "error.form.incomplete": "Correggi tutti gli errori nel form...", "error.form.notSaved": "Non è stato possibile salvare il form", "error.language.code": "Inserisci un codice valido per la lingua", + "error.language.create.permission": "You are not allowed to create a language", + "error.language.delete.permission": "You are not allowed to delete the language", "error.language.duplicate": "La lingua esiste già", "error.language.name": "Inserisci un nome valido per la lingua", "error.language.notFound": "La lingua non è stata trovata", + "error.language.update.permission": "You are not allowed to update the language", "error.layout.validation.block": "C'è un errore sul campo \"{field}\" nel blocco {blockIndex} che utilizza il tipo di blocco \"{fieldset}\" nel layout {layoutIndex}", "error.layout.validation.settings": "C'è un errore nelle impostazioni del layout {index}", - "error.license.format": "Inserisci un codice di licenza valido", + "error.license.domain": "Il dominio per la licenza è assente", "error.license.email": "Inserisci un indirizzo email valido", + "error.license.format": "Per favore inserisci un codice di licenza valido", "error.license.verification": "Non è stato possibile verificare la licenza", + "error.login.totp.confirm.invalid": "Codice non valido", + "error.login.totp.confirm.missing": "Inserisci il codice attuale", + "error.object.validation": "C'è un errore nel campo \"{label}\":\n{message}", "error.offline": "Il pannello di controllo è attualmente offline", "error.page.changeSlug.permission": "Non ti è permesso cambiare l'URL di \"{slug}\"", + "error.page.changeSlug.reserved": "The path of top-level pages must not start with \"{path}\"", "error.page.changeStatus.incomplete": "La pagina contiene errori e non può essere pubblicata", "error.page.changeStatus.permission": "Lo stato di questa pagina non può essere cambiato", "error.page.changeStatus.toDraft.invalid": "La pagina \"{slug}\" non può essere convertita in bozza", @@ -133,17 +179,25 @@ "error.page.delete": "La pagina \"{slug}\" non può essere eliminata", "error.page.delete.confirm": "Inserisci il titolo della pagina per confermare", "error.page.delete.hasChildren": "La pagina ha sottopagine e non può essere eliminata", + "error.page.delete.multiple": "Not all pages could be deleted. Try each remaining page individually to see the specific error that prevents deletion.", "error.page.delete.permission": "Non ti è permesso eliminare \"{slug}\"", "error.page.draft.duplicate": "Una bozza di pagina con l'URL \"{slug}\" esiste già", "error.page.duplicate": "Una pagina con l'URL \"{slug}\" esiste già", "error.page.duplicate.permission": "Non ti è permesso duplicare \"{slug}\"", + "error.page.move.ancestor": "The page cannot be moved into itself", + "error.page.move.directory": "The page directory cannot be moved", + "error.page.move.duplicate": "A sub page with the URL appendix \"{slug}\" already exists", + "error.page.move.noSections": "The page \"{parent}\" cannot be a parent of any page because it lacks any pages sections in its blueprint", + "error.page.move.notFound": "The moved page could not be found", + "error.page.move.permission": "You are not allowed to move \"{slug}\"", + "error.page.move.template": "The \"{template}\" template is not accepted as a subpage of \"{parent}\"", "error.page.notFound": "La pagina \"{slug}\" non è stata trovata", "error.page.num.invalid": "Inserisci un numero di ordinamento valido. I numeri non devono essere negativi", "error.page.slug.invalid": "Per favore inserisci un suffisso valido per l'URL", "error.page.slug.maxlength": "Lo \"slug\" dev'essere più corto di \"{length}\" caratteri", "error.page.sort.permission": "La pagina \"{slug}\" non può essere ordinata", "error.page.status.invalid": "Imposta uno stato valido per la pagina", - "error.page.undefined": "La pagina non è stata trovata", + "error.page.undefined": "La pagina non \u00e8 stata trovata", "error.page.update.permission": "Non ti è permesso modificare \"{slug}\"", "error.section.files.max.plural": "Non puoi aggiungere più di {max} file alla sezione \"{section}\"", @@ -163,6 +217,8 @@ "error.site.changeTitle.permission": "Non ti è permesso modificare il titolo del sito", "error.site.update.permission": "Non ti è permesso modificare i contenuti globali del sito", + "error.structure.validation": "C'è un errore nel campo \"{field}\" nella riga {index}", + "error.template.default.notFound": "Il template \"default\" non esiste", "error.unexpected": "Si è verificato un errore inaspettato! Abilita la modalità \"debug\" per ulteriori informazioni: https://getkirby.com/docs/reference/system/options/debug", @@ -175,14 +231,14 @@ "error.user.changeRole.permission": "Non ti è permesso modificare il ruolo dell'utente \"{name}\"", "error.user.changeRole.toAdmin": "Non ti è permesso assegnare il ruolo di amministratore ad altri utenti", "error.user.create.permission": "Non ti è permesso creare questo utente", - "error.user.delete": "L'utente non può essere eliminato", + "error.user.delete": "L'utente non pu\u00f2 essere eliminato", "error.user.delete.lastAdmin": "L'ultimo amministratore non può essere eliminato", "error.user.delete.lastUser": "L'ultimo utente non può essere eliminato", - "error.user.delete.permission": "Non ti è permesso eliminare questo utente ", + "error.user.delete.permission": "Non ti \u00e8 permesso eliminare questo utente ", "error.user.duplicate": "Esiste già un utente con l'indirizzo email \"{email}\"", "error.user.email.invalid": "Inserisci un indirizzo email valido", "error.user.language.invalid": "Inserisci una lingua valida", - "error.user.notFound": "L'utente non è stato trovato", + "error.user.notFound": "L'utente non \u00e8 stato trovato", "error.user.password.excessive": "Please enter a valid password. Passwords must not be longer than 1000 characters.", "error.user.password.invalid": "Per favore inserisci una password valida. Le password devono essere lunghe almeno 8 caratteri", "error.user.password.notSame": "Le password non corrispondono", @@ -195,8 +251,10 @@ "error.validation.accepted": "Per favore conferma", "error.validation.alpha": "Puoi inserire solo caratteri tra a-z", "error.validation.alphanum": "Puoi inserire solo caratteri tra a-z e numeri 0-9", + "error.validation.anchor": "Please enter a correct link anchor", "error.validation.between": "Inserisci un valore tra \"{min}\" e \"{max}\"", "error.validation.boolean": "Per favore conferma o nega", + "error.validation.color": "Please enter a valid color in the {format} format", "error.validation.contains": "Inserisci un valore che contiene \"{needle}\"", "error.validation.date": "Inserisci una data valida", "error.validation.date.after": "Inserisci una data dopo il {date}", @@ -211,6 +269,7 @@ "error.validation.integer": "Inserisci un numero intero", "error.validation.ip": "Inserisci un indirizzo IP valido", "error.validation.less": "Inserisci un valore inferiore a {max}", + "error.validation.linkType": "The link type is not allowed", "error.validation.match": "Il valore non corrisponde al pattern previsto", "error.validation.max": "Inserisci un valore inferiore o uguale a {max}", "error.validation.maxlength": "Inserisci un testo più corto. (max. {max} caratteri)", @@ -227,15 +286,18 @@ "error.validation.same": "Inserisci \"{other}\"", "error.validation.size": "La dimensione del valore dev'essere \"{size}\"", "error.validation.startswith": "Il valore deve iniziare con \"{start}\"", + "error.validation.tel": "Please enter an unformatted phone number", "error.validation.time": "Inserisci un orario valido", "error.validation.time.after": "Inserisci un orario dopo le {time}", "error.validation.time.before": "Inserisci un orario prima delle {time}", "error.validation.time.between": "Inserisci un orario tra le {min} e le {max}", + "error.validation.uuid": "Please enter a valid UUID", "error.validation.url": "Inserisci un URL valido", "expand": "Espandi", "expand.all": "Espandi tutto", + "field.invalid": "The field is invalid", "field.required": "Il campo è obbligatorio", "field.blocks.changeType": "Cambia tipo", "field.blocks.code.name": "Codice", @@ -245,8 +307,9 @@ "field.blocks.delete.confirm.all": "Vuoi veramente eliminare tutti i blocchi? ", "field.blocks.delete.confirm.selected": "Vuoi veramente eliminare i blocchi selezionati?", "field.blocks.empty": "Nessun blocco inserito", + "field.blocks.fieldsets.empty": "No fieldsets yet", "field.blocks.fieldsets.label": "Seleziona il tipo di blocco …", - "field.blocks.fieldsets.paste": "Premi {{ shortcut }} per incollare/importare i blocchi dagli appunti", + "field.blocks.fieldsets.paste": "Press {{ shortcut }} to import layouts/blocks from your clipboard Only those allowed in the current field will get inserted.", "field.blocks.gallery.name": "Galleria", "field.blocks.gallery.images.empty": "Nessuna immagine inserita", "field.blocks.gallery.images.label": "Immagini", @@ -254,11 +317,16 @@ "field.blocks.heading.name": "Titolo", "field.blocks.heading.text": "Testo", "field.blocks.heading.placeholder": "Titolo …", + "field.blocks.figure.back.plain": "Plain", + "field.blocks.figure.back.pattern.light": "Pattern (light)", + "field.blocks.figure.back.pattern.dark": "Pattern (dark)", "field.blocks.image.alt": "Testo alternativo", "field.blocks.image.caption": "Didascalia", "field.blocks.image.crop": "Ritaglio", "field.blocks.image.link": "Link", "field.blocks.image.location": "Posizione", + "field.blocks.image.location.internal": "This website", + "field.blocks.image.location.external": "External source", "field.blocks.image.name": "Immagine", "field.blocks.image.placeholder": "Seleziona un'immagine", "field.blocks.image.ratio": "Rapporto", @@ -275,38 +343,72 @@ "field.blocks.quote.citation.placeholder": "di …", "field.blocks.text.name": "Testo", "field.blocks.text.placeholder": "Testo …", + "field.blocks.video.autoplay": "Autoplay", "field.blocks.video.caption": "Didascalia", + "field.blocks.video.controls": "Controls", + "field.blocks.video.location": "Posizione", + "field.blocks.video.loop": "Loop", + "field.blocks.video.muted": "Muted", "field.blocks.video.name": "Video", "field.blocks.video.placeholder": "Inserisci un URL di un video", + "field.blocks.video.poster": "Poster", + "field.blocks.video.preload": "Preload", "field.blocks.video.url.label": "URL Video", "field.blocks.video.url.placeholder": "https://youtube.com/?v=", - "field.files.empty": "Nessun file selezionato", + "field.entries.delete.confirm.all": "Vuoi davvero cancellare tutte le voci?", + "field.entries.empty": "Non ci sono ancora elementi.", + "field.files.empty": "Nessun file selezionato", + "field.files.empty.single": "No file selected yet", + + "field.layout.change": "Change layout", "field.layout.delete": "Elimina layout", "field.layout.delete.confirm": "Vuoi veramente eliminare questo layout?", + "field.layout.delete.confirm.all": "Do you really want to delete all layouts?", "field.layout.empty": "Nessuna riga inserita", "field.layout.select": "Scegli un layout", "field.object.empty": "Ancora nessuna informazione", "field.pages.empty": "Nessuna pagina selezionata", + "field.pages.empty.single": "No page selected yet", "field.structure.delete.confirm": "Vuoi veramente eliminare questo elemento?", "field.structure.delete.confirm.all": "Vuoi davvero cancellare tutte le voci?", "field.structure.empty": "Non ci sono ancora elementi.", "field.users.empty": "Nessun utente selezionato", + "field.users.empty.single": "No user selected yet", + "fields.empty": "No fields yet", + + "file": "File", "file.blueprint": "Questo file non ha ancora un blueprint. Puoi definire la sua configurazione in /site/blueprints/files/{blueprint}.yml", + "file.changeTemplate": "Cambia template", + "file.changeTemplate.notice": "Changing the file's template will remove content for fields that don't match in type. If the new template defines certain rules, e.g. image dimensions, those will also be applied irreversibly. Use with caution.", "file.delete.confirm": "Sei sicuro di voler eliminare questo file?", + "file.focus.placeholder": "Set focal point", + "file.focus.reset": "Remove focal point", + "file.focus.title": "Focus", "file.sort": "Cambia posizione", "files": "Files", + "files.delete.confirm.selected": "Do you really want to delete the selected files? This action cannot be undone.", "files.empty": "Nessun file caricato", + "filter": "Filter", + + "form.discard": "Discard changes", + "form.discard.confirm": "Do you really want to discard all your changes?", + "form.locked": "This content is disabled for you as it is currently edited by another user", + "form.unsaved": "The current changes have not yet been saved", + "form.preview": "Preview changes", + "form.preview.draft": "Preview draft", + "hide": "Nascondi", "hour": "Ora", + "hue": "Hue", "import": "Importa", "info": "Info", "insert": "Inserisci", @@ -324,7 +426,6 @@ "installation.issues.mbstring": "È necessaria l'estensione MB String", "installation.issues.media": "La cartella /media non esiste o non dispone dei permessi di scrittura", "installation.issues.php": "Assicurati di utilizzare PHP 8+", - "installation.issues.server": "Kirby necessita di Apache, Nginx o Caddy", "installation.issues.sessions": "La cartella /site/sessionsnon esiste o non dispone dei permessi di scrittura", "language": "Lingua", @@ -332,6 +433,7 @@ "language.convert": "Imposta come predefinito", "language.convert.confirm": "

Sei sicuro di voler convertire {name} nella lingua predefinita? Questa operazione non può essere annullata.

Se {name} non contiene tutte le traduzioni, non ci sarà più una versione alternativa valida e parti del sito potrebbero rimanere vuote.

", "language.create": "Aggiungi una nuova lingua", + "language.default": "Lingua di default", "language.delete.confirm": "Sei sicuro di voler eliminare la lingua {name} con tutte le traduzioni? Non sarà possibile annullare!", "language.deleted": "La lingua è stata eliminata", "language.direction": "Direzione di lettura", @@ -340,7 +442,16 @@ "language.locale": "Stringa \"PHP locale\"", "language.locale.warning": "Stai usando una impostazione personalizzata per il locale. Modificalo nel file della lingua situato in /site/languages", "language.name": "Nome", + "language.secondary": "Secondary language", + "language.settings": "Language settings", "language.updated": "La lingua è stata aggiornata", + "language.variables": "Language variables", + "language.variables.empty": "No translations yet", + + "language.variable.delete.confirm": "Do you really want to delete the variable for {key}?", + "language.variable.key": "Key", + "language.variable.notFound": "The variable could not be found", + "language.variable.value": "Value", "languages": "Lingue", "languages.default": "Lingua di default", @@ -349,15 +460,32 @@ "languages.secondary.empty": "Non ci sono lingue secondarie impostate", "license": "Licenza di Kirby", + "license.activate": "Attiva ora", + "license.activate.label": "Attiva la tua licenza ora", + "license.activate.domain": "La tua licenza sarà attivata per {host}.", + "license.activate.local": "Stai per attivare la licenza per il dominio locale {host}. Se questo sito verrà rilasciato su un dominio pubblico, ti preghiamo di attivarla lì. Se {host} è il dominio per il quale desideri ottenere la licenza di Kirby, procedi pure.", + "license.activated": "Attivata", "license.buy": "Acquista una licenza", - "license.register": "Registra", + "license.code": "Codice", + "license.code.help": "Hai ricevuto il codice di licenza tramite email dopo l'acquisto. Per favore inseriscilo per registrare Kirby.", + "license.code.label": "Inserisci il codice di licenza", + "license.status.active.info": "Comprende nuove versioni major entro il {date}", + "license.status.active.label": "Licenza valida", + "license.status.demo.info": "Questa è un'installazione demo", + "license.status.demo.label": "Demo", + "license.status.inactive.info": "Rinnova la licenza per aggiornare a nuove versioni major", + "license.status.inactive.label": "Nessuna nuova versione major", + "license.status.legacy.bubble": "Pronto per rinnovare la licenza?", + "license.status.legacy.info": "La tua licenza non include questa versione", + "license.status.legacy.label": "Per favore rinnova la tua licenza", + "license.status.missing.bubble": "Pronto a lanciare il tuo sito?", + "license.status.missing.info": "Nessuna licenza valida", + "license.status.missing.label": "Attiva la tua licenza ora", + "license.status.unknown.info": "The license status is unknown", + "license.status.unknown.label": "Unknown", "license.manage": "Gestisci le tue licenze", - "license.register.help": "Hai ricevuto il codice di licenza tramite email dopo l'acquisto. Per favore inseriscilo per registrare Kirby.", - "license.register.label": "Inserisci il codice di licenza", - "license.register.domain": "La tua licenza verrà registrata su {host}.", - "license.register.local": "Stai per registrare la licenza per il dominio locale {host}. Se questo sito verrà rilasciato su un dominio pubblico, ti preghiamo di registrarlo lì. Se {host} è il dominio per il quale desideri ottenere la licenza di Kirby, procedi pure.", - "license.register.success": "Ti ringraziamo per aver supportato Kirby", - "license.unregistered": "Questa è una versione demo di Kirby non registrata", + "license.purchased": "Acquistata", + "license.success": "Ti ringraziamo per aver supportato Kirby", "license.unregistered.label": "Non registrato", "link": "Link", @@ -367,17 +495,21 @@ "lock.unsaved": "Modifiche non salvate", "lock.unsaved.empty": "Non ci sono altre modifiche non salvate", - "lock.isLocked": "Modifiche non salvate di {email}", - "lock.file.isLocked": "Il file viene attualmente modificato da {email} e non può essere cambiato.", - "lock.page.isLocked": "la pagina viene attualmente modificata da {email} e non può essere cambiata.", + "lock.unsaved.files": "Unsaved files", + "lock.unsaved.pages": "Unsaved pages", + "lock.unsaved.users": "Unsaved accounts", + "lock.isLocked": "Unsaved changes by {email}", "lock.unlock": "Sblocca", - "lock.isUnlocked": "Un altro utente ha sovrascritto le tue modifiche non salvate. Puoi scaricarle per recuperarle e quindi incorporarle manualmente. ", + "lock.unlock.submit": "Unlock and overwrite unsaved changes by {email}", + "lock.isUnlocked": "Was unlocked by another user", "login": "Accedi", "login.code.label.login": "Codice di accesso", "login.code.label.password-reset": "Codice per reimpostare la password", "login.code.placeholder.email": "000 000", + "login.code.placeholder.totp": "000000", "login.code.text.email": "Qualora il tuo indirizzo email fosse registrato, il codice richiesto è stato inviato tramite email.", + "login.code.text.totp": "Inserisci il codice monouso dalla tua app di autenticazione.", "login.email.login.body": "Ciao {user.nameOrEmail},\n\nHai recentemente richiesto un codice di login per il pannello di controllo di {site}.\nIl seguente codice di login sarà valido per {timeout} minuti:\n\n{code}\n\nSe non hai richiesto un codice di login, per favore ignora questa mail o contatta il tuo amministratore in caso di domande.\nPer sicurezza, per favore NON inoltrare questa email.", "login.email.login.subject": "Il tuo codice di accesso", "login.email.password-reset.body": "Ciao {user.nameOrEmail},\n\nHai recentemente richiesto di resettare la password per il pannello di controllo di {site}.\nIl seguente codice di reset della password sarà valido per {timeout} minuti:\n\n{code}\n\nSe non hai richiesto di resettare la password per favore ignora questa email o contatta il tuo amministratore in caso di domande.\nPer sicurezza, per favore NON inoltrare questa email.", @@ -388,9 +520,24 @@ "login.toggleText.code.email-password": "Accedi con la password", "login.toggleText.password-reset.email": "Hai dimenticato la password?", "login.toggleText.password-reset.email-password": "← Torna al login", + "login.totp.enable.option": "Configura i codici monouso", + "login.totp.enable.intro": "Le app di autenticazione generano codici monouso che puoi usare come secondo fattore quando accedi al tuo account.", + "login.totp.enable.qr.label": "1. Scansiona il codice QR", + "login.totp.enable.qr.help": "Impossibile eseguire la scansione? Aggiungi manualmente la chiave di configurazione {secret} alla tua app di autenticazione.", + "login.totp.enable.confirm.headline": "2. Conferma con il codice generato", + "login.totp.enable.confirm.text": "La tua app genera un nuovo codice monouso ogni 30 secondi. Inserisci il codice attuale per completare la configurazione:", + "login.totp.enable.confirm.label": "Codice attuale", + "login.totp.enable.confirm.help": "Dopo la configurazione, ti chiederemo un codice monouso ogni volta che effettuerai l'accesso. ", + "login.totp.enable.success": "Codici monouso attivati", + "login.totp.disable.option": "Disattiva i codici monouso", + "login.totp.disable.label": "Inserisci la tua password per disattivare i codici monouso", + "login.totp.disable.help": "In futuro, un secondo fattore diverso, come un codice login inviato tramite email, sarà richiesto per l'accesso. Potrai sempre reimpostare i codici monouso più tardi.", + "login.totp.disable.admin": "

Questo disattiverà i codici monouso per {user}.

In futuro verrà richiesto un secondo fattore diverso per l'accesso, per esempio un codice inviato per email. {user} potrà impostare nuovamente i codici monouso dopo il suo prossimo accesso.

", + "login.totp.disable.success": "Codici monouso disattivati", "logout": "Esci", + "merge": "Merge", "menu": "Menu", "meridiem": "AM/PM", "mime": "MIME Type", @@ -411,21 +558,26 @@ "months.september": "Settembre", "more": "Di più", + "move": "Move", "name": "Nome", "next": "Prossimo", + "night": "Night", "no": "no", "off": "off", "on": "on", "open": "Apri", "open.newWindow": "Apri in una finestra nuova", + "option": "Option", "options": "Opzioni", "options.none": "Nessuna opzione", + "options.all": "Mostra tutte le {count} opzioni", "orientation": "Orientamento", "orientation.landscape": "Orizzontale", "orientation.portrait": "Verticale", "orientation.square": "Quadrato", + "page": "Page", "page.blueprint": "Questa pagina non ha ancora un blueprint. Puoi definire la sua configurazione in /site/blueprints/pages/{blueprint}.yml", "page.changeSlug": "Modifica URL", "page.changeSlug.fromTitle": "Crea in base al titolo", @@ -433,13 +585,15 @@ "page.changeStatus.position": "Scegli una posizione", "page.changeStatus.select": "Seleziona un nuovo stato", "page.changeTemplate": "Cambia template", + "page.changeTemplate.notice": "Changing the page's template will remove content for fields that don't match in type. Use with caution.", + "page.create": "Crea come \"{status}\"", "page.delete.confirm": "Sei sicuro di voler eliminare questa pagina?", "page.delete.confirm.subpages": "Questa pagina ha sottopagine.
Anche tutte le sottopagine verranno eliminate.", "page.delete.confirm.title": "Inserisci il titolo della pagina per confermare", - "page.draft.create": "Crea bozza", "page.duplicate.appendix": "Copia", "page.duplicate.files": "Copia file", "page.duplicate.pages": "Copia pagine", + "page.move": "Move page", "page.sort": "Cambia posizione", "page.status": "Stato", "page.status.draft": "Bozza", @@ -450,24 +604,32 @@ "page.status.unlisted.description": "La pagina è accessibile soltanto tramite URL", "pages": "Pagine", + "pages.delete.confirm.selected": "Do you really want to delete the selected pages? This action cannot be undone.", "pages.empty": "Nessuna pagina", "pages.status.draft": "Bozza", "pages.status.listed": "Pubblicato", "pages.status.unlisted": "Non in elenco", - "pagination.page": "Page", + "pagination.page": "Pagina", "password": "Password", "paste": "Incolla", "paste.after": "Incolla dopo", + "paste.success": "{count} pasted!", "pixel": "Pixel", "plugin": "Plugin", "plugins": "Plugins", "prev": "Precedente", "preview": "Anteprima", + + "publish": "Publish", + "published": "Pubblicato", + "remove": "Rimuovi", "rename": "Rinomina", + "renew": "Rinnova", "replace": "Sostituisci", + "replace.with": "Replace with", "retry": "Riprova", "revert": "Abbandona", "revert.confirm": "Sei sicuro di voler cancellare tutte le modifiche non salvate?", @@ -482,11 +644,14 @@ "role.nobody.title": "Nessuno", "save": "Salva", + "saved": "Saved", "search": "Cerca", + "searching": "Searching", "search.min": "Inserisci almeno {min} caratteri per la ricerca", - "search.all": "Mostra tutti", + "search.all": "Mostra tutti i {count} risultati", "search.results.none": "Nessun risultato", + "section.invalid": "The section is invalid", "section.required": "La sezione è obbligatoria", "security": "Sicurezza", @@ -498,16 +663,25 @@ "size": "Dimensioni", "slug": "URL", "sort": "Ordina", + "sort.drag": "Drag to sort …", + "split": "Split", "stats.empty": "Nessuna segnalazione", + "status": "Stato", + + "system.info.copy": "Copy info", + "system.info.copied": "System info copied", "system.issues.content": "La cartella content sembra essere esposta", "system.issues.eol.kirby": "La versione di Kirby installata è giunta alla fine del suo ciclo di vita e non riceverà ulteriori aggiornamenti di sicurezza ", "system.issues.eol.plugin": "La versione installata del plugin { plugin } è giunta alla fine del suo ciclo di vita e non riceverà ulteriori aggiornamenti di sicurezza", + "system.issues.eol.php": "La versione {release} di PHP installata è giunta alla fine del suo ciclo di vita e non riceverà ulteriori aggiornamenti di sicurezza", "system.issues.debug": "Il debug deve essere disattivato in produzione", "system.issues.git": "La cartella .git sembra essere esposta", "system.issues.https": "Raccomandiamo l'utilizzo di HTTPS per tutti i siti", "system.issues.kirby": "La cartella kirby sembra essere esposta", + "system.issues.local": "The site is running locally with relaxed security checks", "system.issues.site": "La cartella site sembra essere esposta", + "system.issues.vue.compiler": "The Vue template compiler is enabled", "system.issues.vulnerability.kirby": "La tua installazione potrebbe essere colpita dalla seguente vulnerabilità ({ severity } gravità): { description }", "system.issues.vulnerability.plugin": "La tua installazione potrebbe essere colpita dalla seguente vulnerabilità nel plugin { plugin } ({ severity } gravità): { description }", "system.updateStatus": "Aggiorna lo stato", @@ -520,10 +694,19 @@ "system.updateStatus.update": "Aggiornamento gratuito { version } disponibile", "system.updateStatus.upgrade": "Aggiornamento { version } disponibile", - "title": "Titolo", + "tel": "Phone", + "tel.placeholder": "+49123456789", "template": "Template", + + "theme": "Theme", + "theme.light": "Lights on", + "theme.dark": "Lights off", + "theme.automatic": "Match system default", + + "title": "Titolo", "today": "Oggi", + "toolbar.button.clear": "Clear formatting", "toolbar.button.code": "Codice", "toolbar.button.bold": "Grassetto", "toolbar.button.email": "Email", @@ -541,6 +724,8 @@ "toolbar.button.link": "Link", "toolbar.button.paragraph": "Paragrafo", "toolbar.button.strike": "Barrato", + "toolbar.button.sub": "Subscript", + "toolbar.button.sup": "Superscript", "toolbar.button.ol": "Elenco numerato", "toolbar.button.underline": "Sottolinea", "toolbar.button.ul": "Elenco puntato", @@ -550,6 +735,8 @@ "translation.name": "Italiano", "translation.locale": "it_IT", + "type": "Tipo", + "upload": "Carica", "upload.error.cantMove": "Non è stato possibile spostare il file caricato", "upload.error.cantWrite": "Impossibile scrivere il file su disco", @@ -562,7 +749,7 @@ "upload.error.noFiles": "Nessun file è stato caricato", "upload.error.partial": "Il file è stato caricato solo parzialmente", "upload.error.tmpDir": "Manca la cartella temporanea", - "upload.errors": "Error", + "upload.errors": "Errore", "upload.progress": "Caricamento...", "url": "URL", @@ -574,6 +761,7 @@ "user.changeLanguage": "Cambia lingua", "user.changeName": "Rinomina questo utente", "user.changePassword": "Cambia password", + "user.changePassword.current": "Your current password", "user.changePassword.new": "Nuova password", "user.changePassword.new.confirm": "Conferma la nuova password...", "user.changeRole": "Cambia ruolo", @@ -585,10 +773,13 @@ "users": "Utenti", "version": "Versione di Kirby", + "version.changes": "Changed version", + "version.compare": "Compare versions", "version.current": "Versione corrente", "version.latest": "Ultima versione", "versionInformation": "Informazioni sulla versione", + "view": "View", "view.account": "Il tuo account", "view.installation": "Installazione", "view.languages": "Lingue", diff --git a/kirby/i18n/translations/ko.json b/kirby/i18n/translations/ko.json index 7805add..41f85a7 100644 --- a/kirby/i18n/translations/ko.json +++ b/kirby/i18n/translations/ko.json @@ -1,66 +1,84 @@ { "account.changeName": "이름 변경", "account.delete": "계정 삭제", - "account.delete.confirm": "계정을 삭제할까요? 계정을 삭제한 뒤에는 복구할 수 없습니다.", + "account.delete.confirm": "계정을 삭제할까요? 계정을 삭제한 뒤에는 즉시 로그아웃되며, 삭제된 계정은 복구할 수 없습니다.", - "add": "추가", + "activate": "활성화", + "add": "\ucd94\uac00", + "alpha": "알파", "author": "저자", "avatar": "프로필 이미지", "back": "뒤로", - "cancel": "취소", - "change": "변경", - "close": "닫기", + "cancel": "\ucde8\uc18c", + "change": "\ubcc0\uacbd", + "close": "\ub2eb\uae30", + "changes": "변경", "confirm": "확인", "collapse": "접기", "collapse.all": "모두 접기", + "color": "색", + "coordinates": "좌표", "copy": "복사", "copy.all": "모두 복사", + "copy.success": "복사되었습니다. ({count})", + "copy.success.multiple": "복사되었습니다. ({count})", + "copy.url": "Copy URL", "create": "등록", + "custom": "개인화", "date": "날짜", "date.select": "날짜 지정", "day": "일", - "days.fri": "금", - "days.mon": "월", - "days.sat": "토", - "days.sun": "일", - "days.thu": "목", - "days.tue": "화", - "days.wed": "수", + "days.fri": "\uae08", + "days.mon": "\uc6d4", + "days.sat": "\ud1a0", + "days.sun": "\uc77c", + "days.thu": "\ubaa9", + "days.tue": "\ud654", + "days.wed": "\uc218", "debugging": "디버그", - "delete": "삭제", + "delete": "\uc0ad\uc81c", "delete.all": "모두 삭제", + "dialog.fields.empty": "필드가 없습니다.", "dialog.files.empty": "선택할 파일이 없습니다.", "dialog.pages.empty": "선택할 페이지가 없습니다.", + "dialog.text.empty": "정의된 텍스트가 없습니다.", "dialog.users.empty": "선택할 사용자가 없습니다.", "dimensions": "크기", + "disable": "비활성화", "disabled": "비활성화", "discard": "무시", + + "drawer.fields.empty": "필드가 없습니다.", + + "domain": "도메인", "download": "다운로드", "duplicate": "복제", - "edit": "편집", + "edit": "\ud3b8\uc9d1", - "email": "이메일 주소", + "email": "\uc774\uba54\uc77c \uc8fc\uc18c", "email.placeholder": "mail@example.com", + "enter": "Enter", "entries": "항목", "entry": "항목", "environment": "구동 환경", + "error": "오류", "error.access.code": "코드가 올바르지 않습니다.", "error.access.login": "로그인할 수 없습니다.", "error.access.panel": "패널에 접근할 권한이 없습니다.", "error.access.view": "패널에 접근할 권한이 없습니다.", "error.avatar.create.fail": "프로필 이미지를 업로드할 수 없습니다.", - "error.avatar.delete.fail": "프로필 이미지를 삭제할 수 없습니다.", + "error.avatar.delete.fail": "\ud504\ub85c\ud544 \uc774\ubbf8\uc9c0\ub97c \uc0ad\uc81c\ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4.", "error.avatar.dimensions.invalid": "프로필 이미지의 너비와 높이를 3,000픽셀 이하로 설정하세요.", "error.avatar.mime.forbidden": "프로필 이미지의 확장자(JPG, JPEG, PNG)를 확인하세요.", @@ -70,17 +88,35 @@ "error.blocks.max.singular": "블록을 하나 이상 추가할 수 없습니다.", "error.blocks.min.plural": "블록을 {min}개 이상 추가하세요.", "error.blocks.min.singular": "블록을 하나 이상 추가하세요.", - "error.blocks.validation": "There's an error on the \"{field}\" field in block {index} using the \"{fieldset}\" block type", + "error.blocks.validation": "블록 유형({fieldset})을 사용하는 블록({index})의 필드({field})에 오류가 있습니다.", - "error.cache.type.invalid": "Invalid cache type \"{type}\"", + "error.cache.type.invalid": "캐시 형식(({type})이 올바르지 않습니다.", + + "error.content.lock.delete": "The version is locked and cannot be deleted", + "error.content.lock.move": "The source version is locked and cannot be moved", + "error.content.lock.publish": "This version is already published", + "error.content.lock.replace": "The version is locked and cannot be replaced", + "error.content.lock.update": "The version is locked and cannot be updated", + + "error.entries.max.plural": "You must not add more than {max} entries", + "error.entries.max.singular": "You must not add more than one entry", + "error.entries.min.plural": "You must add at least {min} entries", + "error.entries.min.singular": "You must add at least one entry", + "error.entries.supports": "\"{type}\" field type is not supported for the entries field", + "error.entries.validation": "{index}번째 필드({field})에 오류가 있습니다.", "error.email.preset.notFound": "기본 이메일 주소({name})가 없습니다.", "error.field.converter.invalid": "컨버터({converter})가 올바르지 않습니다.", - "error.field.type.missing": "Field \"{ name }\": The field type \"{ type }\" does not exist", + "error.field.link.options": "Invalid options: {options}", + "error.field.type.missing": "필드({name}): 필드 타입({type})이 없습니다.", "error.file.changeName.empty": "이름을 입력하세요.", "error.file.changeName.permission": "파일명({filename})을 변경할 권한이 없습니다.", + "error.file.changeTemplate.invalid": "파일({id}) 템플릿을 다음 템플릿({template})으로 변경할 수 없습니다. (valid: \"{blueprints}\")", + "error.file.changeTemplate.permission": "파일({id}) 템플릿을 변경할 수 없습니다.", + + "error.file.delete.multiple": "Not all files could be deleted. Try each remaining file individually to see the specific error that prevents deletion.", "error.file.duplicate": "파일명이 같은 파일({filename})이 있습니다.", "error.file.extension.forbidden": "이 확장자({extension})는 업로드할 수 없습니다.", "error.file.extension.invalid": "확장자({extension})가 올바르지 않습니다.", @@ -95,55 +131,73 @@ "error.file.minheight": "이미지의 높이를 {height}픽셀 이상으로 설정하세요.", "error.file.minsize": "파일이 너무 작습니다.", "error.file.minwidth": "이미지의 너비를 {width}픽셀 이상으로 설정하세요.", + "error.file.name.unique": "고유한 파일명을 지정하세요.", "error.file.name.missing": "파일명을 입력하세요.", "error.file.notFound": "파일({filename})이 없습니다.", "error.file.orientation": "이미지의 비율({orientation})을 확인하세요.", + "error.file.sort.permission": "You are not allowed to change the sorting of \"{filename}\"", "error.file.type.forbidden": "이 형식({type})의 파일을 업로드할 권한이 없습니다.", - "error.file.type.invalid": "파일의 형식({type})이 올바르지 않습니다.", - "error.file.undefined": "파일이 없습니다.", + "error.file.type.invalid": "파일 형식({type})이 올바르지 않습니다.", + "error.file.undefined": "\ud30c\uc77c\uc774 \uc5c6\uc2b5\ub2c8\ub2e4.", "error.form.incomplete": "항목에 오류가 있습니다.", "error.form.notSaved": "항목을 저장할 수 없습니다.", "error.language.code": "올바른 언어 코드를 입력하세요.", + "error.language.create.permission": "언어를 등록할 권한이 없습니다.", + "error.language.delete.permission": "언어를 삭제할 권한이 없습니다.", "error.language.duplicate": "이미 등록한 언어입니다.", "error.language.name": "올바른 언어명을 입력하세요.", "error.language.notFound": "언어를 찾을 수 없습니다.", + "error.language.update.permission": "언어를 변경할 권한이 없습니다.", - "error.layout.validation.block": "There's an error on the \"{field}\" field in block {blockIndex} using the \"{fieldset}\" block type in layout {layoutIndex}", - "error.layout.validation.settings": "레이아웃({index})의 옵션을 확인하세요.", + "error.layout.validation.block": "레이아웃({layoutIndex})의 특정 블록 유형({fieldset})을 사용하는 블록({blockIndex})의 특정 필드({field})에 오류가 있습니다.", + "error.layout.validation.settings": "레이아웃({index}) 옵션을 확인하세요.", - "error.license.format": "올바른 라이선스 키를 입력하세요.", + "error.license.domain": "라이선스에 대한 도메인이 누락되었습니다.", "error.license.email": "올바른 이메일 주소를 입력하세요.", + "error.license.format": "올바른 라이선스 코드를 입력하세요.", "error.license.verification": "라이선스 키가 올바르지 않습니다.", - "error.object.validation": "{label} 필드에 오류가 있습니다.\n{message}", + "error.login.totp.confirm.invalid": "코드가 올바르지 않습니다.", + "error.login.totp.confirm.missing": "현재 코드를 입력하세요.", + + "error.object.validation": "필드({label})에 오류가 있습니다.\n{message}", "error.offline": "패널이 오프라인 상태입니다.", "error.page.changeSlug.permission": "고유 주소({slug})를 변경할 권한이 없습니다.", + "error.page.changeSlug.reserved": "상위 페이지는 이 경로({path})로 시작할 수 없습니다.", "error.page.changeStatus.incomplete": "페이지를 공개할 수 없습니다.", - "error.page.changeStatus.permission": "페이지의 상태를 변경할 수 없습니다.", - "error.page.changeStatus.toDraft.invalid": "페이지({slug})의 상태를 초안으로 변경할 수 없습니다.", - "error.page.changeTemplate.invalid": "페이지({slug})의 템플릿을 변경할 수 없습니다.", - "error.page.changeTemplate.permission": "페이지({slug})의 템플릿을 변경할 권한이 없습니다.", + "error.page.changeStatus.permission": "페이지 상태를 변경할 수 없습니다.", + "error.page.changeStatus.toDraft.invalid": "페이지({slug}) 상태를 초안으로 변경할 수 없습니다.", + "error.page.changeTemplate.invalid": "페이지({slug}) 템플릿을 변경할 수 없습니다.", + "error.page.changeTemplate.permission": "페이지({slug}) 템플릿을 변경할 권한이 없습니다.", "error.page.changeTitle.empty": "제목을 입력하세요.", - "error.page.changeTitle.permission": "페이지({slug})의 제목을 변경할 권한이 없습니다.", + "error.page.changeTitle.permission": "페이지({slug}) 제목을 변경할 권한이 없습니다.", "error.page.create.permission": "페이지({slug})를 등록할 권한이 없습니다.", "error.page.delete": "페이지({slug})를 삭제할 수 없습니다.", "error.page.delete.confirm": "페이지를 삭제하려면 페이지의 제목을 입력하세요.", "error.page.delete.hasChildren": "하위 페이지가 있는 페이지는 삭제할 수 없습니다.", + "error.page.delete.multiple": "Not all pages could be deleted. Try each remaining page individually to see the specific error that prevents deletion.", "error.page.delete.permission": "페이지({slug})를 삭제할 권한이 없습니다.", "error.page.draft.duplicate": "고유 주소({slug})가 같은 초안 페이지가 있습니다.", "error.page.duplicate": "고유 주소({slug})가 같은 페이지가 있습니다.", "error.page.duplicate.permission": "페이지({slug})를 복제할 권한이 없습니다.", + "error.page.move.ancestor": "해당 페이지로 이동할 수 없습니다.", + "error.page.move.directory": "페이지 디렉토리는 이동할 수 없습니다.", + "error.page.move.duplicate": "고유 주소({slug})가 같은 서브 페이지가 있습니다.", + "error.page.move.noSections": "The page \"{parent}\" cannot be a parent of any page because it lacks any pages sections in its blueprint", + "error.page.move.notFound": "이동된 페이지를 찾을 수 없습니다.", + "error.page.move.permission": "페이지({slug})를 이동할 권한이 없습니다.", + "error.page.move.template": "이 템플릿({template})은 이 페이지({parent})의 서브 페이지로 이동할 수 없습니다.", "error.page.notFound": "페이지({slug})가 없습니다.", "error.page.num.invalid": "올바른 정수를 입력하세요.", "error.page.slug.invalid": "올바른 URL을 입력하세요.", "error.page.slug.maxlength": "고유 주소를 {length}자 이하로 입력하세요.", "error.page.sort.permission": "페이지({slug})를 정렬할 수 없습니다.", "error.page.status.invalid": "올바른 상태를 설정하세요.", - "error.page.undefined": "페이지가 없습니다.", + "error.page.undefined": "\ud398\uc774\uc9c0\uac00 \uc5c6\uc2b5\ub2c8\ub2e4.", "error.page.update.permission": "페이지({slug})를 변경할 권한이 없습니다.", "error.section.files.max.plural": "이 섹션({section})에는 파일을 {max}개 이상 추가할 수 없습니다.", @@ -157,11 +211,13 @@ "error.section.pages.min.singular": "이 섹션({section})에는 페이지가 하나 이상 필요합니다.", "error.section.notLoaded": "섹션({name})을 확인할 수 없습니다.", - "error.section.type.invalid": "섹션의 형식({type})이 올바르지 않습니다.", + "error.section.type.invalid": "섹션 형식({type})이 올바르지 않습니다.", "error.site.changeTitle.empty": "제목을 입력하세요.", "error.site.changeTitle.permission": "사이트명을 변경할 권한이 없습니다.", - "error.site.update.permission": "사이트의 정보를 변경할 권한이 없습니다.", + "error.site.update.permission": "사이트 정보를 변경할 권한이 없습니다.", + + "error.structure.validation": "{index}번째 필드({field})에 오류가 있습니다.", "error.template.default.notFound": "기본 템플릿이 없습니다.", @@ -176,16 +232,16 @@ "error.user.changeRole.toAdmin": "다른 사용자를 관리자로 지정할 권한이 없습니다.", "error.user.create.permission": "사용자를 등록할 권한이 없습니다.", "error.user.delete": "사용자({name})를 삭제할 수 없습니다.", - "error.user.delete.lastAdmin": "최종 관리자는 삭제할 수 없습니다.", + "error.user.delete.lastAdmin": "\ucd5c\uc885 \uad00\ub9ac\uc790\ub294 \uc0ad\uc81c\ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4.", "error.user.delete.lastUser": "최종 사용자는 삭제할 수 없습니다.", "error.user.delete.permission": "사용자({name})를 삭제할 권한이 없습니다.", "error.user.duplicate": "이메일 주소({email})가 같은 사용자가 있습니다.", "error.user.email.invalid": "올바른 이메일 주소를 입력하세요.", "error.user.language.invalid": "올바른 언어를 입력하세요.", "error.user.notFound": "사용자({name})가 없습니다.", - "error.user.password.excessive": "Please enter a valid password. Passwords must not be longer than 1000 characters.", + "error.user.password.excessive": "올바른 암호를 입력하세요. 암호는 1,000자를 넘을 수 없습니다.", "error.user.password.invalid": "암호를 8자 이상으로 설정하세요.", - "error.user.password.notSame": "암호를 확인하세요.", + "error.user.password.notSame": "\uc554\ud638\ub97c \ud655\uc778\ud558\uc138\uc694.", "error.user.password.undefined": "암호가 설정되지 않았습니다.", "error.user.password.wrong": "암호가 올바르지 않습니다.", "error.user.role.invalid": "올바른 역할을 지정하세요.", @@ -195,8 +251,10 @@ "error.validation.accepted": "확인하세요.", "error.validation.alpha": "로마자(a~z)만 입력할 수 있습니다.", "error.validation.alphanum": "로마자(a~z) 또는 숫자(0~9)만 입력할 수 있습니다.", + "error.validation.anchor": "올바른 링크를 입력하세요.", "error.validation.between": "{min}, {max} 사이의 값을 입력하세요.", "error.validation.boolean": "확인하거나 취소하세요.", + "error.validation.color": "색상 값은 {format} 형식으로 입력하세요.", "error.validation.contains": "{needle}에 포함된 값을 입력하세요.", "error.validation.date": "올바른 날짜를 입력하세요.", "error.validation.date.after": "{date} 이후 날짜를 입력하세요.", @@ -211,6 +269,7 @@ "error.validation.integer": "올바른 정수를 입력하세요.", "error.validation.ip": "올바른 IP 주소를 입력하세요.", "error.validation.less": "{max} 미만의 값을 입력하세요.", + "error.validation.linkType": "이 형식의 링크는 입력할 수 없습니다.", "error.validation.match": "입력한 값이 예상 패턴과 일치하지 않습니다.", "error.validation.max": "{max} 이하의 값을 입력하세요.", "error.validation.maxlength": "{max}자 이하의 값을 입력하세요.", @@ -227,38 +286,47 @@ "error.validation.same": "이 값({other})을 입력하세요.", "error.validation.size": "값의 크기({size})를 확인하세요. ", "error.validation.startswith": "값은 다음({start})으로 시작해야 합니다.", + "error.validation.tel": "숫자만 입력하세요.", "error.validation.time": "올바른 시각을 입력하세요.", "error.validation.time.after": "{time} 이후 시각을 입력하세요.", "error.validation.time.before": "{time} 이전 시각을 입력하세요.", "error.validation.time.between": "{min}, {max} 사이의 시각을 입력하세요.", + "error.validation.uuid": "올바른 UUID를 입력하세요.", "error.validation.url": "올바른 URL을 입력하세요.", "expand": "열기", "expand.all": "모두 열기", + "field.invalid": "필드가 올바르지 않습니다.", "field.required": "필드를 채우세요.", "field.blocks.changeType": "유형 변경", - "field.blocks.code.name": "언어 코드", + "field.blocks.code.name": "코드", "field.blocks.code.language": "언어", "field.blocks.code.placeholder": "코드", "field.blocks.delete.confirm": "블록을 삭제할까요?", "field.blocks.delete.confirm.all": "모든 블록을 삭제할까요?", "field.blocks.delete.confirm.selected": "선택한 블록을 삭제할까요?", "field.blocks.empty": "블록이 없습니다.", - "field.blocks.fieldsets.label": "블록의 유형을 선택하세요.", - "field.blocks.fieldsets.paste": "단축키({{ shortcut }})로 클립보드에서 블록을 가져올 수 있습니다.", + "field.blocks.fieldsets.empty": "필드셋이 없습니다.", + "field.blocks.fieldsets.label": "블록 유형을 선택하세요.", + "field.blocks.fieldsets.paste": "{{ shortcut }}를 눌러 클립보드에서 레이아웃 또는 블록을 가져옵니다. 현재 필드에서 허용된 것만 삽입됩니다.", "field.blocks.gallery.name": "갤러리", "field.blocks.gallery.images.empty": "이미지가 없습니다.", "field.blocks.gallery.images.label": "이미지", "field.blocks.heading.level": "단계", "field.blocks.heading.name": "제목", - "field.blocks.heading.text": "마크다운", + "field.blocks.heading.text": "제목", "field.blocks.heading.placeholder": "제목", + "field.blocks.figure.back.plain": "Plain", + "field.blocks.figure.back.pattern.light": "Pattern (light)", + "field.blocks.figure.back.pattern.dark": "Pattern (dark)", "field.blocks.image.alt": "대체 텍스트", "field.blocks.image.caption": "캡션", "field.blocks.image.crop": "자르기", - "field.blocks.image.link": "일반 링크", + "field.blocks.image.link": "링크", "field.blocks.image.location": "위치", + "field.blocks.image.location.internal": "이 웹사이트", + "field.blocks.image.location.external": "외부 소스", "field.blocks.image.name": "이미지", "field.blocks.image.placeholder": "이미지 선택", "field.blocks.image.ratio": "비율", @@ -269,47 +337,81 @@ "field.blocks.markdown.label": "마크다운", "field.blocks.markdown.placeholder": "마크다운", "field.blocks.quote.name": "인용문", - "field.blocks.quote.text.label": "마크다운", + "field.blocks.quote.text.label": "인용문", "field.blocks.quote.text.placeholder": "인용문", "field.blocks.quote.citation.label": "출처", "field.blocks.quote.citation.placeholder": "출처", - "field.blocks.text.name": "마크다운", + "field.blocks.text.name": "텍스트", "field.blocks.text.placeholder": "텍스트", + "field.blocks.video.autoplay": "자동 재생", "field.blocks.video.caption": "캡션", + "field.blocks.video.controls": "제어 도구", + "field.blocks.video.location": "위치", + "field.blocks.video.loop": "반복", + "field.blocks.video.muted": "음소거", "field.blocks.video.name": "영상", "field.blocks.video.placeholder": "영상 URL 입력", + "field.blocks.video.poster": "대표 이미지", + "field.blocks.video.preload": "미리 로드", "field.blocks.video.url.label": "영상 URL", "field.blocks.video.url.placeholder": "https://youtube.com/?v=", - "field.files.empty": "선택한 파일이 없습니다.", + "field.entries.delete.confirm.all": "모든 항목을 삭제할까요?", + "field.entries.empty": "항목이 없습니다.", + "field.files.empty": "선택한 파일이 없습니다.", + "field.files.empty.single": "No file selected yet", + + "field.layout.change": "레이아웃 변경", "field.layout.delete": "레이아웃 삭제", "field.layout.delete.confirm": "레이아웃을 삭제할까요?", + "field.layout.delete.confirm.all": "모든 레이아웃을 삭제할까요?", "field.layout.empty": "행이 없습니다.", "field.layout.select": "레이아웃 선택", "field.object.empty": "정보가 없습니다.", "field.pages.empty": "선택한 페이지가 없습니다.", + "field.pages.empty.single": "No page selected yet", "field.structure.delete.confirm": "이 항목을 삭제할까요?", "field.structure.delete.confirm.all": "모든 항목을 삭제할까요?", "field.structure.empty": "항목이 없습니다.", "field.users.empty": "선택한 사용자가 없습니다.", + "field.users.empty.single": "No user selected yet", + "fields.empty": "필드가 없습니다.", + + "file": "파일", "file.blueprint": "블루프린트(/site/blueprints/files/{blueprint}.yml)를 설정하세요.", + "file.changeTemplate": "템플릿 변경", + "file.changeTemplate.notice": "템플릿을 변경하면 유형이 일치하지 않은 필드에 입력한 콘텐츠가 삭제됩니다. 템플릿에서 이미지 크기 등 특정 규칙이 지정된 경우 해당 규칙을 되돌릴 수 없습니다.", "file.delete.confirm": "파일({filename})을 삭제할까요?", + "file.focus.placeholder": "초점 설정", + "file.focus.reset": "초점 삭제", + "file.focus.title": "초점", "file.sort": "순서 변경", "files": "파일", + "files.delete.confirm.selected": "Do you really want to delete the selected files? This action cannot be undone.", "files.empty": "파일이 없습니다.", + "filter": "필터", + + "form.discard": "Discard changes", + "form.discard.confirm": "Do you really want to discard all your changes?", + "form.locked": "This content is disabled for you as it is currently edited by another user", + "form.unsaved": "The current changes have not yet been saved", + "form.preview": "Preview changes", + "form.preview.draft": "Preview draft", + "hide": "숨기기", "hour": "시", + "hue": "색상", "import": "가져오기", "info": "정보", - "insert": "삽입", + "insert": "\uc0bd\uc785", "insert.after": "뒤에 삽입", "insert.before": "앞에 삽입", "install": "설치", @@ -324,14 +426,14 @@ "installation.issues.mbstring": "MB String 확장 모듈이 필요합니다.", "installation.issues.media": "/media 폴더의 쓰기 권한을 확인하세요.", "installation.issues.php": "PHP 버전이 8 이상인지 확인하세요.", - "installation.issues.server": "Kirby를 실행하려면 Apache, Nginx, 또는 Caddy가 필요합니다.", "installation.issues.sessions": "/site/sessions 폴더의 쓰기 권한을 확인하세요.", - "language": "언어", + "language": "\uc5b8\uc5b4", "language.code": "언어 코드", "language.convert": "기본 언어로 지정", "language.convert.confirm": "이 언어({name})를 기본 언어로 지정할까요? 지정한 뒤에는 복원할 수 없으며, 이 언어로 번역되지 않은 항목은 올바르게 표시되지 않을 수 있습니다.", "language.create": "새 언어 추가", + "language.default": "기본 언어", "language.delete.confirm": "언어({name})를 삭제할까요? 삭제한 뒤에는 복원할 수 없습니다.", "language.deleted": "언어를 삭제했습니다.", "language.direction": "읽기 방향", @@ -339,8 +441,17 @@ "language.direction.rtl": "오른쪽에서 왼쪽", "language.locale": "PHP 로캘 문자열", "language.locale.warning": "사용자 지정 로캘을 사용 중입니다. /site/languages 폴더의 언어 파일을 수정하세요.", - "language.name": "이름", + "language.name": "언어명", + "language.secondary": "보조 언어", + "language.settings": "언어 설정", "language.updated": "언어를 변경했습니다.", + "language.variables": "언어 변수", + "language.variables.empty": "번역이 없습니다.", + + "language.variable.delete.confirm": "변수({key})를 삭제할까요?", + "language.variable.key": "키", + "language.variable.notFound": "변수를 찾을 수 없습니다.", + "language.variable.value": "값", "languages": "언어", "languages.default": "기본 언어", @@ -349,35 +460,56 @@ "languages.secondary.empty": "보조 언어가 없습니다.", "license": "라이선스", + "license.activate": "지금 활성화하세요.", + "license.activate.label": "라이선스를 활성화하세요.", + "license.activate.domain": "{host}에 대한 라이선스를 활성화합니다.", + "license.activate.local": "로컬 도메인({host})에 대한 라이선스를 활성화합니다. 현재 도메인({host})이 라이선스를 사용하려는 도메인인과 같다면 계속 진행하세요.", + "license.activated": "활성화됨", "license.buy": "라이선스 구매", - "license.register": "등록", + "license.code": "언어 코드", + "license.code.help": "이메일로 전송된 라이선스 코드를 복사해 붙여넣으세요.", + "license.code.label": "라이선스 코드를 입력하세요.", + "license.status.active.info": "새로운 메이저 버전은 {date}까지 포함됩니다.", + "license.status.active.label": "유효한 라이선스", + "license.status.demo.info": "데모를 설치합니다.", + "license.status.demo.label": "데모", + "license.status.inactive.info": "새로운 메이저 버전으로 업데이트하려면 라이선스를 갱신하세요.", + "license.status.inactive.label": "새로운 메이저 버전이 없습니다.", + "license.status.legacy.bubble": "라이선스를 갱신합니다.", + "license.status.legacy.info": "라이선스가 이 버전을 지원하지 않습니다.", + "license.status.legacy.label": "라이선스를 갱신하세요.", + "license.status.missing.bubble": "사이트를 공개합니다.", + "license.status.missing.info": "유효한 라이선스가 없습니다.", + "license.status.missing.label": "라이선스를 활성화하세요.", + "license.status.unknown.info": "The license status is unknown", + "license.status.unknown.label": "Unknown", "license.manage": "라이선스 관리", - "license.register.help": "Kirby를 등록하려면 이메일로 전송받은 라이선스 코드와 이메일 주소를 입력하세요.", - "license.register.label": "라이선스 코드를 입력하세요.", - "license.register.domain": "Your license will be registered to {host}.", - "license.register.local": "You are about to register your license for your local domain {host}. If this site will be deployed to a public domain, please register it there instead. If {host} is the domain you want to license Kirby to, please continue.", - "license.register.success": "Kirby와 함께해주셔서 감사합니다.", - "license.unregistered": "Kirby가 등록되지 않았습니다.", + "license.purchased": "구입했습니다.", + "license.success": "Kirby와 함께해주셔서 감사합니다.", "license.unregistered.label": "Kirby가 등록되지 않았습니다.", - "link": "일반 링크", - "link.text": "문자", + "link": "\uc77c\ubc18 \ub9c1\ud06c", + "link.text": "\ubb38\uc790", "loading": "로딩 중…", "lock.unsaved": "저장되지 않은 항목이 있습니다.", "lock.unsaved.empty": "모든 페이지를 저장했습니다.", - "lock.isLocked": "다른 사용자({email})가 수정한 사항이 저장되지 않았습니다.", - "lock.file.isLocked": "파일을 편집할 수 없습니다. 다른 사용자({email})가 편집 중입니다.", - "lock.page.isLocked": "페이지를 편집할 수 없습니다. 다른 사용자({email})가 편집 중입니다.", + "lock.unsaved.files": "Unsaved files", + "lock.unsaved.pages": "Unsaved pages", + "lock.unsaved.users": "Unsaved accounts", + "lock.isLocked": "사용자({email})의 변경 사항이 저장되지 않았습니다.", "lock.unlock": "잠금 해제", - "lock.isUnlocked": "다른 사용자가 이미 내용을 수정했으므로 현재 내용이 올바르게 저장되지 않았습니다. 저장되지 않은 내용은 다운로드해 수동으로 대치할 수 있습니다.", + "lock.unlock.submit": "사용자({email})의 저장되지 않은 변경 사항을 해제하고 덮어쓰기", + "lock.isUnlocked": "다른 사용자가 잠금을 해제했습니다.", "login": "로그인", "login.code.label.login": "로그인 코드", "login.code.label.password-reset": "암호 초기화 코드", "login.code.placeholder.email": "000 000", + "login.code.placeholder.totp": "000000", "login.code.text.email": "입력한 이메일 주소로 코드를 전송했습니다.", + "login.code.text.totp": "인증 앱에서 생성된 일회용 코드를 입력하세요.", "login.email.login.body": "{user.nameOrEmail} 님,\n\n{site} 패널에서 요청한 로그인 코드는 다음과 같습니다. 로그인 코드는 {timeout}분 동안 유효합니다.\n\n{code}\n\n로그인 코드를 요청한 적이 없다면, 이 이메일을 무시하거나 관리자에게 문의하세요. 보안을 위해 이 이메일은 다른 사람과 공유하지 마세요.", "login.email.login.subject": "로그인 코드", "login.email.password-reset.body": "{user.nameOrEmail} 님,\n\n{site} 패널에서 요청한 암호 초기화 코드는 다음과 같습니다. 암호 초기화 코드는 {timeout}분 동안 유효합니다.\n\n{code}\n\n암호 초기화 코드를 요청한 적이 없다면, 이 이메일을 무시하거나 관리자에게 문의하세요. 보안을 위해 이 이메일은 다른 사람과 공유하지 마세요.", @@ -388,44 +520,64 @@ "login.toggleText.code.email-password": "암호로 로그인", "login.toggleText.password-reset.email": "암호 찾기", "login.toggleText.password-reset.email-password": "로그인 화면으로", + "login.totp.enable.option": "일회용 코드 설정", + "login.totp.enable.intro": "인증 앱은 계정에 로그인하기 위한 일회용 코드를 생성할 수 있습니다.", + "login.totp.enable.qr.label": "1. 이 QR 코드를 스캔하세요.", + "login.totp.enable.qr.help": "스캔할 수 없다면 인증 앱에 {secret} 설정 키를 수동으로 추가하세요.", + "login.totp.enable.confirm.headline": "2. 생성된 코드로 확인하세요.", + "login.totp.enable.confirm.text": "앱은 매 30초마다 새로운 일회용 코드를 생성합니다. 설정을 완료하기 위해 현재 코드를 입력하세요.", + "login.totp.enable.confirm.label": "현재 코드", + "login.totp.enable.confirm.help": "로그인할 때마다 일회용 코드를 요청합니다.", + "login.totp.enable.success": "일회용 코드가 활성화되었습니다.", + "login.totp.disable.option": "일회용 코드 비활성화", + "login.totp.disable.label": "비밀번호를 입력해 일회용 코드를 비활성화하세요.", + "login.totp.disable.help": "이후 로그인할 때 이메일로 발송된 로그인 코드와 같은 다른 두 번째 인증 요소를 요청합니다. 언제든 일회용 코드를 나중에 다시 설정할 수 있습니다.", + "login.totp.disable.admin": "사용자({user})의 일회용 코드를 비활성화합니다. 사용자({user})가 로그인할 때 이메일로 전송된 로그인 코드와 같은 다른 두 번째 인증 요소가 요청됩니다. 사용자({user})는 다음 로그인 후에 다시 일회용 코드를 설정할 수 있습니다.", + "login.totp.disable.success": "일회용 코드가 비활성화되었습니다.", "logout": "로그아웃", + "merge": "합치기", "menu": "메뉴", "meridiem": "오전/오후", "mime": "MIME 형식", "minutes": "분", "month": "월", - "months.april": "4월", - "months.august": "8월", - "months.december": "12월", + "months.april": "4\uc6d4", + "months.august": "8\uc6d4", + "months.december": "12\uc6d4", "months.february": "2월", - "months.january": "1월", - "months.july": "7월", - "months.june": "6월", - "months.march": "3월", - "months.may": "5월", - "months.november": "11월", - "months.october": "10월", - "months.september": "9월", + "months.january": "1\uc6d4", + "months.july": "7\uc6d4", + "months.june": "6\uc6d4", + "months.march": "3\uc6d4", + "months.may": "5\uc6d4", + "months.november": "11\uc6d4", + "months.october": "10\uc6d4", + "months.september": "9\uc6d4", "more": "더 보기", + "move": "이동", "name": "이름", "next": "다음", + "night": "밤", "no": "아니요", "off": "끔", "on": "켬", "open": "열기", "open.newWindow": "새 창에서 열기", + "option": "옵션", "options": "옵션", "options.none": "옵션이 없습니다.", + "options.all": "모든 옵션({count}) 표시", "orientation": "비율", "orientation.landscape": "가로로 긴 사각형", "orientation.portrait": "세로로 긴 사각형", "orientation.square": "정사각형", + "page": "페이지", "page.blueprint": "블루프린트(/site/blueprints/pages/{blueprint}.yml)를 설정하세요.", "page.changeSlug": "고유 주소 변경", "page.changeSlug.fromTitle": "제목에서 가져오기", @@ -433,13 +585,15 @@ "page.changeStatus.position": "순서를 지정하세요.", "page.changeStatus.select": "새 상태 선택", "page.changeTemplate": "템플릿 변경", + "page.changeTemplate.notice": "템플릿을 변경하면 유형이 일치하지 않은 필드에 입력한 콘텐츠가 삭제됩니다.", + "page.create": "해당 상태({status})로 생성", "page.delete.confirm": "페이지({title})를 삭제할까요?", "page.delete.confirm.subpages": "페이지에 하위 페이지가 있습니다. 모든 하위 페이지가 삭제됩니다.", - "page.delete.confirm.title": "페이지의 제목을 입력하세요.", - "page.draft.create": "초안 등록", + "page.delete.confirm.title": "페이지 제목을 입력하세요.", "page.duplicate.appendix": "복사", "page.duplicate.files": "파일 복사", "page.duplicate.pages": "페이지 복사", + "page.move": "페이지 이동", "page.sort": "순서 변경", "page.status": "상태", "page.status.draft": "초안", @@ -447,9 +601,10 @@ "page.status.listed": "공개", "page.status.listed.description": "누구나 읽을 수 있습니다.", "page.status.unlisted": "비공개", - "page.status.unlisted.description": "URL을 통해 접근할 수 있습니다.", + "page.status.unlisted.description": "오직 URL을 통해 접근할 수 있습니다.", "pages": "페이지", + "pages.delete.confirm.selected": "Do you really want to delete the selected pages? This action cannot be undone.", "pages.empty": "페이지가 없습니다.", "pages.status.draft": "초안", "pages.status.listed": "발행", @@ -457,18 +612,25 @@ "pagination.page": "페이지", - "password": "암호", + "password": "\uc554\ud638", "paste": "붙여넣기", "paste.after": "뒤로 붙여넣기", + "paste.success": "붙여넣었습니다. ({count})", "pixel": "픽셀", "plugin": "플러그인", "plugins": "플러그인", "prev": "이전", "preview": "미리 보기", + + "publish": "Publish", + "published": "발행", + "remove": "삭제", "rename": "이름 변경", - "replace": "교체", - "retry": "다시 시도", + "renew": "갱신", + "replace": "\uad50\uccb4", + "replace.with": "다음으로 교체", + "retry": "\ub2e4\uc2dc \uc2dc\ub3c4", "revert": "복원", "revert.confirm": "저장되지 않은 내용을 삭제할까요?", @@ -481,12 +643,15 @@ "role.nobody.description": "대체 사용자는 아무 권한이 없습니다.", "role.nobody.title": "사용자가 없습니다.", - "save": "저장", + "save": "\uc800\uc7a5", + "saved": "Saved", "search": "검색", + "searching": "Searching", "search.min": "{min}자 이상 입력하세요.", - "search.all": "모두 보기", + "search.all": "모든 결과({count}) 보기", "search.results.none": "해당하는 결과가 없습니다.", + "section.invalid": "섹션이 올바르지 않습니다.", "section.required": "섹션이 필요합니다.", "security": "보안", @@ -498,33 +663,51 @@ "size": "크기", "slug": "고유 주소", "sort": "정렬", + "sort.drag": "Drag to sort …", + "split": "나누기", "stats.empty": "관련 기록이 없습니다.", + "status": "상태", + + "system.info.copy": "정보 복사", + "system.info.copied": "시스템 정보가 복사되었습니다.", "system.issues.content": "/content 폴더의 권한을 확인하세요.", - "system.issues.eol.kirby": "설치된 Kirby는 버전이 만료되었습니다. 더 이상 보안 업데이트를 받을 수 없습니다.", - "system.issues.eol.plugin": "Your installed version of the { plugin } plugin is has reached end-of-life and will not receive further security updates", + "system.issues.eol.kirby": "설치된 Kirby 버전이 만료되었습니다. 더 이상 보안 업데이트를 받을 수 없습니다.", + "system.issues.eol.plugin": "설치된 플러그인({plugin}의 지원이 종료되었습니다. 더 이상 보안 업데이트를 받을 수 없습니다.", + "system.issues.eol.php": "설치된 PHP 버전({release})이 만료되었습니다. 더 이상 보안 업데이트를 받을 수 없습니다.", "system.issues.debug": "공개 서버상에서는 디버그 모드를 해제하세요.", "system.issues.git": "/.git 폴더의 권한을 확인하세요.", "system.issues.https": "HTTPS를 권장합니다.", "system.issues.kirby": "/kirby 폴더의 권한을 확인하세요.", + "system.issues.local": "The site is running locally with relaxed security checks", "system.issues.site": "/site 폴더의 권한을 확인하세요.", - "system.issues.vulnerability.kirby": "Your installation might be affected by the following vulnerability ({ severity } severity): { description }", - "system.issues.vulnerability.plugin": "Your installation might be affected by the following vulnerability in the { plugin } plugin ({ severity } severity): { description }", + "system.issues.vue.compiler": "The Vue template compiler is enabled", + "system.issues.vulnerability.kirby": "설치한 시스템에 취약점이 있습니다.\n심각도: {severity}\n{description}", + "system.issues.vulnerability.plugin": "설치한 플러그인({plugin})에 취약점이 있습니다.\n심각도: {severity}\n{ description }", "system.updateStatus": "업데이트 상태", "system.updateStatus.error": "업데이트를 확인할 수 없습니다.", "system.updateStatus.not-vulnerable": "알려진 취약성이 없습니다.", - "system.updateStatus.security-update": "Free security update { version } available", - "system.updateStatus.security-upgrade": "Upgrade { version } with security fixes available", + "system.updateStatus.security-update": "{ version } 버전으로 무료 보안 업데이트", + "system.updateStatus.security-upgrade": "{ version } 버전으로 보안 업그레이드", "system.updateStatus.unreleased": "출시 전 버전", "system.updateStatus.up-to-date": "최신 버전입니다.", "system.updateStatus.update": "{ version } 버전으로 무료 업데이트", "system.updateStatus.upgrade": "{ version } 버전으로 업그레이드", + "tel": "Phone", + "tel.placeholder": "+49123456789", + "template": "\ud15c\ud50c\ub9bf", + + "theme": "Theme", + "theme.light": "Lights on", + "theme.dark": "Lights off", + "theme.automatic": "Match system default", + "title": "제목", - "template": "템플릿", "today": "오늘", - "toolbar.button.code": "언어 코드", + "toolbar.button.clear": "서식 제거", + "toolbar.button.code": "코드", "toolbar.button.bold": "강조", "toolbar.button.email": "이메일 주소", "toolbar.button.headings": "제목", @@ -538,9 +721,11 @@ "toolbar.button.file": "파일", "toolbar.button.file.select": "파일 선택", "toolbar.button.file.upload": "파일 업로드", - "toolbar.button.link": "일반 링크", + "toolbar.button.link": "링크", "toolbar.button.paragraph": "문단", "toolbar.button.strike": "취소선", + "toolbar.button.sub": "아래 첨자", + "toolbar.button.sup": "위 첨자", "toolbar.button.ol": "숫자 목록", "toolbar.button.underline": "밑줄", "toolbar.button.ul": "기호 목록", @@ -550,6 +735,8 @@ "translation.name": "한국어", "translation.locale": "ko_KR", + "type": "유형", + "upload": "업로드", "upload.error.cantMove": "파일을 이동할 수 없습니다.", "upload.error.cantWrite": "디스크를 읽을 수 없습니다.", @@ -574,6 +761,7 @@ "user.changeLanguage": "언어 변경", "user.changeName": "사용자명 변경", "user.changePassword": "암호 변경", + "user.changePassword.current": "Your current password", "user.changePassword.new": "새 암호", "user.changePassword.new.confirm": "새 암호 확인", "user.changeRole": "역할 변경", @@ -585,17 +773,20 @@ "users": "사용자", "version": "버전", + "version.changes": "Changed version", + "version.compare": "Compare versions", "version.current": "현재 버전", "version.latest": "최신 버전", "versionInformation": "버전 정보", + "view": "View", "view.account": "계정", - "view.installation": "설치", + "view.installation": "\uc124\uce58", "view.languages": "언어", "view.resetPassword": "암호 초기화", "view.site": "사이트", "view.system": "시스템", - "view.users": "사용자", + "view.users": "\uc0ac\uc6a9\uc790", "welcome": "반갑습니다.", "year": "년", diff --git a/kirby/i18n/translations/lt.json b/kirby/i18n/translations/lt.json index 8407817..a6706c4 100644 --- a/kirby/i18n/translations/lt.json +++ b/kirby/i18n/translations/lt.json @@ -3,19 +3,28 @@ "account.delete": "Panaikinti savo paskyrą", "account.delete.confirm": "Ar tikrai norite panaikinti savo paskyrą? Jūs iš karto atsijungsite. Paskyros bus neįmanoma atstatyti.", + "activate": "Aktyvuoti", "add": "Pridėti", + "alpha": "Alpha", "author": "Autorius", "avatar": "Profilio nuotrauka", "back": "Atgal", "cancel": "Atšaukti", "change": "Keisti", "close": "Uždaryti", + "changes": "Changes", "confirm": "Ok", "collapse": "Sutraukti", "collapse.all": "Sutraukti viską", + "color": "Color", + "coordinates": "Coordinates", "copy": "Kopijuoti", "copy.all": "Kopijuoti visus", + "copy.success": "{count} nukopijuota!", + "copy.success.multiple": "{count} nukopijuota!", + "copy.url": "Copy URL", "create": "Sukurti", + "custom": "Custom", "date": "Data", "date.select": "Pasirinkite datą", @@ -34,26 +43,35 @@ "delete": "Pašalinti", "delete.all": "Pašalinti viską", + "dialog.fields.empty": "This dialog has no fields", "dialog.files.empty": "Nėra failų pasirinkimui", "dialog.pages.empty": "Nėra puslapių pasirinkimui", + "dialog.text.empty": "This dialog does not define any text", "dialog.users.empty": "Nėra vartotojų pasirinkimui", "dimensions": "Išmatavimai", + "disable": "Išjungti", "disabled": "Išjungta", "discard": "Atšaukti", + + "drawer.fields.empty": "This drawer has no fields", + + "domain": "Domenas", "download": "Parsisiųsti", "duplicate": "Dublikuoti", "edit": "Redaguoti", "email": "El. paštas", - "email.placeholder": "mail@example.com", + "email.placeholder": "info@pavyzdys.lt", + "enter": "Enter", "entries": "Įrašai", "entry": "Įrašas", "environment": "Aplinka", + "error": "Error", "error.access.code": "Neteisinas kodas", "error.access.login": "Neteisingas prisijungimo vardas", "error.access.panel": "Neturite teisės prisijungti prie valdymo pulto", @@ -74,13 +92,31 @@ "error.cache.type.invalid": "Invalid cache type \"{type}\"", + "error.content.lock.delete": "The version is locked and cannot be deleted", + "error.content.lock.move": "The source version is locked and cannot be moved", + "error.content.lock.publish": "This version is already published", + "error.content.lock.replace": "The version is locked and cannot be replaced", + "error.content.lock.update": "The version is locked and cannot be updated", + + "error.entries.max.plural": "You must not add more than {max} entries", + "error.entries.max.singular": "You must not add more than one entry", + "error.entries.min.plural": "You must add at least {min} entries", + "error.entries.min.singular": "You must add at least one entry", + "error.entries.supports": "\"{type}\" field type is not supported for the entries field", + "error.entries.validation": "There's an error on the \"{field}\" field in row {index}", + "error.email.preset.notFound": "El. pašto paruoštukas \"{name}\" nerastas", "error.field.converter.invalid": "Neteisingas konverteris \"{converter}\"", + "error.field.link.options": "Invalid options: {options}", "error.field.type.missing": "Laukelis \"{ name }\": Šio tipo (\"{ type }\") laukelis neegzistuoja", "error.file.changeName.empty": "Pavadinimas negali būti tuščias", "error.file.changeName.permission": "Neturite teisės pakeisti failo pavadinimo \"{filename}\"", + "error.file.changeTemplate.invalid": "The template for the file \"{id}\" cannot be changed to \"{template}\" (valid: \"{blueprints}\")", + "error.file.changeTemplate.permission": "You are not allowed to change the template for the file \"{id}\"", + + "error.file.delete.multiple": "Not all files could be deleted. Try each remaining file individually to see the specific error that prevents deletion.", "error.file.duplicate": "Failas su pavadinimu \"{filename}\" jau yra", "error.file.extension.forbidden": "Failo tipas (plėtinys) \"{extension}\" neleidžiamas", "error.file.extension.invalid": "Neteisingas plėtinys: {extension}", @@ -95,9 +131,11 @@ "error.file.minheight": "Failo aukštis turi būti bent {height} px", "error.file.minsize": "Failas per mažas", "error.file.minwidth": "Failo plotis turi būti bent {width} px", + "error.file.name.unique": "The filename must be unique", "error.file.name.missing": "Failo pavadinimas negali būti tuščias", "error.file.notFound": "Failas \"{filename}\" nerastas", "error.file.orientation": "Failo orientacija turi būti \"{orientation}\"", + "error.file.sort.permission": "You are not allowed to change the sorting of \"{filename}\"", "error.file.type.forbidden": "Jūs neturite teisės įkelti {type} tipo failų", "error.file.type.invalid": "Neteisingas failo tipas: {type}", "error.file.undefined": "Failas nerastas", @@ -106,22 +144,30 @@ "error.form.notSaved": "Formos nepavyko išsaugoti", "error.language.code": "Prašome įrašyti teisingą kalbos kodą", + "error.language.create.permission": "You are not allowed to create a language", + "error.language.delete.permission": "You are not allowed to delete the language", "error.language.duplicate": "Tokia kalba jau yra", "error.language.name": "Prašome įrašyti teisingą kalbos pavadinimą", "error.language.notFound": "Nepavyko rasti šios kalbos", + "error.language.update.permission": "You are not allowed to update the language", "error.layout.validation.block": "There's an error on the \"{field}\" field in block {blockIndex} using the \"{fieldset}\" block type in layout {layoutIndex}", "error.layout.validation.settings": "Yra klaida išdėstymo {index} nustatymuose", - "error.license.format": "Prašome įrašyti teisingą licenzijos kodą", + "error.license.domain": "Licencijai trūksta domeno", "error.license.email": "Prašome įrašyti teisingą el. pašto adresą", + "error.license.format": "Prašome įrašyti teisingą licencijos kodą", "error.license.verification": "Nepavyko patikrinti licenzijos", + "error.login.totp.confirm.invalid": "Neteisinas kodas", + "error.login.totp.confirm.missing": "Prašome įrašyti kodą", + "error.object.validation": "There’s an error in the \"{label}\" field:\n{message}", "error.offline": "Valdymo pultas dabar yra offline", "error.page.changeSlug.permission": "Neturite teisės pakeisti \"{slug}\" URL", + "error.page.changeSlug.reserved": "The path of top-level pages must not start with \"{path}\"", "error.page.changeStatus.incomplete": "Puslapis turi klaidų ir negali būti paskelbtas", "error.page.changeStatus.permission": "Šiam puslapiui negalima pakeisti statuso", "error.page.changeStatus.toDraft.invalid": "Puslapio \"{slug}\" negalima paversti juodraščiu", @@ -133,10 +179,18 @@ "error.page.delete": "Puslapio \"{slug}\" negalima pašalinti", "error.page.delete.confirm": "Įrašykite puslapio pavadinimą, tam kad patvirtintumėte", "error.page.delete.hasChildren": "Puslapis turi vidinių puslapių, dėl to negalima jo pašalinti", + "error.page.delete.multiple": "Not all pages could be deleted. Try each remaining page individually to see the specific error that prevents deletion.", "error.page.delete.permission": "Neturite leidimo šalinti \"{slug}\"", "error.page.draft.duplicate": "Puslapio juodraštis su URL pabaiga \"{slug}\" jau yra", "error.page.duplicate": "Puslapis su URL pabaiga \"{slug}\" jau yra", "error.page.duplicate.permission": "Neturite leidimo dubliuoti \"{slug}\"", + "error.page.move.ancestor": "Puslapio negalima perkelti į save patį", + "error.page.move.directory": "The page directory cannot be moved", + "error.page.move.duplicate": "A sub page with the URL appendix \"{slug}\" already exists", + "error.page.move.noSections": "The page \"{parent}\" cannot be a parent of any page because it lacks any pages sections in its blueprint", + "error.page.move.notFound": "The moved page could not be found", + "error.page.move.permission": "You are not allowed to move \"{slug}\"", + "error.page.move.template": "The \"{template}\" template is not accepted as a subpage of \"{parent}\"", "error.page.notFound": "Puslapis \"{slug}\" nerastas", "error.page.num.invalid": "Įrašykite teisingą eiliškumo numerį. Numeris negali būti neigiamas.", "error.page.slug.invalid": "Įrašykite teisingą URL priedą", @@ -163,6 +217,8 @@ "error.site.changeTitle.permission": "Neturite leidimo keisti svetainės pavadinimo", "error.site.update.permission": "Neturite leidimo atnaujinti svetainės", + "error.structure.validation": "There's an error on the \"{field}\" field in row {index}", + "error.template.default.notFound": "Nėra šablono pagal nutylėjimą", "error.unexpected": "An unexpected error occurred! Enable debug mode for more info: https://getkirby.com/docs/reference/system/options/debug", @@ -180,7 +236,7 @@ "error.user.delete.lastUser": "Vienintelio vartotojo negalima pašalinti", "error.user.delete.permission": "Neturite leidimo pašalinti vartotoją \"{name}\"", "error.user.duplicate": "Vartotojas su el. paštu \"{email}\" jau yra", - "error.user.email.invalid": "Prašome įrašyti teisingą el. pašto adresą", + "error.user.email.invalid": "Įrašykite teisingą el. pašto adresą", "error.user.language.invalid": "Įrašykite teisingą kalbą", "error.user.notFound": "Vartotojas \"{name}\" nerastas", "error.user.password.excessive": "Please enter a valid password. Passwords must not be longer than 1000 characters.", @@ -195,8 +251,10 @@ "error.validation.accepted": "Prašome patvirtinti", "error.validation.alpha": "Prašome įrašyti tik raides a-z", "error.validation.alphanum": "Prašome įrašyti tik raides a-z arba skaičius 0-9", + "error.validation.anchor": "Please enter a correct link anchor", "error.validation.between": "Prašome įrašyti reikšmę tarp \"{min}\" ir \"{max}\"", "error.validation.boolean": "Patvirtinkite arba atšaukite", + "error.validation.color": "Please enter a valid color in the {format} format", "error.validation.contains": "Prašome įrašyti reikšmę, kuri turėtų \"{needle}\"", "error.validation.date": "Prašome įrašyti korektišką datą", "error.validation.date.after": "Įrašykite datą nuo {date}", @@ -204,13 +262,14 @@ "error.validation.date.between": "Įrašykite datą tarp {min} ir {max}", "error.validation.denied": "Prašome neleisti", "error.validation.different": "Reikšmė neturi būti \"{other}\"", - "error.validation.email": "Prašome įrašyti teisingą el. pašto adresą", + "error.validation.email": "Prašome įrašyti korektišką el. paštą", "error.validation.endswith": "Reikšmė turi baigtis su \"{end}\"", "error.validation.filename": "Prašome įrašyti teisingą failo pavadinimą", "error.validation.in": "Prašome įrašyti vieną iš šių: ({in})", "error.validation.integer": "Prašome įrašyti teisingą sveiką skaičių", "error.validation.ip": "Prašome įrašyti teisingą IP adresą", "error.validation.less": "Prašome įrašyti mažiau nei {max}", + "error.validation.linkType": "The link type is not allowed", "error.validation.match": "Reikšmė nesutampa su laukiamu šablonu", "error.validation.max": "Prašome įrašyti reikšmę lygią arba didesnę, nei {max}", "error.validation.maxlength": "Prašome įrašyti trumpesnę reikšmę. (max. {max} characters)", @@ -227,15 +286,18 @@ "error.validation.same": "Prašome įrašyti \"{other}\"", "error.validation.size": "Reikšmės dydis turi būti \"{size}\"", "error.validation.startswith": "Reikšmė turi prasidėti su \"{start}\"", + "error.validation.tel": "Please enter an unformatted phone number", "error.validation.time": "Prašome įrašyti korektišką laiką", "error.validation.time.after": "Įrašykite laiką po {time}", "error.validation.time.before": "Įrašykite laiką prieš {time}", "error.validation.time.between": "Įrašykite laiką tarp {min} ir {max}", + "error.validation.uuid": "Please enter a valid UUID", "error.validation.url": "Prašome įrašyti teisingą URL", "expand": "Išskleisti", "expand.all": "Išskleisti viską", + "field.invalid": "The field is invalid", "field.required": "Laukas privalomas", "field.blocks.changeType": "Pakeisti tipą", "field.blocks.code.name": "Kodas", @@ -245,8 +307,9 @@ "field.blocks.delete.confirm.all": "Ar tikrai norite pašalinti visus blokus?", "field.blocks.delete.confirm.selected": "Ar tikrai norite pašalinti pasirinktus blokus?", "field.blocks.empty": "Dar nėra blokų", + "field.blocks.fieldsets.empty": "No fieldsets yet", "field.blocks.fieldsets.label": "Pasirinkite bloko tipą ...", - "field.blocks.fieldsets.paste": "Spauskite {{ shortcut }} įterpti/importuoti nukopijuotus blokus", + "field.blocks.fieldsets.paste": "Press {{ shortcut }} to import layouts/blocks from your clipboard Only those allowed in the current field will get inserted.", "field.blocks.gallery.name": "Galerija", "field.blocks.gallery.images.empty": "Dar nėra nuotraukų", "field.blocks.gallery.images.label": "Nuotraukos", @@ -254,11 +317,16 @@ "field.blocks.heading.name": "Antraštė", "field.blocks.heading.text": "Tekstas", "field.blocks.heading.placeholder": "Antraštė ...", + "field.blocks.figure.back.plain": "Plain", + "field.blocks.figure.back.pattern.light": "Pattern (light)", + "field.blocks.figure.back.pattern.dark": "Pattern (dark)", "field.blocks.image.alt": "Alternatyvus tekstas", "field.blocks.image.caption": "Aprašymas", "field.blocks.image.crop": "Kirpti", "field.blocks.image.link": "Nuoroda", "field.blocks.image.location": "Šaltinis", + "field.blocks.image.location.internal": "This website", + "field.blocks.image.location.external": "External source", "field.blocks.image.name": "Nuotrauka", "field.blocks.image.placeholder": "Pasirinkite nuotrauką", "field.blocks.image.ratio": "Proporcijos", @@ -275,38 +343,72 @@ "field.blocks.quote.citation.placeholder": "autorius", "field.blocks.text.name": "Tekstas", "field.blocks.text.placeholder": "Tekstas ...", + "field.blocks.video.autoplay": "Autoplay", "field.blocks.video.caption": "Aprašymas", + "field.blocks.video.controls": "Controls", + "field.blocks.video.location": "Šaltinis", + "field.blocks.video.loop": "Loop", + "field.blocks.video.muted": "Muted", "field.blocks.video.name": "Video", "field.blocks.video.placeholder": "Įrašykite video URL", + "field.blocks.video.poster": "Poster", + "field.blocks.video.preload": "Preload", "field.blocks.video.url.label": "Video-URL", "field.blocks.video.url.placeholder": "https://youtube.com/?v=", - "field.files.empty": "Pasirinkti", + "field.entries.delete.confirm.all": "Ar tikrai norite išrtinti visus įrašus?", + "field.entries.empty": "Dar nėra įrašų", + "field.files.empty": "Pasirinkti", + "field.files.empty.single": "No file selected yet", + + "field.layout.change": "Change layout", "field.layout.delete": "Pašalinti eilutę", "field.layout.delete.confirm": "Ar tikrai norite pašalinti šią eilutę", + "field.layout.delete.confirm.all": "Do you really want to delete all layouts?", "field.layout.empty": "Dar nėra eilučių", "field.layout.select": "Pasirinkite išdėstymą", "field.object.empty": "Dar nėra informacijos", "field.pages.empty": "Dar nėra puslapių", + "field.pages.empty.single": "No page selected yet", "field.structure.delete.confirm": "Ar tikrai norite pašalinti šią eilutę?", "field.structure.delete.confirm.all": "Ar tikrai norite išrtinti visus įrašus?", "field.structure.empty": "Dar nėra įrašų", "field.users.empty": "Dar nėra vartotojų", + "field.users.empty.single": "No user selected yet", + "fields.empty": "No fields yet", + + "file": "Failas", "file.blueprint": "Šis failas dar neturi blueprint. Galite nustatyti jį per /site/blueprints/files/{blueprint}.yml", + "file.changeTemplate": "Pakeisti šabloną", + "file.changeTemplate.notice": "Changing the file's template will remove content for fields that don't match in type. If the new template defines certain rules, e.g. image dimensions, those will also be applied irreversibly. Use with caution.", "file.delete.confirm": "Ar tikrai norite pašalinti
{filename}?", + "file.focus.placeholder": "Set focal point", + "file.focus.reset": "Remove focal point", + "file.focus.title": "Focus", "file.sort": "Pakeisti poziciją", "files": "Failai", + "files.delete.confirm.selected": "Do you really want to delete the selected files? This action cannot be undone.", "files.empty": "Įkelti", + "filter": "Filter", + + "form.discard": "Discard changes", + "form.discard.confirm": "Do you really want to discard all your changes?", + "form.locked": "This content is disabled for you as it is currently edited by another user", + "form.unsaved": "The current changes have not yet been saved", + "form.preview": "Preview changes", + "form.preview.draft": "Preview draft", + "hide": "Paslėpti", "hour": "Valanda", + "hue": "Hue", "import": "Importuoti", "info": "Info", "insert": "Įterpti", @@ -314,7 +416,7 @@ "insert.before": "Įterpti prieš", "install": "Įdiegti", - "installation": "Installation", + "installation": "Įdiegimas", "installation.completed": "Valdymo pultas įdiegtas", "installation.disabled": "Pagal nutylėjimą valdymo pulto įdiegimas viešuose serveriuose yra negalimas. Prašome įdiegti lokalioje aplinkoje arba įgalinkite jį su panel.install opcija.", "installation.issues.accounts": "Katalogas /site/accounts neegzistuoja arba neturi įrašymo teisių", @@ -324,7 +426,6 @@ "installation.issues.mbstring": "Plėtinys MB String yra privalomas", "installation.issues.media": "Katalogas /media neegzistuoja arba neturi įrašymo teisių", "installation.issues.php": "Įsitikinkite, kad naudojama PHP 8+", - "installation.issues.server": "Kirby reikalauja Apache, Nginx arba Caddy", "installation.issues.sessions": "Katalogas /site/sessions neegzistuoja arba neturi įrašymo teisių", "language": "Kalba", @@ -332,6 +433,7 @@ "language.convert": "Padaryti pagrindinį", "language.convert.confirm": "

Do you really want to convert {name} to the default language? This cannot be undone.

If {name} has untranslated content, there will no longer be a valid fallback and parts of your site might be empty.

", "language.create": "Pridėti naują kalbą", + "language.default": "Pagrindinė kalba", "language.delete.confirm": "Ar tikrai norite pašalinti {name} kalbą, kartu su visais vertimais? Grąžinti nebus įmanoma! 🙀", "language.deleted": "Kalba pašalinta", "language.direction": "Skaitymo kryptis", @@ -340,7 +442,16 @@ "language.locale": "PHP locale string", "language.locale.warning": "Jūs naudojate pasirinktinį lokalės nustatymą. Prašome pakeisti jį faile /site/languages", "language.name": "Pavadinimas", + "language.secondary": "Secondary language", + "language.settings": "Language settings", "language.updated": "Kalba atnaujinta", + "language.variables": "Language variables", + "language.variables.empty": "No translations yet", + + "language.variable.delete.confirm": "Do you really want to delete the variable for {key}?", + "language.variable.key": "Key", + "language.variable.notFound": "The variable could not be found", + "language.variable.value": "Value", "languages": "Kalbos", "languages.default": "Pagrindinė kalba", @@ -349,15 +460,32 @@ "languages.secondary.empty": "Dar nėra papildomų kalbų", "license": "Licenzija", + "license.activate": "Aktyvuoti dabar", + "license.activate.label": "Prašome aktyvuoti jūsų licenciją", + "license.activate.domain": "Jūsų licencija bus akvytuota šiam domenui: {host}", + "license.activate.local": "Jūs ruošiatės aktyvuoti jūsų Kirby licenciją vietiniam domenui {host}. Jei ši svetainė veiks su viešu domenu, aktyvuokite jį. Arba jei {host} yra tikrai tas domenas, kurį norite naudoti, galite tęsti.", + "license.activated": "Aktyvuota", "license.buy": "Pirkti licenziją", - "license.register": "Registruoti", + "license.code": "Kodas", + "license.code.help": "Jūs gavote licencijos kodą po pirkimo el. paštu. Nukopijuokite jį ir įterpkite čia.", + "license.code.label": "Prašome įrašyti jūsų licenzijos kodą", + "license.status.active.info": "Įeina naujos pagrindinės versijos iki {date}", + "license.status.active.label": "Galiojanti licencija", + "license.status.demo.info": "Tai demo versija", + "license.status.demo.label": "Demo", + "license.status.inactive.info": "Atnaujinkite licenciją, kad būtų galimybė atnaujinti iki naujų pagrindinių versijų", + "license.status.inactive.label": "Nėra naujų pagrindinių versijų", + "license.status.legacy.bubble": "Pasiruošę atnaujinti jūsų licenciją?", + "license.status.legacy.info": "Jūsų licencija negalioja šiai versijai", + "license.status.legacy.label": "Prašome atnaujinti jūsų licenciją", + "license.status.missing.bubble": "Pasiruošę paleisti naują svetainę?", + "license.status.missing.info": "Nėra galiojančios licencijos", + "license.status.missing.label": "Prašome aktyvuoti jūsų licenciją", + "license.status.unknown.info": "The license status is unknown", + "license.status.unknown.label": "Unknown", "license.manage": "Valdyti savo licencijas", - "license.register.help": "Licenzijos kodą gavote el. paštu po apmokėjimo. Prašome įterpti čia, kad sistema būtų užregistruota.", - "license.register.label": "Prašome įrašyti jūsų licenzijos kodą", - "license.register.domain": "Your license will be registered to {host}.", - "license.register.local": "You are about to register your license for your local domain {host}. If this site will be deployed to a public domain, please register it there instead. If {host} is the domain you want to license Kirby to, please continue.", - "license.register.success": "Ačiū, kad palaikote Kirby", - "license.unregistered": "Tai neregistruota Kirby demo versija", + "license.purchased": "Nupirkta", + "license.success": "Ačiū, kad palaikote Kirby", "license.unregistered.label": "Neregistruota", "link": "Nuoroda", @@ -367,17 +495,21 @@ "lock.unsaved": "Neišsaugoti pakeitimai", "lock.unsaved.empty": "Nebeliko neišsaugotų pakeitimų", - "lock.isLocked": "Vartotojo {email} neišsaugoti pakeitimai", - "lock.file.isLocked": "Šį failą dabar redaguoja kitas vartotojas {email}, tad jo negalima pekeisti.", - "lock.page.isLocked": "Šį puslapį dabar redaguoja kitas vartotojas {email}, tad jo negalima pekeisti.", + "lock.unsaved.files": "Unsaved files", + "lock.unsaved.pages": "Unsaved pages", + "lock.unsaved.users": "Unsaved accounts", + "lock.isLocked": "Unsaved changes by {email}", "lock.unlock": "Atrakinti", - "lock.isUnlocked": "Jūsų neišsaugoti pakeitimai buvo perrašyti kito vartotojo. Galite parsisiųsti savo pakeitimus ir įkelti juos rankiniu būdu.", + "lock.unlock.submit": "Unlock and overwrite unsaved changes by {email}", + "lock.isUnlocked": "Was unlocked by another user", "login": "Prisijungti", "login.code.label.login": "Prisijungimo kodas", "login.code.label.password-reset": "Slaptažodžio atstatymo kodas", "login.code.placeholder.email": "000 000", + "login.code.placeholder.totp": "000000", "login.code.text.email": "Jei jūsų el. paštas yra užregistruotas, užklaistas kodas buvo išsiųstas el. paštu.", + "login.code.text.totp": "Prašome įrašyti vienkartinį kodą iš jūsų autentifikavimo programėlės.", "login.email.login.body": "Sveiki, {user.nameOrEmail},\n\nNeseniai užklausėte prisijungimo kodo svetainėje {site}.\nŠis kodas galios {timeout} min.:\n\n{code}\n\nJei neprašėte šio kodo, tiesiog ignoruokite, arba susisiekite su administratoriumi.\nDėl saugumo, prašome NEPERSIŲSTI šio laiško.", "login.email.login.subject": "Jūsų prisijungimo kodas", "login.email.password-reset.body": "Sveiki, {user.nameOrEmail},\n\nNeseniai užklausėte naujo slaptažodžio kūrimo kodo svetainėje {site}.\nŠis kodas galios {timeout} min.:\n\n{code}\n\nJei neprašėte šio kodo, tiesiog ignoruokite, arba susisiekite su administratoriumi.\nDėl saugumo, prašome NEPERSIŲSTI šio laiško", @@ -388,9 +520,24 @@ "login.toggleText.code.email-password": "Prisijungti su slaptažodžiu", "login.toggleText.password-reset.email": "Pamiršote slaptažodį?", "login.toggleText.password-reset.email-password": "← Atgal į prisijungimą", + "login.totp.enable.option": "Nustatyti vienkartinius kodus", + "login.totp.enable.intro": "Autentifikavimo programėlės gali generuoti vienkartinius kodus, kurie bus naudojami kaip 2-factor prisijungiant prie svetainės.", + "login.totp.enable.qr.label": "1. Nuskenuokite šį QR kodą", + "login.totp.enable.qr.help": "Negalite nuskenuoti? Pridėkite raktą {secret} rankiniu būdu į savo autentifikavimo programėlę.", + "login.totp.enable.confirm.headline": "2. Patvirtinti su sugeneruotu kodu", + "login.totp.enable.confirm.text": "Jūsų programėlė generuoja naują vienkartinį kodą kas 30 sekundžių. Įrašykite dabartinį kodą, norėdami užbaigti:", + "login.totp.enable.confirm.label": "Dabartinis kodas", + "login.totp.enable.confirm.help": "Po šio nustatymo, iš jūsų bus prašomas vienkartinis kodas jungiantis kiekvieną kartą.", + "login.totp.enable.success": "Vienkartiniai kodai įjungti", + "login.totp.disable.option": "Išjungti vienkartinius kodus", + "login.totp.disable.label": "Įrašykite savo slaptažodį norėdami išjungti vienkartinius kodus", + "login.totp.disable.help": "Ateityje kitoks 2-factor bus prašomas prisijungiant, pvz. login kodas, siunčiamas el. paštu. Jūs galite visada nustatyti vienkartinius kodus vėl vėliau.", + "login.totp.disable.admin": "

Tai išjungs vienkartinius kodus vartotojui {user}. Ateityje kitoks 2-factor bus prašomas prisijungiant, pvz. login kodas, siunčiamas el. paštu. Jūs galite visada nustatyti vienkartinius kodus vėl vėliau. {user} galės nustatyti vienkartinius kodus, kai jungsis kitą kartą.", + "login.totp.disable.success": "Vienkartiniai kodai išjungti", "logout": "Atsijungti", + "merge": "Merge", "menu": "Meniu", "meridiem": "AM/PM", "mime": "Media Tipas", @@ -411,21 +558,26 @@ "months.september": "Rugsėjis", "more": "Daugiau", + "move": "Move", "name": "Pavadinimas", "next": "Toliau", + "night": "Night", "no": "ne", "off": "ne", "on": "taip", "open": "Atidaryti", "open.newWindow": "Atidaryti naujame lange", + "option": "Option", "options": "Pasirinkimai", "options.none": "Nėra pasirinkimų", + "options.all": "Rodyti visas {count} opcijas", "orientation": "Orientacija", "orientation.landscape": "Horizontali", "orientation.portrait": "Portretas", "orientation.square": "Kvadratas", + "page": "Puslapis", "page.blueprint": "Šis puslapis dar neturi blueprint. Galite jį nustatyti per /site/blueprints/pages/{blueprint}.yml", "page.changeSlug": "Pakeisti URL", "page.changeSlug.fromTitle": "Sukurti URL pagal pavadinimą", @@ -433,13 +585,15 @@ "page.changeStatus.position": "Pasirinkite poziciją", "page.changeStatus.select": "Pasirinkite statusą", "page.changeTemplate": "Pakeisti šabloną", + "page.changeTemplate.notice": "Changing the page's template will remove content for fields that don't match in type. Use with caution.", + "page.create": "Sukurti kaip {status}", "page.delete.confirm": "🙀 Ar tikrai norite pašalinti puslapį {title}?", "page.delete.confirm.subpages": "Šis puslapis turi sub-puslapių.
Visi sub-puslapiai taip pat bus pašalinti.", "page.delete.confirm.title": "Įrašykite puslapio pavadinimą tam, kad patvirtinti", - "page.draft.create": "Sukurti juodraštį", "page.duplicate.appendix": "Kopijuoti", "page.duplicate.files": "Kopijuoti failus", "page.duplicate.pages": "Kopijuoti puslapius", + "page.move": "Perkelti puslapį", "page.sort": "Pakeisti poziciją", "page.status": "Statusas", "page.status.draft": "Juodraštis", @@ -450,24 +604,32 @@ "page.status.unlisted.description": "Rodomas viešai visiems, bet tik per URL", "pages": "Puslapiai", + "pages.delete.confirm.selected": "Do you really want to delete the selected pages? This action cannot be undone.", "pages.empty": "Dar nėra puslapių", "pages.status.draft": "Juodraščiai", "pages.status.listed": "Paskelbti", - "pages.status.unlisted": "Nerodomas", + "pages.status.unlisted": "Nerodomi", "pagination.page": "Puslapis", "password": "Slaptažodis", "paste": "Įterpti", "paste.after": "Įterpti po", + "paste.success": "{count} pasted!", "pixel": "Pikselis", "plugin": "Įskiepas", "plugins": "Įskiepai", "prev": "Ankstesnis", "preview": "Peržiūra", + + "publish": "Publish", + "published": "Paskelbti", + "remove": "Pašalinti", "rename": "Pervadinti", + "renew": "Atnaujinti", "replace": "Apkeisti", + "replace.with": "Replace with", "retry": "Bandyti dar", "revert": "Grąžinti", "revert.confirm": "Ar tikrai norite atšaukti visus neišsaugotus pakeitimus?", @@ -482,11 +644,14 @@ "role.nobody.title": "Niekas", "save": "Išsaugoti", + "saved": "Saved", "search": "Ieškoti", + "searching": "Searching", "search.min": "Minimalus simbolių kiekis paieškai: {min}", - "search.all": "Rodyti viską", + "search.all": "Parodyti visus {count} rezultatus", "search.results.none": "Nėra rezultatų", + "section.invalid": "The section is invalid", "section.required": "Sekcija privaloma", "security": "Saugumas", @@ -498,16 +663,25 @@ "size": "Dydis", "slug": "URL pabaiga", "sort": "Rikiuoti", + "sort.drag": "Drag to sort …", + "split": "Split", "stats.empty": "Nėra pranešimų", + "status": "Statusas", + + "system.info.copy": "Copy info", + "system.info.copied": "System info copied", "system.issues.content": "The content folder seems to be exposed", "system.issues.eol.kirby": "Your installed Kirby version has reached end-of-life and will not receive further security updates", "system.issues.eol.plugin": "Your installed version of the { plugin } plugin is has reached end-of-life and will not receive further security updates", + "system.issues.eol.php": "Jūsų PHP versija { release } pasiekė gyvenimo galą ir daugiau negaus saugumo atnaujinimų", "system.issues.debug": "Debugging must be turned off in production", "system.issues.git": "The .git folder seems to be exposed", "system.issues.https": "Rekomenduojame HTTPS visoms svetainėms", "system.issues.kirby": "The kirby folder seems to be exposed", + "system.issues.local": "The site is running locally with relaxed security checks", "system.issues.site": "The site folder seems to be exposed", + "system.issues.vue.compiler": "The Vue template compiler is enabled", "system.issues.vulnerability.kirby": "Your installation might be affected by the following vulnerability ({ severity } severity): { description }", "system.issues.vulnerability.plugin": "Your installation might be affected by the following vulnerability in the { plugin } plugin ({ severity } severity): { description }", "system.updateStatus": "Atnaujinimų statusas", @@ -520,10 +694,19 @@ "system.updateStatus.update": "Prieinamas nemokamas atnaujinimas { version }", "system.updateStatus.upgrade": "Prieinamas atnaujinimas { version }", - "title": "Pavadinimas", + "tel": "Phone", + "tel.placeholder": "+49123456789", "template": "Puslapio šablonas", + + "theme": "Theme", + "theme.light": "Lights on", + "theme.dark": "Lights off", + "theme.automatic": "Match system default", + + "title": "Pavadinimas", "today": "Šiandien", + "toolbar.button.clear": "Clear formatting", "toolbar.button.code": "Kodas", "toolbar.button.bold": "Bold", "toolbar.button.email": "El. paštas", @@ -541,6 +724,8 @@ "toolbar.button.link": "Nuoroda", "toolbar.button.paragraph": "Paragrafas", "toolbar.button.strike": "Perbraukimas", + "toolbar.button.sub": "Subscript", + "toolbar.button.sup": "Superscript", "toolbar.button.ol": "Sąrašas su skaičiais", "toolbar.button.underline": "Pabraukimas", "toolbar.button.ul": "Sąrašas su taškais", @@ -550,6 +735,8 @@ "translation.name": "Lietuvių", "translation.locale": "lt_LT", + "type": "Type", + "upload": "Įkelti", "upload.error.cantMove": "Įkeltas failas negali būti perkeltas", "upload.error.cantWrite": "Nepavyko įrašyti failo į diską", @@ -562,7 +749,7 @@ "upload.error.noFiles": "Failai nebuvo įkelti", "upload.error.partial": "Failas įkeltas tik iš dalies", "upload.error.tmpDir": "Trūksta laikinojo katalogo", - "upload.errors": "Error", + "upload.errors": "Klaida", "upload.progress": "Įkėlimas…", "url": "Url", @@ -574,6 +761,7 @@ "user.changeLanguage": "Keisti kalbą", "user.changeName": "Pervadinti vartotoją", "user.changePassword": "Keisti slaptažodį", + "user.changePassword.current": "Your current password", "user.changePassword.new": "Naujas slaptažodis", "user.changePassword.new.confirm": "Patvirtinti naują slaptažodį…", "user.changeRole": "Keisti rolę", @@ -585,10 +773,13 @@ "users": "Vartotojai", "version": "Versija", + "version.changes": "Changed version", + "version.compare": "Compare versions", "version.current": "Dabartinė versija", "version.latest": "Naujausia versija", "versionInformation": "Versijos informacija", + "view": "View", "view.account": "Jūsų paskyra", "view.installation": "Installation", "view.languages": "Kalbos", diff --git a/kirby/i18n/translations/nb.json b/kirby/i18n/translations/nb.json index 3272036..236e105 100644 --- a/kirby/i18n/translations/nb.json +++ b/kirby/i18n/translations/nb.json @@ -3,19 +3,28 @@ "account.delete": "Slett kontoen din", "account.delete.confirm": "Er du sikker på at du vil slette kontoen din? Du vil bli logget ut umiddelbart. Kontoen din kan ikke gjenopprettes.", + "activate": "Aktiver", "add": "Legg til", + "alpha": "Alfa", "author": "Forfatter", "avatar": "Profilbilde", "back": "Tilbake", "cancel": "Avbryt", "change": "Endre", "close": "Lukk", + "changes": "Endringer", "confirm": "Lagre", "collapse": "Skjul", "collapse.all": "Skjule alle", + "color": "Farge", + "coordinates": "Koordinater", "copy": "Kopier", "copy.all": "Kopier alle", + "copy.success": "{count} kopiert!", + "copy.success.multiple": "{count} copied!", + "copy.url": "Copy URL", "create": "Opprett", + "custom": "Egendefinert", "date": "Dato", "date.select": "Velg dato", @@ -23,8 +32,8 @@ "day": "Dag", "days.fri": "Fre", "days.mon": "Man", - "days.sat": "Lør", - "days.sun": "Søn", + "days.sat": "L\u00f8r", + "days.sun": "S\u00f8n", "days.thu": "Tor", "days.tue": "Tir", "days.wed": "Ons", @@ -34,13 +43,20 @@ "delete": "Slett", "delete.all": "Slett alle", + "dialog.fields.empty": "Denne dialogen har ingen felter", "dialog.files.empty": "Ingen filer å velge", "dialog.pages.empty": "Ingen sider å velge", + "dialog.text.empty": "Denne dialogen definerer ingen tekst", "dialog.users.empty": "Ingen brukere å velge", "dimensions": "Dimensjoner", + "disable": "Deaktivere", "disabled": "Deaktivert", "discard": "Forkast", + + "drawer.fields.empty": "Denne skuffen har ingen felt", + + "domain": "Domene", "download": "Last ned", "duplicate": "Dupliser", @@ -49,11 +65,13 @@ "email": "Epost", "email.placeholder": "epost@eksempel.no", - "entries": "Entries", - "entry": "Entry", + "enter": "Enter", + "entries": "Artikler", + "entry": "Artikkel", "environment": "Miljø", + "error": "Feil", "error.access.code": "Ugyldig kode", "error.access.login": "Ugyldig innlogging", "error.access.panel": "Du har ikke tilgang til panelet", @@ -70,17 +88,35 @@ "error.blocks.max.singular": "Du kan ikke legge til mer enn en blokk", "error.blocks.min.plural": "Du må legge til minst {min} blokker", "error.blocks.min.singular": "Du må legge til minst en blokk", - "error.blocks.validation": "There's an error on the \"{field}\" field in block {index} using the \"{fieldset}\" block type", + "error.blocks.validation": "Det er en feil med feltet \"{field}\" i blokk {index} hvor blokktypen \"{fieldset}\" brukes", - "error.cache.type.invalid": "Invalid cache type \"{type}\"", + "error.cache.type.invalid": "Ugyldig type cache \"{type}\"", + + "error.content.lock.delete": "The version is locked and cannot be deleted", + "error.content.lock.move": "The source version is locked and cannot be moved", + "error.content.lock.publish": "This version is already published", + "error.content.lock.replace": "The version is locked and cannot be replaced", + "error.content.lock.update": "The version is locked and cannot be updated", + + "error.entries.max.plural": "You must not add more than {max} entries", + "error.entries.max.singular": "You must not add more than one entry", + "error.entries.min.plural": "You must add at least {min} entries", + "error.entries.min.singular": "You must add at least one entry", + "error.entries.supports": "\"{type}\" field type is not supported for the entries field", + "error.entries.validation": "Det er en feilmelding på felt \"{field}\" i rad {index}", "error.email.preset.notFound": "E-postinnstillingen \"{name}\" ble ikke funnet", "error.field.converter.invalid": "Ugyldig omformer \"{converter}\"", - "error.field.type.missing": "Field \"{ name }\": The field type \"{ type }\" does not exist", + "error.field.link.options": "Invalid options: {options}", + "error.field.type.missing": "Felt \"{ name }\": Felttypen \"{ type }\" finnes ikke", "error.file.changeName.empty": "Navnet kan ikke være tomt", "error.file.changeName.permission": "Du har ikke rettighet til å endre navnet til \"{filename}\"", + "error.file.changeTemplate.invalid": "Malen for filen \"{id}\" Kan ikke endres til \"{template}\" (gyldig: \"{blueprints}\")", + "error.file.changeTemplate.permission": "Du har ikke rettigheter til å endre malen for filen \"{id}\"", + + "error.file.delete.multiple": "Not all files could be deleted. Try each remaining file individually to see the specific error that prevents deletion.", "error.file.duplicate": "En fil med navnet \"{filename}\" eksisterer allerede", "error.file.extension.forbidden": "Ugyldig filtype", "error.file.extension.invalid": "Ugyldig utvidelse: {extension}", @@ -95,9 +131,11 @@ "error.file.minheight": "Høyden til bildet må være minst {height} piksler", "error.file.minsize": "Filen er for liten", "error.file.minwidth": "Bredden til bildet må være minst {width} piksler", + "error.file.name.unique": "The filename must be unique", "error.file.name.missing": "Filnavnet kan ikke være tomt", - "error.file.notFound": "Finner ikke filen", + "error.file.notFound": "Filen \"{filename}\" kan ikke bli funnet", "error.file.orientation": "Bilderetningen må være \"{orientation}\"", + "error.file.sort.permission": "You are not allowed to change the sorting of \"{filename}\"", "error.file.type.forbidden": "Du har ikke lov til å laste opp filer av typen {type}", "error.file.type.invalid": "Ugyldig filtype: {type}", "error.file.undefined": "Finner ikke filen", @@ -106,22 +144,30 @@ "error.form.notSaved": "Skjemaet kunne ikke lagres", "error.language.code": "Vennligst skriv inn gyldig språkkode", + "error.language.create.permission": "You are not allowed to create a language", + "error.language.delete.permission": "You are not allowed to delete the language", "error.language.duplicate": "Språket eksisterer allerede", "error.language.name": "Vennligst skriv inn et gyldig navn for språket", "error.language.notFound": "Finner ikke språket", + "error.language.update.permission": "You are not allowed to update the language", - "error.layout.validation.block": "There's an error on the \"{field}\" field in block {blockIndex} using the \"{fieldset}\" block type in layout {layoutIndex}", + "error.layout.validation.block": "Det er en feilmelding på \"{field}\" feltet i blokk {blockIndex} med bruk av \"{fieldset}\" blokktypen i layout {layoutIndex}", "error.layout.validation.settings": "Det er en feil i layout {index} innstillinger", - "error.license.format": "Vennligst skriv inn gyldig lisensnøkkel", + "error.license.domain": "Domenen for lisensen mangler", "error.license.email": "Vennligst skriv inn en gyldig e-postadresse", + "error.license.format": "Please enter a valid license code", "error.license.verification": "Lisensen kunne ikke verifiseres", - "error.object.validation": "There’s an error in the \"{label}\" field:\n{message}", + "error.login.totp.confirm.invalid": "Ugyldig kode", + "error.login.totp.confirm.missing": "Vennligst skriv inn nåværende koden", + + "error.object.validation": "Det er en feilmelding i \"{label}\" feltet:\n{message}", "error.offline": "Panelet er i øyeblikket offline", "error.page.changeSlug.permission": "Du kan ikke endre URLen for denne siden", + "error.page.changeSlug.reserved": "Stien til toppnivåsider kan ikke starte med \"{path}\"", "error.page.changeStatus.incomplete": "Siden har feil og kan ikke publiseres", "error.page.changeStatus.permission": "Sidens status kan ikke endres", "error.page.changeStatus.toDraft.invalid": "Siden \"{slug}\" kan ikke konverteres til et utkast", @@ -133,10 +179,18 @@ "error.page.delete": "Siden \"{slug}\" kan ikke slettes", "error.page.delete.confirm": "Vennligst skriv inn sidens tittel for å bekrefte", "error.page.delete.hasChildren": "Siden har undersider og kan derfor ikke slettes", + "error.page.delete.multiple": "Not all pages could be deleted. Try each remaining page individually to see the specific error that prevents deletion.", "error.page.delete.permission": "Du har ikke til å slette \"{slug}\"", "error.page.draft.duplicate": "Et sideutkast med URL-tillegget \"{slug}\" eksisterer allerede", "error.page.duplicate": "En side med URL-tillegget \"{slug}\" eksisterer allerede", "error.page.duplicate.permission": "Du har ikke tillatelse til å duplisere \"{slug}\"", + "error.page.move.ancestor": "Siden kan ikke flyttes inn i seg selv", + "error.page.move.directory": "Sidestrukturen kan ikke flyttes", + "error.page.move.duplicate": "En underside med url banen \"{slug}\" finnes allerede", + "error.page.move.noSections": "The page \"{parent}\" cannot be a parent of any page because it lacks any pages sections in its blueprint", + "error.page.move.notFound": "Den flyttede siden kan ikke bli funnet", + "error.page.move.permission": "You are not allowed to move \"{slug}\"", + "error.page.move.template": "\"{template}\" malen er ikke akseptert som underside av \"{parent}\"", "error.page.notFound": "Siden \"{slug}\" ble ikke funnet", "error.page.num.invalid": "Vennligst skriv inn et gyldig sorteringsnummer. Tallet må ikke være negativt.", "error.page.slug.invalid": "Vennligst skriv inn en gyldig URL endelse", @@ -163,6 +217,8 @@ "error.site.changeTitle.permission": "Du har ikke tillatelse til å endre tittel på siden", "error.site.update.permission": "Du har ikke tillatelse til å oppdatere denne siden", + "error.structure.validation": "Det er en feilmelding på felt \"{field}\" i rad {index}", + "error.template.default.notFound": "Standardmalen eksisterer ikke", "error.unexpected": "En uventet feil oppstod! Aktiver feilsøkmodus for mer info: https://getkirby.com/docs/reference/system/options/debug", @@ -178,7 +234,7 @@ "error.user.delete": "Denne brukeren kunne ikke bli slettet", "error.user.delete.lastAdmin": "Siste administrator kan ikke slettes", "error.user.delete.lastUser": "Den siste brukeren kan ikke slettes", - "error.user.delete.permission": "Du er ikke tillat å slette denne brukeren", + "error.user.delete.permission": "Du er ikke tillat \u00e5 slette denne brukeren", "error.user.duplicate": "En bruker med e-postadresse \"{email}\" eksisterer allerede", "error.user.email.invalid": "Vennligst skriv inn en gyldig e-postadresse", "error.user.language.invalid": "Vennligst skriv inn et gyldig språk", @@ -195,8 +251,10 @@ "error.validation.accepted": "Vennligst bekreft", "error.validation.alpha": "Vennligst skriv kun tegn mellom a-z", "error.validation.alphanum": "Vennligst skriv kun tegn mellom a-z eller tall mellom 0-9", + "error.validation.anchor": "Vennligst skriv inn en riktig link-ankertekst", "error.validation.between": "Vennligst angi en verdi mellom \"{min}\" og \"{max}\"", "error.validation.boolean": "Vennligst bekreft eller avslå", + "error.validation.color": "Please enter a valid color in the {format} format", "error.validation.contains": "Vennligst skriv inn en verdi som inneholder \"{needle}\"", "error.validation.date": "Vennligst skriv inn en gyldig dato", "error.validation.date.after": "Vennligst angi en dato etter {date}", @@ -211,6 +269,7 @@ "error.validation.integer": "Vennligst skriv inn et gyldig tall", "error.validation.ip": "Vennligst skriv inn en gyldig IP-adresse", "error.validation.less": "Vennligst angi en verdi lavere enn {max}", + "error.validation.linkType": "The link type is not allowed", "error.validation.match": "Verdien samsvarer ikke med det forventede mønsteret", "error.validation.max": "Vennligst angi en verdi lik eller lavere enn {max}", "error.validation.maxlength": "Vennligst angi en kortere verdi. (maks. {max} tegn)", @@ -227,15 +286,18 @@ "error.validation.same": "Vennligst angi \"{other}\"", "error.validation.size": "Størrelsen på verdien må være \"{size}\"", "error.validation.startswith": "Verdien må starte med \"{start}\"", + "error.validation.tel": "Please enter an unformatted phone number", "error.validation.time": "Vennligst angi et gyldig tidspunkt", "error.validation.time.after": "Vennligst angi et tidspunkt etter {time}", "error.validation.time.before": "Vennligst angi et tidspunkt før {time}", "error.validation.time.between": "Vennligst angi et tidspunkt mellom {min} og {max}", + "error.validation.uuid": "Please enter a valid UUID", "error.validation.url": "Vennligst skriv inn en gyldig URL", "expand": "Utvid", "expand.all": "Utvid alle", + "field.invalid": "The field is invalid", "field.required": "Feltet er påkrevd", "field.blocks.changeType": "Endre type", "field.blocks.code.name": "Kode", @@ -245,8 +307,9 @@ "field.blocks.delete.confirm.all": "Er du sikker på at du vil slette alle blokkene?", "field.blocks.delete.confirm.selected": "Er du sikker på at du vil slette de valgte blokkene?", "field.blocks.empty": "Ingen blokker enda", + "field.blocks.fieldsets.empty": "Ingen feltsett enda", "field.blocks.fieldsets.label": "Vennligst velg en blokktype…", - "field.blocks.fieldsets.paste": "Trykk {{ shortcut }} for å lime/importere blokker fra din utklippstavle", + "field.blocks.fieldsets.paste": "Trykk {{ shortcut }} for å importere layout/blokker fra utklippsverktøyet Bare de som er tillat i nåværende felt vil bli limt inn.", "field.blocks.gallery.name": "Galleri", "field.blocks.gallery.images.empty": "Ingen bilder enda", "field.blocks.gallery.images.label": "Bilder", @@ -254,11 +317,16 @@ "field.blocks.heading.name": "Overskrift", "field.blocks.heading.text": "Tekst", "field.blocks.heading.placeholder": "Overskrift…", + "field.blocks.figure.back.plain": "Plain", + "field.blocks.figure.back.pattern.light": "Pattern (light)", + "field.blocks.figure.back.pattern.dark": "Pattern (dark)", "field.blocks.image.alt": "Alternativ tekst", "field.blocks.image.caption": "Caption", "field.blocks.image.crop": "Beskjær", "field.blocks.image.link": "Adresse", "field.blocks.image.location": "Plassering", + "field.blocks.image.location.internal": "Denne nettsiden", + "field.blocks.image.location.external": "Ekstern kilde", "field.blocks.image.name": "Bilde", "field.blocks.image.placeholder": "Velg et bilde", "field.blocks.image.ratio": "Ratio", @@ -275,38 +343,72 @@ "field.blocks.quote.citation.placeholder": "av…", "field.blocks.text.name": "Tekst", "field.blocks.text.placeholder": "Tekst…", + "field.blocks.video.autoplay": "Autoplay", "field.blocks.video.caption": "Caption", + "field.blocks.video.controls": "Controls", + "field.blocks.video.location": "Plassering", + "field.blocks.video.loop": "Loop", + "field.blocks.video.muted": "Muted", "field.blocks.video.name": "Video", "field.blocks.video.placeholder": "Legg til en video URL", + "field.blocks.video.poster": "Poster", + "field.blocks.video.preload": "Preload", "field.blocks.video.url.label": "Video-URL", "field.blocks.video.url.placeholder": "https://youtube.com/?v=", - "field.files.empty": "Ingen filer har blitt valgt", + "field.entries.delete.confirm.all": "Vil du virkelig slette alle oppføringer?", + "field.entries.empty": "Ingen oppføringer enda", + "field.files.empty": "Ingen filer har blitt valgt", + "field.files.empty.single": "No file selected yet", + + "field.layout.change": "Endre layout", "field.layout.delete": "Slett layout", "field.layout.delete.confirm": "Er du sikker på at du vil slette denne layouten?", + "field.layout.delete.confirm.all": "Vil du virkelig slette alle layout?", "field.layout.empty": "Ingen rader enda", "field.layout.select": "Velg en layout", - "field.object.empty": "No information yet", + "field.object.empty": "Ingen informasjon enda", "field.pages.empty": "Ingen side har blitt valgt", + "field.pages.empty.single": "No page selected yet", - "field.structure.delete.confirm": "Ønsker du virkelig å slette denne oppføringen?", - "field.structure.delete.confirm.all": "Do you really want to delete all entries?", - "field.structure.empty": "Ingen oppføringer enda", + "field.structure.delete.confirm": "\u00d8nsker du virkelig \u00e5 slette denne oppf\u00f8ringen?", + "field.structure.delete.confirm.all": "Vil du virkelig slette alle oppføringer?", + "field.structure.empty": "Ingen oppf\u00f8ringer enda", "field.users.empty": "Ingen bruker har blitt valgt", + "field.users.empty.single": "No user selected yet", + "fields.empty": "Ingen felt enda", + + "file": "Fil", "file.blueprint": "Denne filen har ikke en blueprint enda. Du kan definere oppsettet i /site/blueprints/files/{blueprint}.yml", + "file.changeTemplate": "Endre mal", + "file.changeTemplate.notice": "Endring av denne filens mal kommer til å fjerne innhold for felter som ikke korresponderer med typen. Dersom den nye malen inneholder gitte regler, f.eks bildedimensjoner, vil også disse bli påført irreversibelt. Bruk varsomt.", "file.delete.confirm": "Vil du virkelig slette denne filen?", + "file.focus.placeholder": "Sett fokuspunkt", + "file.focus.reset": "Fjern fokuspunkt", + "file.focus.title": "Focus", "file.sort": "Endre plassering", "files": "Filer", + "files.delete.confirm.selected": "Do you really want to delete the selected files? This action cannot be undone.", "files.empty": "Ingen filer ennå", + "filter": "Filter", + + "form.discard": "Discard changes", + "form.discard.confirm": "Do you really want to discard all your changes?", + "form.locked": "This content is disabled for you as it is currently edited by another user", + "form.unsaved": "The current changes have not yet been saved", + "form.preview": "Preview changes", + "form.preview.draft": "Preview draft", + "hide": "Skjul", "hour": "Tid", + "hue": "Hue", "import": "Importer", "info": "Info", "insert": "Sett Inn", @@ -317,21 +419,21 @@ "installation": "Installasjon", "installation.completed": "Panelet har blitt installert", "installation.disabled": "Installasjonsprogrammet for Panelet er deaktivert på offentlige servere som standard. Vennligst kjør installasjonsprogrammet på en lokal maskin eller aktiver den med panel.install innstillingen.", - "installation.issues.accounts": "/site/accounts er ikke skrivbar", - "installation.issues.content": "Mappen content og alt av innhold må være skrivbar.", + "installation.issues.accounts": "\/site\/accounts er ikke skrivbar", + "installation.issues.content": "Mappen content og alt av innhold m\u00e5 v\u00e6re skrivbar.", "installation.issues.curl": "Utvidelsen CURL er nødvendig", "installation.issues.headline": "Panelet kan ikke installeres", "installation.issues.mbstring": "Utvidelsen MB String er nødvendig", "installation.issues.media": "Mappen /media eksisterer ikke eller er ikke skrivbar", "installation.issues.php": "Pass på at du bruker PHP 8+", - "installation.issues.server": "Kirby krever Apache, Nginx eller Caddy", "installation.issues.sessions": "Mappen /site/sessions eksisterer ikke eller er ikke skrivbar", - "language": "Språk", + "language": "Spr\u00e5k", "language.code": "Kode", "language.convert": "Gjør til standard", "language.convert.confirm": "

Vil du virkelig konvertere {name} til standardspråk? Dette kan ikke angres.

Dersom {name} har innhold som ikke er oversatt, vil nettstedet mangle innhold å falle tilbake på. Dette kan resultere i at deler av nettstedet fremstår som tomt.

", "language.create": "Legg til språk", + "language.default": "Standardspråk", "language.delete.confirm": "Vil du virkelig slette språket {name} inkludert alle oversettelser? Dette kan ikke angres!", "language.deleted": "Språket har blitt slettet", "language.direction": "Leseretning", @@ -340,7 +442,16 @@ "language.locale": "PHP locale streng", "language.locale.warning": "Du bruker et egendefinert lokalt oppsett. Vennligst endre det i språkfilen i /site/languages", "language.name": "Navn", + "language.secondary": "Sekundærspråk", + "language.settings": "Språkinstillinger", "language.updated": "Språk har blitt oppdatert", + "language.variables": "Språkvariabler", + "language.variables.empty": "Ingen oversettelse enda", + + "language.variable.delete.confirm": "Ønsker du virkelig å slette variablen for {key}?", + "language.variable.key": "Key", + "language.variable.notFound": "Variablen kan ikke bli funnet", + "language.variable.value": "Verdi", "languages": "Språk", "languages.default": "Standardspråk", @@ -349,16 +460,33 @@ "languages.secondary.empty": "Det er ingen andre språk ennå", "license": "Kirby lisens", + "license.activate": "Aktiver den nå", + "license.activate.label": "Vennligst skriv inn din lisenskode", + "license.activate.domain": "Lisenses skal bli aktivert for {host}.", + "license.activate.local": "Du er i ferd med å aktivere Kirby lisensen din til lokale domenen din {host}. Hvis nettsiden skal plasseres til en offentlig domene, vennligst aktivere den der isteden. Hvis {host} er domenen du vil bruke med din lisens, vennligst fortsett.", + "license.activated": "Aktivert", "license.buy": "Kjøp lisens", - "license.register": "Registrer", - "license.manage": "Manage your licenses", - "license.register.help": "Du skal ha mottatt din lisenskode for kjøpet via e-post. Vennligst kopier og lim inn denne for å registrere deg.", - "license.register.label": "Vennligst skriv inn din lisenskode", - "license.register.domain": "Your license will be registered to {host}.", - "license.register.local": "You are about to register your license for your local domain {host}. If this site will be deployed to a public domain, please register it there instead. If {host} is the domain you want to license Kirby to, please continue.", - "license.register.success": "Takk for at du støtter Kirby", - "license.unregistered": "Dette er en uregistrert demo av Kirby", - "license.unregistered.label": "Unregistered", + "license.code": "Kode", + "license.code.help": "Du har mottatt din lisenskoden via e-post etter kjøpet. Vennligst kopier og lim den inn her.", + "license.code.label": "Vennligst skriv inn din lisenskode", + "license.status.active.info": "Inkluderer nye hovedversjoner til {date}", + "license.status.active.label": "Gyldig lisens", + "license.status.demo.info": "This is a demo installation", + "license.status.demo.label": "Demo", + "license.status.inactive.info": "Forny lisens for å oppdatere til nye hovedversjoner", + "license.status.inactive.label": "Ingen nye hovedversjoner", + "license.status.legacy.bubble": "Klar til å fornye lisensen?", + "license.status.legacy.info": "Lisensen din omfatter ikke denne versjonen", + "license.status.legacy.label": "Vennligst fornye lisensen din", + "license.status.missing.bubble": "Klar til å lansere din nettside?", + "license.status.missing.info": "Ingen gyldig lisens", + "license.status.missing.label": "Vennligst skriv inn din lisenskode", + "license.status.unknown.info": "The license status is unknown", + "license.status.unknown.label": "Unknown", + "license.manage": "Håndter dine lisenser", + "license.purchased": "Kjøpt", + "license.success": "Takk for at du støtter Kirby", + "license.unregistered.label": "Ikke registrert", "link": "Adresse", "link.text": "Koblingstekst", @@ -367,17 +495,21 @@ "lock.unsaved": "Ulagrede endringer", "lock.unsaved.empty": "Det er ingen flere ulagrede endringer", - "lock.isLocked": "Ulagrede endringer av {email}", - "lock.file.isLocked": "Filen redigeres for øyeblikket av {email} og kan ikke endres.", - "lock.page.isLocked": "Siden redigeres for øyeblikket av {email} og kan ikke endres.", + "lock.unsaved.files": "Unsaved files", + "lock.unsaved.pages": "Unsaved pages", + "lock.unsaved.users": "Unsaved accounts", + "lock.isLocked": "Ulagrede endringer av {email}", "lock.unlock": "Lås opp", - "lock.isUnlocked": "Dine ulagrede endringer har blitt overskrevet av en annen bruker. Du kan laste ned dine endringer for å sammenslå dem manuelt", + "lock.unlock.submit": "Lås opp og overskriv ulagrede endringer fra {email}", + "lock.isUnlocked": "Ble låst opp av en annen bruker", "login": "Logg Inn", - "login.code.label.login": "Login kode", + "login.code.label.login": "Login-kode", "login.code.label.password-reset": "Passord tilbakestillingskode", "login.code.placeholder.email": "000 000", + "login.code.placeholder.totp": "000000", "login.code.text.email": "Dersom din e-post er registrert vil den forespurte koden bli sendt via e-post.", + "login.code.text.totp": "Vennligst skriv inn engangskoden fra authenticator appen din.", "login.email.login.body": "Hei {user.nameOrEmail},\n\nDu ba nylig om en innloggingskode til panelet til {site}.\nFølgende innloggingskode vil være gyldig i {timeout} minutter:\n\n{code}\n\nDersom du ikke ba om en innloggingskode, vennligst ignorer denne e-posten eller kontakt din administrator hvis du har spørsmål.\nFor sikkerhets skyld, vennligst IKKE videresend denne e-posten.", "login.email.login.subject": "Din innloggingskode", "login.email.password-reset.body": "Hei {user.nameOrEmail},\n\nDu ba nylig om en tilbakestilling av passord til panelet til {site}.\nFølgende tilbakestillingskode vil være gyldig i {timeout} minutter:\n\n{code}\n\nDersom du ikke ba om en tilbakestillingskode, vennligst ignorer denne e-posten eller kontakt din administrator hvis du har spørsmål.\nFor sikkerhets skyld, vennligst IKKE videresend denne e-posten.", @@ -388,9 +520,24 @@ "login.toggleText.code.email-password": "Logg inn med passord", "login.toggleText.password-reset.email": "Glemt passord?", "login.toggleText.password-reset.email-password": "← Tilbake til innlogging", + "login.totp.enable.option": "Sett opp engangskoder", + "login.totp.enable.intro": "Autentiseringsapper kan generere engangskoder til bruk for totrinnspålogging når du logger inn på din konto.", + "login.totp.enable.qr.label": "1. Scan denne QR-koden", + "login.totp.enable.qr.help": "Kan du ikke scanne? Legg til installasjonsnøkkelen {secret} manuelt i din autentiseringsapp.", + "login.totp.enable.confirm.headline": "2. Bekreft med den genererte koden", + "login.totp.enable.confirm.text": "Din app genererer en engangskode hvert 30ende sekund. Skriv inn koden som vises nå for å ferdigstille oppsettet:", + "login.totp.enable.confirm.label": "Nærværende kode", + "login.totp.enable.confirm.help": "Etter dette er satt opp, vil vi spørre etter en engangskode hver gang du logger inn.", + "login.totp.enable.success": "Engangskoder er aktivert", + "login.totp.disable.option": "Deaktiver engangskoder", + "login.totp.disable.label": "Skriv inn ditt passord for å deaktivere bruk av engangskoder", + "login.totp.disable.help": "I fremtiden vil en annen tofaktorløsning – som en loginkode sendt via epost – bli etterspurt når du logger inn. Du kan alltid sette opp tofaktorkoder igjen på senere tidspunkt.", + "login.totp.disable.admin": "

Dette kommer til å deaktivere engangskoder for {user}.

I fremtiden vil en annen tofaktorløsning – som en loginkode sendt via epost – bli etterspurt når de logger inn. {user} kan alltid sette opp tofaktorkoder igjen på senere tidspunkt.", + "login.totp.disable.success": "Engangskoder deaktivert", "logout": "Logg ut", + "merge": "Slå sammen", "menu": "Meny", "meridiem": "AM/PM", "mime": "Mediatype", @@ -411,21 +558,26 @@ "months.september": "September", "more": "Mer", + "move": "Flytt", "name": "Navn", "next": "Neste", + "night": "Natt", "no": "nei", "off": "av", "on": "på", "open": "Åpne", "open.newWindow": "Åpne i nytt vindu", + "option": "Alternativ", "options": "Alternativer", "options.none": "Ingen alternativer", + "options.all": "Vis alle {count} alternativ", "orientation": "Orientering", "orientation.landscape": "Landskap", "orientation.portrait": "Portrett", "orientation.square": "Kvadrat", + "page": "Side", "page.blueprint": "Denne siden har ikke en blueprint enda. Du kan definere oppsettet i /site/blueprints/pages/{blueprint}.yml", "page.changeSlug": "Endre URL", "page.changeSlug.fromTitle": "Opprett fra tittel", @@ -433,13 +585,15 @@ "page.changeStatus.position": "Vennligst velg en posisjon", "page.changeStatus.select": "Velg ny status", "page.changeTemplate": "Endre mal", + "page.changeTemplate.notice": "Changing the page's template will remove content for fields that don't match in type. Use with caution.", + "page.create": "Lag som {status}", "page.delete.confirm": "Vil du virkelig slette denne siden?", "page.delete.confirm.subpages": "Denne siden har undersider.
Alle undersider vil også bli slettet.", "page.delete.confirm.title": "Skriv inn sidetittel for å bekrefte", - "page.draft.create": "Lag utkast", "page.duplicate.appendix": "Kopier", "page.duplicate.files": "Kopier filer", "page.duplicate.pages": "Kopier sider", + "page.move": "Flytt side", "page.sort": "Endre plassering", "page.status": "Status", "page.status.draft": "Utkast", @@ -450,25 +604,33 @@ "page.status.unlisted.description": "Siden er ikke er oppført og er kun tilgjengelig via URL", "pages": "Sider", + "pages.delete.confirm.selected": "Do you really want to delete the selected pages? This action cannot be undone.", "pages.empty": "Ingen sider ennå", "pages.status.draft": "Utkast", "pages.status.listed": "Publisert", "pages.status.unlisted": "Unotert", - "pagination.page": "Page", + "pagination.page": "Side", "password": "Passord", "paste": "Lim inn", "paste.after": "Lim inn etter", + "paste.success": "{count} limt inn!", "pixel": "Piksel", - "plugin": "Plugin", + "plugin": "Utvidelse", "plugins": "Plugins", "prev": "Forrige", "preview": "Forhåndsvisning", + + "publish": "Publish", + "published": "Publisert", + "remove": "Fjern", "rename": "Endre navn", + "renew": "Fornye", "replace": "Erstatt", - "retry": "Prøv på nytt", + "replace.with": "Erstatt med", + "retry": "Pr\u00f8v p\u00e5 nytt", "revert": "Forkast", "revert.confirm": "Er du sikker på at vil slette alle ulagrede endringer?", @@ -482,14 +644,17 @@ "role.nobody.title": "Ingen", "save": "Lagre", + "saved": "Saved", "search": "Søk", + "searching": "Searching", "search.min": "Skriv inn {min} tegn for å søke", - "search.all": "Vis alle", + "search.all": "Vis alle {count} resultat", "search.results.none": "Ingen resultater", + "section.invalid": "The section is invalid", "section.required": "Denne seksjonen er påkrevd", - "security": "Security", + "security": "Sikkerhet", "select": "Velg", "server": "Server", "settings": "Innstillinger", @@ -498,32 +663,50 @@ "size": "Størrelse", "slug": "URL-appendiks", "sort": "Sortere", + "sort.drag": "Drag to sort …", + "split": "Del", - "stats.empty": "No reports", - "system.issues.content": "The content folder seems to be exposed", - "system.issues.eol.kirby": "Your installed Kirby version has reached end-of-life and will not receive further security updates", - "system.issues.eol.plugin": "Your installed version of the { plugin } plugin is has reached end-of-life and will not receive further security updates", - "system.issues.debug": "Debugging must be turned off in production", - "system.issues.git": "The .git folder seems to be exposed", - "system.issues.https": "We recommend HTTPS for all your sites", - "system.issues.kirby": "The kirby folder seems to be exposed", - "system.issues.site": "The site folder seems to be exposed", - "system.issues.vulnerability.kirby": "Your installation might be affected by the following vulnerability ({ severity } severity): { description }", - "system.issues.vulnerability.plugin": "Your installation might be affected by the following vulnerability in the { plugin } plugin ({ severity } severity): { description }", - "system.updateStatus": "Update status", - "system.updateStatus.error": "Could not check for updates", - "system.updateStatus.not-vulnerable": "No known vulnerabilities", - "system.updateStatus.security-update": "Free security update { version } available", - "system.updateStatus.security-upgrade": "Upgrade { version } with security fixes available", - "system.updateStatus.unreleased": "Unreleased version", - "system.updateStatus.up-to-date": "Up to date", - "system.updateStatus.update": "Free update { version } available", - "system.updateStatus.upgrade": "Upgrade { version } available", + "stats.empty": "Ingen rapporter", + "status": "Status", + + "system.info.copy": "Copy info", + "system.info.copied": "System info copied", + "system.issues.content": "content-mappen ser ut til å være eksponert", + "system.issues.eol.kirby": "Din installerte Kirby versjon har nådd sitt end-of-life, og vil ikke lenger motta sikkerhetsoppdateringer", + "system.issues.eol.plugin": "Din installerte plugin { plugin } har nådd sitt end-of-life og vil ikke lenger motta sikkerhetsoppdateringer", + "system.issues.eol.php": "Din installerte PHP versjon { release } har nådd sitt end-of-life og vil ikke lenger motta sikkerhetsoppdateringer", + "system.issues.debug": "Debugging må bli skrudd av i production", + "system.issues.git": ".git mappen ser ut til å være eksponert", + "system.issues.https": "Vi anbefaler HTTPS for alle dine sider", + "system.issues.kirby": "kirby-mappen ser ut til å være eksponert", + "system.issues.local": "The site is running locally with relaxed security checks", + "system.issues.site": "site-mappen ser ut til å være eksponert", + "system.issues.vue.compiler": "The Vue template compiler is enabled", + "system.issues.vulnerability.kirby": "Din installasjon er muligens påvirket av følgende sikkerhetshull ({ severity } severity): { description }", + "system.issues.vulnerability.plugin": "Din installasjon er muligens påvirket av følgende sikkerhetshull i pluginen { plugin } ({ severity } severity): { description }", + "system.updateStatus": "Oppdater status", + "system.updateStatus.error": "Klarte ikke å lete etter oppdateringer", + "system.updateStatus.not-vulnerable": "Ingen kjente sikkerhetshull", + "system.updateStatus.security-update": "Gratis sikkerhetsoppdatering { version } tilgjengelig", + "system.updateStatus.security-upgrade": "Oppdatering { version } med sikkerhetsoppdateringer tilgjengelig", + "system.updateStatus.unreleased": "Ulansert versjon", + "system.updateStatus.up-to-date": "Oppdatert", + "system.updateStatus.update": "Gratis oppdatering { version } tilgjengelig", + "system.updateStatus.upgrade": "Oppdatering { version } tilgjengelig", + + "tel": "Telefon", + "tel.placeholder": "+49123456789", + "template": "Mal", + + "theme": "Theme", + "theme.light": "Lights on", + "theme.dark": "Lights off", + "theme.automatic": "Match system default", "title": "Tittel", - "template": "Mal", "today": "I dag", + "toolbar.button.clear": "Fjern formattering", "toolbar.button.code": "Kode", "toolbar.button.bold": "Fet tekst", "toolbar.button.email": "Epost", @@ -541,15 +724,19 @@ "toolbar.button.link": "Adresse", "toolbar.button.paragraph": "Avsnitt", "toolbar.button.strike": "Gjennomstreking", + "toolbar.button.sub": "Subscript", + "toolbar.button.sup": "Superscript", "toolbar.button.ol": "Ordnet liste", "toolbar.button.underline": "Understrek", "toolbar.button.ul": "Punktliste", "translation.author": "Kirby Team", "translation.direction": "ltr", - "translation.name": "Norsk Bokmål", + "translation.name": "Norsk Bokm\u00e5l", "translation.locale": "nb_NO", + "type": "Type", + "upload": "Last opp", "upload.error.cantMove": "Den opplastede filen kunne ikke flyttes", "upload.error.cantWrite": "Kunne ikke skrive fil til disk", @@ -562,11 +749,11 @@ "upload.error.noFiles": "Ingen filer ble lastet opp", "upload.error.partial": "Den opplastede filen ble bare delvis lastet opp", "upload.error.tmpDir": "Mangler en midlertidig mappe", - "upload.errors": "Error", + "upload.errors": "Feil", "upload.progress": "Laster opp…", "url": "Nettadresse", - "url.placeholder": "https://example.com", + "url.placeholder": "https://eksempel.no", "user": "Bruker", "user.blueprint": "Du kan definere flere seksjoner og skjemafelter for denne brukerrollen i /site/blueprints/users/{blueprint}.yml", @@ -574,6 +761,7 @@ "user.changeLanguage": "Endre språk", "user.changeName": "Angi nytt navn for denne brukeren", "user.changePassword": "Bytt passord", + "user.changePassword.current": "Your current password", "user.changePassword.new": "Nytt passord", "user.changePassword.new.confirm": "Bekreft nytt passord…", "user.changeRole": "Bytt rolle", @@ -585,10 +773,13 @@ "users": "Brukere", "version": "Kirby versjon", - "version.current": "Current version", - "version.latest": "Latest version", - "versionInformation": "Version information", + "version.changes": "Changed version", + "version.compare": "Compare versions", + "version.current": "Nåværende versjon", + "version.latest": "Siste versjon", + "versionInformation": "Versjonsinformasjon", + "view": "View", "view.account": "Din konto", "view.installation": "Installasjon", "view.languages": "Språk", diff --git a/kirby/i18n/translations/nl.json b/kirby/i18n/translations/nl.json index 2751f30..899c397 100644 --- a/kirby/i18n/translations/nl.json +++ b/kirby/i18n/translations/nl.json @@ -1,21 +1,30 @@ { "account.changeName": "Wijzig je naam", "account.delete": "Verwijder je account", - "account.delete.confirm": "Wil je echt je account verwijderen? Je wordt direct uitgelogd. Uw account kan niet worden hersteld.", + "account.delete.confirm": "Wil je echt je account verwijderen? Je wordt direct uitgelogd. Je account kan niet worden hersteld.", + "activate": "Activeren", "add": "Voeg toe", + "alpha": "Alpha", "author": "Auteur", "avatar": "Avatar", "back": "Terug", "cancel": "Annuleren", "change": "Wijzigen", "close": "Sluiten", - "confirm": "OK", + "changes": "Wijzigingen", + "confirm": "Oke", "collapse": "Sluit", "collapse.all": "Sluit alles", + "color": "Kleur", + "coordinates": "Coördinaten ", "copy": "Kopiëren", "copy.all": "Kopieer alles", + "copy.success": "{count} gekopieerd!", + "copy.success.multiple": "{count} gekopieerd!", + "copy.url": "Copy URL", "create": "Aanmaken", + "custom": "Custom", "date": "Datum", "date.select": "Selecteer een datum", @@ -34,13 +43,20 @@ "delete": "Verwijderen", "delete.all": "Verwijder alles", + "dialog.fields.empty": "Dit venster heeft geen velden", "dialog.files.empty": "Geen bestanden om te selecteren", "dialog.pages.empty": "Geen pagina's om te selecteren", + "dialog.text.empty": "Dit venster bevat geen tekst", "dialog.users.empty": "Geen gebruikers om te selecteren", "dimensions": "Dimensies", + "disable": "Uitschakelen", "disabled": "Uitgeschakeld", "discard": "Annuleren", + + "drawer.fields.empty": "Deze drawer heeft geen velden", + + "domain": "Domein", "download": "Download", "duplicate": "Dupliceren", @@ -49,15 +65,17 @@ "email": "E-mailadres", "email.placeholder": "mail@voorbeeld.nl", - "entries": "Entries", - "entry": "Entry", + "enter": "Enter", + "entries": "Items", + "entry": "Item", "environment": "Omgeving", + "error": "Foutmelding", "error.access.code": "Ongeldige code", "error.access.login": "Ongeldige login", "error.access.panel": "Je hebt geen toegang tot het Panel", - "error.access.view": "Je hebt geen toegangsrechten voor deze zone van het Panel", + "error.access.view": "Je hebt geen toegangsrechten voor dit gedeelte van het Panel", "error.avatar.create.fail": "De avatar kon niet worden geupload", "error.avatar.delete.fail": "De avatar kan niet worden verwijderd", @@ -70,17 +88,35 @@ "error.blocks.max.singular": "Je kunt niet meer dan één blok toevoegen", "error.blocks.min.plural": "Je moet ten minste {min} blok toevoegen", "error.blocks.min.singular": "Je moet ten minste één blok toevoegen", - "error.blocks.validation": "There's an error on the \"{field}\" field in block {index} using the \"{fieldset}\" block type", + "error.blocks.validation": "Er is een fout opgetreden bij het \"{field}\" veld in blok {index} in het \"{fieldset}\" bloktype", - "error.cache.type.invalid": "Invalid cache type \"{type}\"", + "error.cache.type.invalid": "Ongeldig cache type \"{type}\"", + + "error.content.lock.delete": "The version is locked and cannot be deleted", + "error.content.lock.move": "The source version is locked and cannot be moved", + "error.content.lock.publish": "This version is already published", + "error.content.lock.replace": "The version is locked and cannot be replaced", + "error.content.lock.update": "The version is locked and cannot be updated", + + "error.entries.max.plural": "You must not add more than {max} entries", + "error.entries.max.singular": "You must not add more than one entry", + "error.entries.min.plural": "You must add at least {min} entries", + "error.entries.min.singular": "You must add at least one entry", + "error.entries.supports": "\"{type}\" field type is not supported for the entries field", + "error.entries.validation": "Er is een fout opgetreden in veld \"{field}\" in rij {index}", "error.email.preset.notFound": "De e-mailvoorinstelling \"{name}\" kan niet worden gevonden", "error.field.converter.invalid": "Ongeldige converter \"{converter}\"", - "error.field.type.missing": "Field \"{ name }\": The field type \"{ type }\" does not exist", + "error.field.link.options": "Invalid options: {options}", + "error.field.type.missing": "Veld \"{ name }\": Het veldtype \"{ type }\" bestaat niet", "error.file.changeName.empty": "De naam mag niet leeg zijn", "error.file.changeName.permission": "Je hebt geen rechten om de naam te wijzigen van \"{filename}\"", + "error.file.changeTemplate.invalid": "Het template voor het bestand \"{id}\" kan niet worden gewijzigd in \"{template}\" (geldig: \"{blueprints}\")", + "error.file.changeTemplate.permission": "Je hebt geen rechten om het template te wijzigen voor bestand \"{id}\"", + + "error.file.delete.multiple": "Not all files could be deleted. Try each remaining file individually to see the specific error that prevents deletion.", "error.file.duplicate": "Er bestaat al een bestand met de naam \"{filename}\"", "error.file.extension.forbidden": "Bestandsextensie \"{extension}\" is niet toegestaan", "error.file.extension.invalid": "Ongeldige extensie: {extension}", @@ -95,9 +131,11 @@ "error.file.minheight": "De hoogte van de afbeelding moet minimaal {height} pixels zijn", "error.file.minsize": "Het bestand is te klein", "error.file.minwidth": "De breedte van de afbeelding moet minimaal {width} pixels zijn", + "error.file.name.unique": "De bestandsnaam moet uniek zijn", "error.file.name.missing": "De bestandsnaam mag niet leeg zijn", "error.file.notFound": "Het bestand kan niet worden gevonden", "error.file.orientation": "De oriëntatie van de afbeelding moet \"{orientation}\" zijn", + "error.file.sort.permission": "You are not allowed to change the sorting of \"{filename}\"", "error.file.type.forbidden": "Je hebt geen rechten om {type} bestanden up te loaden", "error.file.type.invalid": "Ongeldig bestands type: {type}", "error.file.undefined": "Het bestand kan niet worden gevonden", @@ -106,22 +144,30 @@ "error.form.notSaved": "Het formulier kon niet worden opgeslagen", "error.language.code": "Vul een geldige code voor deze taal in", + "error.language.create.permission": "Je hebt geen rechten om een taal toe te voegen", + "error.language.delete.permission": "Je hebt geen rechten om een taal te verwijderen", "error.language.duplicate": "De taal bestaat al", "error.language.name": "Vul een geldige naam voor deze taal in", "error.language.notFound": "De taal kan niet worden gevonden", + "error.language.update.permission": "Je hebt geen rechten om deze taal te updaten", - "error.layout.validation.block": "There's an error on the \"{field}\" field in block {blockIndex} using the \"{fieldset}\" block type in layout {layoutIndex}", + "error.layout.validation.block": "Er is een fout opgetreden bij het \"{field}\" veld in blok {blockIndex} in het \"{fieldset}\" bloktype in layout {layoutIndex}", "error.layout.validation.settings": "Er is een fout gevonden in de instellingen van ontwerp {index} ", - "error.license.format": "Vul een gelidge licentie-key in", + "error.license.domain": "Het domein voor de licentie ontbreekt", "error.license.email": "Gelieve een geldig emailadres in te voeren", + "error.license.format": "Vul een geldige licentie in", "error.license.verification": "De licentie kon niet worden geverifieerd. ", - "error.object.validation": "There’s an error in the \"{label}\" field:\n{message}", + "error.login.totp.confirm.invalid": "Ongeldige code", + "error.login.totp.confirm.missing": "Vul de code in", + + "error.object.validation": "Er is een fout opgetreden in het veld \"{label}\":\n{message}", "error.offline": "Het Panel is momenteel offline", "error.page.changeSlug.permission": "Je kunt de URL van deze pagina niet wijzigen", + "error.page.changeSlug.reserved": "Het pad van hoofdpagina's mogen niet beginnen met \"{path}\".", "error.page.changeStatus.incomplete": "Deze pagina bevat fouten en kan niet worden gepubliceerd", "error.page.changeStatus.permission": "De status van deze pagina kan niet worden gewijzigd", "error.page.changeStatus.toDraft.invalid": "De pagina \"{slug}\" kan niet worden aangepast naar 'concept'", @@ -133,10 +179,18 @@ "error.page.delete": "De pagina \"{slug}\" kan niet worden verwijderd", "error.page.delete.confirm": "Voer de paginatitel in om te bevestigen", "error.page.delete.hasChildren": "Deze pagina heeft subpagina's en kan niet worden verwijderd", + "error.page.delete.multiple": "Not all pages could be deleted. Try each remaining page individually to see the specific error that prevents deletion.", "error.page.delete.permission": "Je hebt geen rechten om \"{slug}\" te verwijderen", "error.page.draft.duplicate": "Er bestaat al een conceptpagina met de URL-appendix \"{slug}\"", "error.page.duplicate": "Er bestaat al een pagina met de URL-appendix \"{slug}\"", "error.page.duplicate.permission": "Je bent niet gemachtigd om \"{slug}\" te dupliceren", + "error.page.move.ancestor": "De pagina kan niet in zichzelf worden verplaatst", + "error.page.move.directory": "De page map kan niet worden verplaatst", + "error.page.move.duplicate": "Er bestaat al een subpagina met de URL-appendix \"{slug}\"", + "error.page.move.noSections": "The page \"{parent}\" cannot be a parent of any page because it lacks any pages sections in its blueprint", + "error.page.move.notFound": "De verplaatste pagina kan niet gevonden worden", + "error.page.move.permission": "Je hebt geen rechten om \"{slug}\" te verplaatsen", + "error.page.move.template": "De \"{template}\" template is niet toegestaan als een subpagina van \"{parent}\"", "error.page.notFound": "De pagina \"{slug}\" kan niet worden gevonden", "error.page.num.invalid": "Vul een geldig sorteer-cijfer in. Het cijfer mag niet negatief zijn", "error.page.slug.invalid": "Vul een geldig URL-achtervoegsel in", @@ -163,6 +217,8 @@ "error.site.changeTitle.permission": "Je hebt geen rechten om de titel van de site te wijzigen", "error.site.update.permission": "Je hebt geen rechten om de site te updaten", + "error.structure.validation": "Er is een fout opgetreden in veld \"{field}\" in rij {index}", + "error.template.default.notFound": "Het standaard template bestaat niet", "error.unexpected": "Een onverwacht fout heeft plaats gevonden! Schakel debug-modus in voor meer informatie: https://getkirby.com/docs/reference/system/options/debug", @@ -180,23 +236,25 @@ "error.user.delete.lastUser": "De laatste gebruiker kan niet worden verwijderd", "error.user.delete.permission": "Je hebt geen rechten om gebruiker \"{name}\" te verwijderen", "error.user.duplicate": "Er bestaat al een gebruiker met e-mailadres \"{email}\"", - "error.user.email.invalid": "Gelieve een geldig emailadres in te voeren", - "error.user.language.invalid": "Gelieve een geldige taal in te voeren", + "error.user.email.invalid": "Vul een geldig e-mailadres in", + "error.user.language.invalid": "Vul een geldige taal in", "error.user.notFound": "De gebruiker \"{name}\" kan niet worden gevonden", - "error.user.password.excessive": "Please enter a valid password. Passwords must not be longer than 1000 characters.", - "error.user.password.invalid": "Gelieve een geldig wachtwoord in te voeren. Wachtwoorden moeten minstens 8 karakters lang zijn.", + "error.user.password.excessive": "Voer een geldig wachtwoord in. Wachtwoorden mogen niet langer zijn dan 1000 tekens.", + "error.user.password.invalid": "Voer een geldig wachtwoord in. Wachtwoorden moeten minstens 8 tekens lang zijn.", "error.user.password.notSame": "De wachtwoorden komen niet overeen", "error.user.password.undefined": "De gebruiker heeft geen wachtwoord", "error.user.password.wrong": "Fout wachtwoord", - "error.user.role.invalid": "Gelieve een geldige rol in te voeren", + "error.user.role.invalid": "Vul een geldige rol in", "error.user.undefined": "De gebruiker kan niet worden gevonden", "error.user.update.permission": "Je hebt geen rechten om gebruiker \"{name}\" te updaten", - "error.validation.accepted": "Gelieve te bevestigen", + "error.validation.accepted": "Ga akkoord", "error.validation.alpha": "Vul alleen a-z karakters in", - "error.validation.alphanum": "Vul alleen a-z karakters of cijfers (0-9) in", + "error.validation.alphanum": "Vul alleen tekens in tussen a-z of cijfers 0-9", + "error.validation.anchor": "Vul een juiste link in", "error.validation.between": "Vul een waarde tussen \"{min}\" en \"{max}\"", "error.validation.boolean": "Ga akkoord of weiger", + "error.validation.color": "Vul een geldige kleur in {format} in", "error.validation.contains": "Vul een waarde in die \"{needle}\" bevat", "error.validation.date": "Vul een geldige datum in", "error.validation.date.after": "Vul een datum in na {date}", @@ -204,13 +262,14 @@ "error.validation.date.between": "Vul een datum in tussen {min} en {max}", "error.validation.denied": "Weiger", "error.validation.different": "De invoer mag niet \"{other}\" zijn", - "error.validation.email": "Gelieve een geldig emailadres in te voeren", + "error.validation.email": "Vul een geldig e-mailadres in", "error.validation.endswith": "De invoer moet eindigen met \"{end}\"", "error.validation.filename": "Vul een geldige bestandsnaam in", "error.validation.in": "Vul één van de volgende dingen in: ({in})", "error.validation.integer": "Vul een geldig geheel getal in", "error.validation.ip": "Vul een geldig IP-adres in", "error.validation.less": "Vul een waarde in lager dan {max}", + "error.validation.linkType": "Het type link is niet toegestaan", "error.validation.match": "De invoer klopt niet met het verwachte patroon", "error.validation.max": "Vul een waarde in die gelijk is aan of lager dan {max}", "error.validation.maxlength": "Gebruik minder karakters (maximaal {max} karakters)", @@ -227,15 +286,18 @@ "error.validation.same": "Vul \"{other}\" in", "error.validation.size": "De lengte van de invoer moet \"{size}\" zijn", "error.validation.startswith": "De invoer moet beginnen met \"{start}\"", + "error.validation.tel": "Vul een niet-geformatteerd telefoonnummer in", "error.validation.time": "Vul een geldige tijd in", - "error.validation.time.after": "Voer een tijd in na {time}", - "error.validation.time.before": "Voer een tijd in voor {time}", - "error.validation.time.between": "Voer een tijd in tussen {min} en {max}", + "error.validation.time.after": "Vul een tijd in na {time}", + "error.validation.time.before": "Vul een tijd in voor {time}", + "error.validation.time.between": "Vul een tijd in tussen {min} en {max}", + "error.validation.uuid": "Vul een geldige UUID in", "error.validation.url": "Vul een geldige URL in", "expand": "Open", "expand.all": "Open alles", + "field.invalid": "Dit veld is niet geldig", "field.required": "Dit veld is verplicht", "field.blocks.changeType": "Wijzig type", "field.blocks.code.name": "Code", @@ -245,8 +307,9 @@ "field.blocks.delete.confirm.all": "Wil je echt alle blokken verwijderen?", "field.blocks.delete.confirm.selected": "Wil je de geselecteerde blokken echt verwijderen?", "field.blocks.empty": "Nog geen blokken", + "field.blocks.fieldsets.empty": "Nog geen veldsets", "field.blocks.fieldsets.label": "Selecteer een bloktype ...", - "field.blocks.fieldsets.paste": "Druk op {{ shortcut }} om blokken van je klembord te plakken/importeren", + "field.blocks.fieldsets.paste": "Druk op {{ shortcut }} om layouts/blokken van je klembord te importeren Alleen de toegestane layouts/blokken in het huidige veld worden ingevoegd.", "field.blocks.gallery.name": "Galerij", "field.blocks.gallery.images.empty": "Nog geen afbeeldingen", "field.blocks.gallery.images.label": "Afbeeldingen", @@ -254,11 +317,16 @@ "field.blocks.heading.name": "Koptekst", "field.blocks.heading.text": "Tekst", "field.blocks.heading.placeholder": "Koptekst ...", + "field.blocks.figure.back.plain": "Plain", + "field.blocks.figure.back.pattern.light": "Pattern (light)", + "field.blocks.figure.back.pattern.dark": "Pattern (dark)", "field.blocks.image.alt": "Alternatieve tekst", "field.blocks.image.caption": "Beschrijving", "field.blocks.image.crop": "Uitsnede", "field.blocks.image.link": "Link", "field.blocks.image.location": "Locatie", + "field.blocks.image.location.internal": "Deze website", + "field.blocks.image.location.external": "Externe bron", "field.blocks.image.name": "Afbeelding", "field.blocks.image.placeholder": "Selecteer een afbeelding", "field.blocks.image.ratio": "Verhouding", @@ -275,38 +343,72 @@ "field.blocks.quote.citation.placeholder": "door ...", "field.blocks.text.name": "Tekst", "field.blocks.text.placeholder": "Tekst ...", + "field.blocks.video.autoplay": "Automatisch afspelen", "field.blocks.video.caption": "Beschrijving", + "field.blocks.video.controls": "Besturingselementen", + "field.blocks.video.location": "Locatie", + "field.blocks.video.loop": "Herhalen", + "field.blocks.video.muted": "Gedempt", "field.blocks.video.name": "Video", "field.blocks.video.placeholder": "Voer een video link in", + "field.blocks.video.poster": "Afbeelding", + "field.blocks.video.preload": "Vooral laden", "field.blocks.video.url.label": "Video link", "field.blocks.video.url.placeholder": "https://youtube.com/?v=", - "field.files.empty": "Nog geen bestanden geselecteerd", + "field.entries.delete.confirm.all": "Weet je zeker dat je alle items wil verwijderen?", + "field.entries.empty": "Nog geen items", + "field.files.empty": "Nog geen bestanden geselecteerd", + "field.files.empty.single": "No file selected yet", + + "field.layout.change": "Verander layout", "field.layout.delete": "Verwijder indeling", - "field.layout.delete.confirm": "Weet je zeker dat je deze indeling wilt verwijderen?", + "field.layout.delete.confirm": "Weet je zeker dat je deze layout wilt verwijderen?", + "field.layout.delete.confirm.all": "Weet je zeker dat je alle layouts wilt verwijderen?", "field.layout.empty": "Er zijn nog geen rijen", "field.layout.select": "Selecteer een indeling", "field.object.empty": "Nog geen informatie", "field.pages.empty": "Nog geen pagina's geselecteerd", + "field.pages.empty.single": "No page selected yet", - "field.structure.delete.confirm": "Wil je deze entry verwijderen?", - "field.structure.delete.confirm.all": "Do you really want to delete all entries?", - "field.structure.empty": "Nog geen items.", + "field.structure.delete.confirm": "Wil je deze rij verwijderen?", + "field.structure.delete.confirm.all": "Weet je zeker dat je alle items wil verwijderen?", + "field.structure.empty": "Nog geen items", "field.users.empty": "Nog geen gebruikers geselecteerd", + "field.users.empty.single": "No user selected yet", + "fields.empty": "Nog geen velden", + + "file": "Bestand", "file.blueprint": "Dit bestand heeft nog geen blauwdruk. U kunt de instellingen definiëren in /site/blueprints/files/{blueprint}.yml", + "file.changeTemplate": "Verander template", + "file.changeTemplate.notice": "Door het template van het bestand te wijzigen, wordt inhoud verwijderd voor velden waarvan het type niet overeenkomt. Als het nieuwe template bepaalde regels definieert, bv. afmetingen van afbeeldingen, dan worden die ook onomkeerbaar toegepast. Wees hier voorzichtig mee.", "file.delete.confirm": "Wil je dit bestand
{filename} verwijderen?", + "file.focus.placeholder": "Set focal point", + "file.focus.reset": "Remove focal point", + "file.focus.title": "Focus", "file.sort": "Verander positie", "files": "Bestanden", + "files.delete.confirm.selected": "Do you really want to delete the selected files? This action cannot be undone.", "files.empty": "Nog geen bestanden", + "filter": "Filter", + + "form.discard": "Discard changes", + "form.discard.confirm": "Do you really want to discard all your changes?", + "form.locked": "This content is disabled for you as it is currently edited by another user", + "form.unsaved": "The current changes have not yet been saved", + "form.preview": "Preview changes", + "form.preview.draft": "Preview draft", + "hide": "Verberg", "hour": "Uur", + "hue": "Hue", "import": "Importeer", "info": "Info", "insert": "Toevoegen", @@ -324,7 +426,6 @@ "installation.issues.mbstring": "De MB String extensie is verplicht", "installation.issues.media": "De map /mediabestaat niet of heeft geen schrijfrechten", "installation.issues.php": "Gebruik PHP8+", - "installation.issues.server": "Kirby vereist Apache, Nginx of Caddy", "installation.issues.sessions": "De map /site/sessions bestaat niet of heeft geen schrijfrechten", "language": "Taal", @@ -332,6 +433,7 @@ "language.convert": "Maak standaard", "language.convert.confirm": "

Weet je zeker dat je {name} wilt aanpassen naar de standaard taal? Dit kan niet ongedaan worden gemaakt

Als {name} nog niet vertaalde content heeft, is er geen content meer om op terug te vallen en zouden delen van je site leeg kunnen zijn.

", "language.create": "Nieuwe taal toevoegen", + "language.default": "Standaard taal", "language.delete.confirm": "Weet je zeker dat je de taal {name} inclusief alle vertalingen wilt verwijderen? Je kunt dit niet ongedaan maken!", "language.deleted": "De taal is verwijderd", "language.direction": "Leesrichting", @@ -340,7 +442,16 @@ "language.locale": "PHP-locale regel", "language.locale.warning": "Je gebruikt een aangepaste landinstelling. Wijzig het het taalbestand in /site/languages", "language.name": "Naam", + "language.secondary": "Tweede taal", + "language.settings": "Taal instellingen", "language.updated": "De taal is geüpdatet", + "language.variables": "Taal variabelen", + "language.variables.empty": "Nog geen vertalingen", + + "language.variable.delete.confirm": "Weet je zeker dat je de variabele voor {key} wil verwijderen?", + "language.variable.key": "Key", + "language.variable.notFound": "De variabele kan niet gevonden worden", + "language.variable.value": "Waarde", "languages": "Talen", "languages.default": "Standaard taal", @@ -349,16 +460,33 @@ "languages.secondary.empty": "Er zijn nog geen andere talen beschikbaar", "license": "Licentie", + "license.activate": "Activeer nu", + "license.activate.label": "Activeer je licentie", + "license.activate.domain": "Je licentie wordt geactiveerd voor {host}.", + "license.activate.local": "Je staat op het punt om je Kirby licentie voor je lokale domein {host} te activeren. Als deze site op een publiek domein geplaatst wordt, activeer deze licentie dan daar. Als het domein {host} wel degene is die je voor deze licentie wil gebruiken, ga dan door.", + "license.activated": "Geactiveerd", "license.buy": "Koop een licentie", - "license.register": "Registreren", + "license.code": "Code", + "license.code.help": "Je hebt de licentiecode via e-mail gekregen nadat je de aankoop hebt gedaan. Kopieer en plak de licentiecode hier.", + "license.code.label": "Vul je licentie in", + "license.status.active.info": "Inclusief nieuwe major versies tot {date}", + "license.status.active.label": "Geldige licentie", + "license.status.demo.info": "Dit is een demo installatie", + "license.status.demo.label": "Demo", + "license.status.inactive.info": "Verleng licentie om bij te werken naar nieuwe versies", + "license.status.inactive.label": "Geen nieuwe major versies", + "license.status.legacy.bubble": "Klaar om je licentie te vernieuwen?", + "license.status.legacy.info": "Je licentie dekt deze versie niet", + "license.status.legacy.label": "Verleng je licentie", + "license.status.missing.bubble": "Klaar om je website te lanceren?", + "license.status.missing.info": "Geen geldige licentie", + "license.status.missing.label": "Activeer je licentie", + "license.status.unknown.info": "The license status is unknown", + "license.status.unknown.label": "Unknown", "license.manage": "Beheer je licenties", - "license.register.help": "Je hebt de licentie via e-mail gekregen nadat je de aankoop hebt gedaan. Kopieer en plak de licentie om te registreren. ", - "license.register.label": "Vul je licentie in", - "license.register.domain": "Your license will be registered to {host}.", - "license.register.local": "You are about to register your license for your local domain {host}. If this site will be deployed to a public domain, please register it there instead. If {host} is the domain you want to license Kirby to, please continue.", - "license.register.success": "Bedankt dat je Kirby ondersteunt", - "license.unregistered": "Dit is een niet geregistreerde demo van Kirby", - "license.unregistered.label": "Unregistered", + "license.purchased": "Gekocht", + "license.success": "Bedankt dat je Kirby ondersteunt", + "license.unregistered.label": "Niet geregistreerd", "link": "Link", "link.text": "Linktekst", @@ -367,20 +495,24 @@ "lock.unsaved": "Niet opgeslagen wijzigingen", "lock.unsaved.empty": "Er zijn geen niet opgeslagen wijzigingen meer", - "lock.isLocked": "Niet opgeslagen wijzigingen door {email}", - "lock.file.isLocked": "Dit bestand wordt momenteel bewerkt door {email} en kan niet worden gewijzigd.", - "lock.page.isLocked": "Deze pagina wordt momenteel bewerkt door {email} en kan niet worden gewijzigd.", + "lock.unsaved.files": "Unsaved files", + "lock.unsaved.pages": "Unsaved pages", + "lock.unsaved.users": "Unsaved accounts", + "lock.isLocked": "Niet opgeslagen wijzigingen door {email}", "lock.unlock": "Ontgrendelen", - "lock.isUnlocked": "Je niet opgeslagen wijzigingen zijn overschreven door een andere gebruiker. Je kunt je wijzigingen downloaden om ze handmatig samen te voegen.", + "lock.unlock.submit": "Niet-opgeslagen wijzigingen ontgrendelen en overschrijven met {email}", + "lock.isUnlocked": "Is ontgrendeld door een andere gebruiker", "login": "Inloggen", "login.code.label.login": "Log in code", "login.code.label.password-reset": "Wachtwoord herstel code", "login.code.placeholder.email": "000 000", - "login.code.text.email": "Als uw e-mailadres geregistreerd is, werd de gevraagde code per e-mail verzonden.", - "login.email.login.body": "Hallo {user.nameOrEmail},\n\nJe hebt onlangs een inlogcode aangevraagd voor het Panel van {site}.\nDe volgende inlogcode is {timeout} minuten geldig:\n\n{code}\n\nAls je geen inlogcode hebt aangevraagd, mag je deze mail negeren of neem je contact op met uw beheerder.\nOm veiligheidsredenen verzoeken wij deze e-mail NIET door te sturen.", + "login.code.placeholder.totp": "000000", + "login.code.text.email": "Als je e-mailadres is geregistreerd, is de aangevraagde code per e-mail verzonden.", + "login.code.text.totp": "Vul de eenmalige code in vanuit je Authenticator-app. ", + "login.email.login.body": "Hallo {user.nameOrEmail},\n\nJe hebt onlangs een inlogcode aangevraagd voor het panel van {site}.\nDe volgende inlogcode is geldig voor {timeout} minuten:\n\n{code}\n\nAls je geen inlogcode hebt aangevraagd, negeer deze e-mail dan of neem contact op met de beheerder als je vragen hebt.\nStuur deze e-mail voor de zekerheid NIET door.", "login.email.login.subject": "Jouw log in code", - "login.email.password-reset.body": "Hallo {user.nameOrEmail},\n\nJe hebt onlangs een paswoord herstel code aangevraagd voor het Panel van {site}.\nDe volgende paswoord herstel code is {timeout} minuten geldig:\n\n{code}\n\nAls je geen paswoord herstel code hebt aangevraagd, mag je deze mail negeren of neem je contact op met uw beheerder.\nOm veiligheidsredenen verzoeken wij deze e-mail NIET door te sturen.", + "login.email.password-reset.body": "Hallo {user.nameOrEmail},\n\nJe hebt onlangs een wachtwoord reset code aangevraagd voor het panel van {site}.\nDe volgende wachtwoord reset code is geldig voor {timeout} minuten:\n\n{code}\n\nAls je geen wachtwoord reset code hebt aangevraagd, negeer dan deze e-mail of neem contact op met de beheerder als je vragen hebt.\nStuur deze e-mail voor de zekerheid NIET door.", "login.email.password-reset.subject": "Jouw wachtwoord herstel code", "login.remember": "Houd mij ingelogd", "login.reset": "Wachtwoord herstellen", @@ -388,9 +520,24 @@ "login.toggleText.code.email-password": "Log in met je wachtwoord", "login.toggleText.password-reset.email": "Wachtwoord vergeten?", "login.toggleText.password-reset.email-password": "← Terug naar log in", + "login.totp.enable.option": "Stel eenmalige codes in.", + "login.totp.enable.intro": "Authenticator-apps kunnen eenmalige codes genereren die dienen als een tweede factor als jij inlogt in je account.", + "login.totp.enable.qr.label": "1. Scan deze QR code", + "login.totp.enable.qr.help": "Problemen met scannen? Voeg de setup key {secret} handmatig toe aan je Authenticator-app.", + "login.totp.enable.confirm.headline": "2. Bevestig met een gegenereerde code", + "login.totp.enable.confirm.text": "De app genereert elke 30 seconden een nieuwe eenmalige code. Voer de huidige code in om de setup af te ronden:", + "login.totp.enable.confirm.label": "Huidige code", + "login.totp.enable.confirm.help": "Na het instellen zullen we elke keer om een eenmalige code vragen bij het inloggen.", + "login.totp.enable.success": "Eenmalige codes geactiveerd", + "login.totp.disable.option": "Schakel eenmalige codes uit", + "login.totp.disable.label": "Voer je wachtwoord in om eenmalige codes uit te schakelen", + "login.totp.disable.help": "In de toekomst zal een andere tweede factor, zoals een inlogcode die via e-mail wordt verzonden, worden gevraagd wanneer je inlogt. Je kunt later altijd weer eenmalige codes instellen.", + "login.totp.disable.admin": "

Dit schakelt eenmalige codes uit voor {user}.

In de toekomst zal bij het inloggen om een andere tweede factor worden gevraagd, zoals een inlogcode die via e-mail wordt verzonden. {user} kan na zijn volgende aanmelding opnieuw eenmalige codes instellen.

", + "login.totp.disable.success": "Eenmalige codes uitgeschakeld", "logout": "Uitloggen", + "merge": "Merge", "menu": "Menu", "meridiem": "AM/PM", "mime": "Mime-type", @@ -411,21 +558,26 @@ "months.september": "september", "more": "Meer", + "move": "Verplaatsen", "name": "Naam", "next": "Volgende", + "night": "Nacht", "no": "nee", "off": "uit", "on": "aan", "open": "Open", "open.newWindow": "Openen in een nieuw scherm", + "option": "Option", "options": "Opties", "options.none": "Geen opties beschikbaar", + "options.all": "Laat alle {count} opties zien", "orientation": "Oriëntatie", "orientation.landscape": "Liggend", "orientation.portrait": "Staand", "orientation.square": "Vierkant", + "page": "Pagina", "page.blueprint": "Deze pagina heeft nog geen blauwdruk. Je kan de instellingen definiëren in /site/blueprints/pages/{blueprint}.yml", "page.changeSlug": "Verander URL", "page.changeSlug.fromTitle": "Aanmaken op basis van titel", @@ -433,13 +585,15 @@ "page.changeStatus.position": "Selecteer een positie", "page.changeStatus.select": "Selecteer een nieuwe status", "page.changeTemplate": "Verander template", + "page.changeTemplate.notice": "Door de template te wijzigen, wordt inhoud verwijderd voor velden waarvan het type niet overeenkomt. Gebruik dit voorzichtig.", + "page.create": "Maak aan als {status}", "page.delete.confirm": "Weet je zeker dat je pagina {title} wilt verwijderen?", "page.delete.confirm.subpages": "Deze pagina heeft subpagina's.
Alle subpagina's zullen ook worden verwijderd.", "page.delete.confirm.title": "Voeg een paginatitel in om te bevestigen", - "page.draft.create": "Maak concept", "page.duplicate.appendix": "Kopiëren", "page.duplicate.files": "Kopieer bestanden", "page.duplicate.pages": "Kopieer pagina's", + "page.move": "Move page", "page.sort": "Verander positie", "page.status": "Status", "page.status.draft": "Concept", @@ -450,6 +604,7 @@ "page.status.unlisted.description": "Deze pagina is alleen bereikbaar via URL", "pages": "Pagina’s", + "pages.delete.confirm.selected": "Do you really want to delete the selected pages? This action cannot be undone.", "pages.empty": "Nog geen pagina's", "pages.status.draft": "Concepten", "pages.status.listed": "Gepubliceerd", @@ -460,14 +615,21 @@ "password": "Wachtwoord", "paste": "Plak", "paste.after": "Plak achter", + "paste.success": "{count} geplakt!", "pixel": "Pixel", "plugin": "Plugin", "plugins": "Plugins", "prev": "Vorige", "preview": "Voorbeeld", + + "publish": "Publish", + "published": "Gepubliceerd", + "remove": "Verwijder", "rename": "Hernoem", + "renew": "Verlengen", "replace": "Vervang", + "replace.with": "Vervangen met", "retry": "Probeer opnieuw", "revert": "Annuleren", "revert.confirm": "Weet je zeker dat je alle niet-opgeslagen veranderingen wilt verwijderen?", @@ -482,11 +644,14 @@ "role.nobody.title": "Niemand", "save": "Opslaan", + "saved": "Saved", "search": "Zoeken", + "searching": "Searching", "search.min": "Voer {min} tekens in om te zoeken", - "search.all": "Toon alles", + "search.all": "Laat alle {count} resultaten zien", "search.results.none": "Geen resultaten", + "section.invalid": "De sectie is ongeldig", "section.required": "De sectie is verplicht", "security": "Beveiliging", @@ -498,21 +663,30 @@ "size": "Grootte", "slug": "URL-toevoeging", "sort": "Sorteren", + "sort.drag": "Sleep om te sorteren ...", + "split": "Splitsen", - "stats.empty": "No reports", - "system.issues.content": "The content folder seems to be exposed", - "system.issues.eol.kirby": "Your installed Kirby version has reached end-of-life and will not receive further security updates", - "system.issues.eol.plugin": "Your installed version of the { plugin } plugin is has reached end-of-life and will not receive further security updates", - "system.issues.debug": "Debugging must be turned off in production", + "stats.empty": "Geen rapporten", + "status": "Status", + + "system.info.copy": "Kopieer informatie", + "system.info.copied": "Systeem informatie gekopieerd", + "system.issues.content": "De content map lijkt zichtbaar te zijn", + "system.issues.eol.kirby": "De geïnstalleerde Kirby versie is niet meer actueel en zal geen verdere beveiligingsupdates meer ontvangen.", + "system.issues.eol.plugin": "De geïnstalleerde versie van plugin { plugin } is niet meer actueel en zal geen verdere beveiligingsupdates meer ontvangen.", + "system.issues.eol.php": "De geïnstalleerde PHP versie { release } is niet meer actueel en zal geen verdere beveiligingsupdates meer ontvangen.", + "system.issues.debug": "De debug modus moet uitgeschakeld zijn in productie", "system.issues.git": "De .git map lijkt zichtbaar te zijn", "system.issues.https": "We raden HTTPS aan voor al je sites", - "system.issues.kirby": "The kirby folder seems to be exposed", - "system.issues.site": "The site folder seems to be exposed", - "system.issues.vulnerability.kirby": "Your installation might be affected by the following vulnerability ({ severity } severity): { description }", - "system.issues.vulnerability.plugin": "Your installation might be affected by the following vulnerability in the { plugin } plugin ({ severity } severity): { description }", + "system.issues.kirby": "De kirby map lijkt zichtbaar te zijn", + "system.issues.local": "The site is running locally with relaxed security checks", + "system.issues.site": "De site map lijkt zichtbaar te zijn", + "system.issues.vue.compiler": "The Vue template compiler is enabled", + "system.issues.vulnerability.kirby": "De installatie is mogelijk getroffen door de volgende kwetsbaarheid ({ severity } ernst): { description }", + "system.issues.vulnerability.plugin": "De installatie is mogelijk getroffen door de volgende kwetsbaarheid in plugin { plugin } ({ severity } ernst): { description }", "system.updateStatus": "Update status", - "system.updateStatus.error": "Could not check for updates", - "system.updateStatus.not-vulnerable": "Geen gekende kwetsbaarheden", + "system.updateStatus.error": "Kan niet checken voor updates", + "system.updateStatus.not-vulnerable": "Geen bekende kwetsbaarheden", "system.updateStatus.security-update": "Gratis veiligheids update { version } beschikbaar", "system.updateStatus.security-upgrade": "Upgrade { version } met veiligheid aanpassingen beschikbaar", "system.updateStatus.unreleased": "Niet vrijgegeven versie", @@ -520,10 +694,19 @@ "system.updateStatus.update": "Gratis update { version } beschikbaar", "system.updateStatus.upgrade": "Upgrade { version } beschikbaar", - "title": "Titel", + "tel": "Telefoon", + "tel.placeholder": "+49123456789", "template": "Template", + + "theme": "Theme", + "theme.light": "Lights on", + "theme.dark": "Lights off", + "theme.automatic": "Match system default", + + "title": "Titel", "today": "Vandaag", + "toolbar.button.clear": "Verwijder formattering", "toolbar.button.code": "Code", "toolbar.button.bold": "Dikgedrukte tekst", "toolbar.button.email": "E-mailadres", @@ -541,6 +724,8 @@ "toolbar.button.link": "Link", "toolbar.button.paragraph": "Paragraaf", "toolbar.button.strike": "Doorstreept", + "toolbar.button.sub": "Subscript", + "toolbar.button.sup": "Superscript", "toolbar.button.ol": "Genummerde lijst", "toolbar.button.underline": "Onderlijn", "toolbar.button.ul": "Opsomming", @@ -550,6 +735,8 @@ "translation.name": "Nederlands", "translation.locale": "nl_NL", + "type": "Type", + "upload": "Upload", "upload.error.cantMove": "Het geüploadde bestand kon niet worden verplaatst", "upload.error.cantWrite": "Fout bij het schrijven van het bestand naar de schijf", @@ -574,6 +761,7 @@ "user.changeLanguage": "Taal veranderen", "user.changeName": "Gebruiker hernoemen", "user.changePassword": "Wachtwoord wijzigen", + "user.changePassword.current": "Your current password", "user.changePassword.new": "Nieuw wachtwoord", "user.changePassword.new.confirm": "Bevestig het nieuwe wachtwoord...", "user.changeRole": "Verander rol", @@ -585,10 +773,13 @@ "users": "Gebruikers", "version": "Kirby-versie", + "version.changes": "Changed version", + "version.compare": "Compare versions", "version.current": "Huidige versie", "version.latest": "Laatste versie", "versionInformation": "Versie informatie", + "view": "View", "view.account": "Jouw account", "view.installation": "Installatie", "view.languages": "Talen", diff --git a/kirby/i18n/translations/pl.json b/kirby/i18n/translations/pl.json index 8fd5bac..ae5a73b 100644 --- a/kirby/i18n/translations/pl.json +++ b/kirby/i18n/translations/pl.json @@ -3,44 +3,60 @@ "account.delete": "Usuń swoje konto", "account.delete.confirm": "Czy na pewno chcesz usunąć swoje konto? Zostaniesz natychmiast wylogowany. Twojego konta nie da się odzyskać.", + "activate": "Aktywuj", "add": "Dodaj", + "alpha": "Alfa", "author": "Autor", - "avatar": "Zdjęcie profilowe", + "avatar": "Zdj\u0119cie profilowe", "back": "Wróć", "cancel": "Anuluj", - "change": "Zmień", + "change": "Zmie\u0144", "close": "Zamknij", + "changes": "Zmiany", "confirm": "Ok", "collapse": "Zwiń", "collapse.all": "Zwiń wszystkie", + "color": "Kolor", + "coordinates": "Współrzędne", "copy": "Kopiuj", "copy.all": "Skopiuj wszystko", + "copy.success": "{count} skopiowanych!", + "copy.success.multiple": "{count} skopiowanych!", + "copy.url": "Copy URL", "create": "Utwórz", + "custom": "Niestandardowe", "date": "Data", "date.select": "Wybierz datę", "day": "Dzień", "days.fri": "Pt", - "days.mon": "Pon", + "days.mon": "Pn", "days.sat": "Sb", "days.sun": "Nd", "days.thu": "Czw", "days.tue": "Wt", - "days.wed": "Śr", + "days.wed": "\u015ar", "debugging": "Debugowanie", - "delete": "Usuń", + "delete": "Usu\u0144", "delete.all": "Usuń wszystkie", + "dialog.fields.empty": "To okno dialogowe nie zawiera żadnych pól", "dialog.files.empty": "Brak plików do wyboru", "dialog.pages.empty": "Brak stron do wyboru", + "dialog.text.empty": "To okno dialogowe nie definiuje żadnego tekstu", "dialog.users.empty": "Brak użytkowników do wyboru", "dimensions": "Wymiary", + "disable": "Wyłącz", "disabled": "Wyłączone", - "discard": "Odrzuć", + "discard": "Odrzu\u0107", + + "drawer.fields.empty": "Ten panel nie zawiera żadnych pól", + + "domain": "Domena", "download": "Pobierz", "duplicate": "Zduplikuj", @@ -49,11 +65,13 @@ "email": "Email", "email.placeholder": "mail@example.com", + "enter": "Wprowadź", "entries": "Wpisy", "entry": "Wpis", "environment": "Środowisko", + "error": "Błąd", "error.access.code": "Nieprawidłowy kod", "error.access.login": "Nieprawidłowy login", "error.access.panel": "Nie masz uprawnień by dostać się do panelu", @@ -74,13 +92,31 @@ "error.cache.type.invalid": "Nieprawidłowy typ pamięci podręcznej „{type}”", + "error.content.lock.delete": "The version is locked and cannot be deleted", + "error.content.lock.move": "The source version is locked and cannot be moved", + "error.content.lock.publish": "This version is already published", + "error.content.lock.replace": "The version is locked and cannot be replaced", + "error.content.lock.update": "The version is locked and cannot be updated", + + "error.entries.max.plural": "You must not add more than {max} entries", + "error.entries.max.singular": "You must not add more than one entry", + "error.entries.min.plural": "You must add at least {min} entries", + "error.entries.min.singular": "You must add at least one entry", + "error.entries.supports": "\"{type}\" field type is not supported for the entries field", + "error.entries.validation": "Wystąpił błąd w polu \"{field}\" w wierszu {index}", + "error.email.preset.notFound": "Nie udało się załadować wzorca wiadomości e-mail \"{name}\"", "error.field.converter.invalid": "Nieprawidłowy konwerter \"{converter}\"", + "error.field.link.options": "Invalid options: {options}", "error.field.type.missing": "Pole „{ name }”: Typ pola „{ type }” nie istnieje", "error.file.changeName.empty": "Imię nie może być puste", "error.file.changeName.permission": "Nie masz uprawnień, by zmienić nazwę \"{filename}\"", + "error.file.changeTemplate.invalid": "Szablonu pliku \"{id}\" nie można zmienić na \"{template}\" (poprawne: \"{blueprints}\")", + "error.file.changeTemplate.permission": "Nie masz uprawnień, by zmieniać szablon pliku \"{id}\"", + + "error.file.delete.multiple": "Not all files could be deleted. Try each remaining file individually to see the specific error that prevents deletion.", "error.file.duplicate": "Istnieje już plik o nazwie \"{filename}\"", "error.file.extension.forbidden": "Rozszerzenie \"{extension}\" jest niedozwolone", "error.file.extension.invalid": "Nieprawidłowe rozszerzenie: {extension}", @@ -95,9 +131,11 @@ "error.file.minheight": "Wysokość obrazka musi wynosić co najmniej {height} pikseli", "error.file.minsize": "Plik jest za mały", "error.file.minwidth": "Szerokość obrazka musi wynosić co najmniej {width} pikseli", + "error.file.name.unique": "Nazwa pliku musi być unikalna", "error.file.name.missing": "Nazwa pliku nie może być pusta", "error.file.notFound": "Nie można znaleźć pliku \"{filename}\"", "error.file.orientation": "Orientacja obrazka musi być \"{orientation}\"", + "error.file.sort.permission": "You are not allowed to change the sorting of \"{filename}\"", "error.file.type.forbidden": "Nie możesz przesyłać plików {type}", "error.file.type.invalid": "Nieprawidłowy typ pliku: {type}", "error.file.undefined": "Nie można znaleźć pliku", @@ -106,22 +144,30 @@ "error.form.notSaved": "Nie udało się zapisać formularza", "error.language.code": "Wprowadź poprawny kod języka.", + "error.language.create.permission": "You are not allowed to create a language", + "error.language.delete.permission": "You are not allowed to delete the language", "error.language.duplicate": "Język już istnieje.", "error.language.name": "Wprowadź poprawną nazwę języka.", "error.language.notFound": "Język nie został odnaleziony", + "error.language.update.permission": "You are not allowed to update the language", "error.layout.validation.block": "Wystąpił błąd w polu „{field}” w bloku {blockIndex} o typie bloku „{fieldset}” w układzie {layoutIndex}", "error.layout.validation.settings": "W ustawieniach układu {index} jest błąd", - "error.license.format": "Wprowadź poprawny klucz licencyjny", + "error.license.domain": "Brakuje domeny dla licencji", "error.license.email": "Wprowadź poprawny adres email", + "error.license.format": "Please enter a valid license code", "error.license.verification": "Nie udało się zweryfikować licencji", + "error.login.totp.confirm.invalid": "Nieprawidłowy kod", + "error.login.totp.confirm.missing": "Wpisz aktualny kod", + "error.object.validation": "Wystąpił błąd w polu „{label}”:\n{message}", "error.offline": "Panel jest obecnie offline", "error.page.changeSlug.permission": "Nie możesz zmienić końcówki adresu URL w \"{slug}\"", + "error.page.changeSlug.reserved": "Ścieżka stron najwyższego poziomu nie może zaczynać się od \"{path}\"", "error.page.changeStatus.incomplete": "Strona zawiera błędy i nie można jej opublikować", "error.page.changeStatus.permission": "Status tej strony nie może zostać zmieniony", "error.page.changeStatus.toDraft.invalid": "Strony \"{slug}\" nie można przekonwertować na szkic", @@ -133,10 +179,18 @@ "error.page.delete": "Strony \"{slug}\" nie można usunąć", "error.page.delete.confirm": "Wprowadź tytuł strony, aby potwierdzić", "error.page.delete.hasChildren": "Strona zawiera podstrony i nie można jej usunąć", + "error.page.delete.multiple": "Not all pages could be deleted. Try each remaining page individually to see the specific error that prevents deletion.", "error.page.delete.permission": "Nie masz uprawnień, by usunąć \"{slug}\"", "error.page.draft.duplicate": "Istnieje już szkic z końcówką URL \"{slug}\"", "error.page.duplicate": "Istnieje już strona z końcówką URL \"{slug}\"", "error.page.duplicate.permission": "Nie masz uprawnień, by zduplikować \"{slug}\"", + "error.page.move.ancestor": "Strony nie można przenieść do siebie samej", + "error.page.move.directory": "Nie można przenieść katalogu strony", + "error.page.move.duplicate": "Istnieje już podstrona z końcówką URL \"{slug}\"", + "error.page.move.noSections": "The page \"{parent}\" cannot be a parent of any page because it lacks any pages sections in its blueprint", + "error.page.move.notFound": "Przeniesiona strona nie została odnaleziona", + "error.page.move.permission": "Nie masz uprawnień, by przenieść \"{slug}\"", + "error.page.move.template": "Szablon \"{template}\" nie jest akceptowany jako podstrona \"{parent}\"", "error.page.notFound": "Nie można znaleźć strony \"{slug}\"", "error.page.num.invalid": "Wprowadź poprawny numer sortujący. Liczby nie mogą być ujemne.", "error.page.slug.invalid": "Wprowadź poprawną końcówkę adresu URL", @@ -163,6 +217,8 @@ "error.site.changeTitle.permission": "Nie masz uprawnień, by zmienić tytuł strony", "error.site.update.permission": "Nie masz uprawnień, by zaktualizować stronę", + "error.structure.validation": "Wystąpił błąd w polu \"{field}\" w wierszu {index}", + "error.template.default.notFound": "Domyślny szablon nie istnieje", "error.unexpected": "Wystąpił nieoczekiwany błąd! Włącz tryb debugowania, aby uzyskać więcej informacji: https://getkirby.com/docs/reference/system/options/debug", @@ -195,8 +251,10 @@ "error.validation.accepted": "Proszę potwierdzić", "error.validation.alpha": "Wprowadź tylko znaki między a-z", "error.validation.alphanum": "Wprowadź tylko znaki między a-z lub cyfry 0-9", + "error.validation.anchor": "Wprowadź poprawny odnośnik", "error.validation.between": "Wprowadź wartość między \"{min}\" i \"{max}\"", "error.validation.boolean": "Potwierdź lub odmów", + "error.validation.color": "Wprowadź poprawny kolor w formacie {format}", "error.validation.contains": "Wprowadź wartość, która zawiera \"{needle}\"", "error.validation.date": "Wprowadź poprawną datę", "error.validation.date.after": "Wprowadź datę późniejszą niż {date}", @@ -211,6 +269,7 @@ "error.validation.integer": "Wprowadź poprawną liczbę całkowitą", "error.validation.ip": "Wprowadź poprawny adres IP", "error.validation.less": "Wprowadź wartość mniejszą niż {max}", + "error.validation.linkType": "Typ łącza jest niedozwolony", "error.validation.match": "Wartość nie jest zgodna z oczekiwanym wzorcem", "error.validation.max": "Wprowadź wartość równą lub mniejszą niż {max}", "error.validation.maxlength": "Wprowadź krótszą wartość. (maks. {max} znaków)", @@ -227,15 +286,18 @@ "error.validation.same": "Wprowadź \"{other}\"", "error.validation.size": "Rozmiar wartości musi wynosić \"{size}\"", "error.validation.startswith": "Wartość musi zaczynać się od \"{start}\"", + "error.validation.tel": "Wprowadź niesformatowany numer telefonu", "error.validation.time": "Wprowadź poprawny czas", "error.validation.time.after": "Wprowadź czas późniejszy niż {time}", "error.validation.time.before": "Wprowadź czas wcześniejszy niż {time}", "error.validation.time.between": "Wprowadź czas między {min} a {max}", + "error.validation.uuid": "Wprowadź prawidłowy identyfikator UUID", "error.validation.url": "Wprowadź poprawny adres URL", "expand": "Rozwiń", "expand.all": "Rozwiń wszystkie", + "field.invalid": "Pole jest nieprawidłowe", "field.required": "Pole jest wymagane", "field.blocks.changeType": "Zmień typ", "field.blocks.code.name": "Kod", @@ -245,8 +307,9 @@ "field.blocks.delete.confirm.all": "Czy na pewno chcesz usunąć wszystkie bloki?", "field.blocks.delete.confirm.selected": "Czy na pewno chcesz usunąć wszystkie wybrane bloki?", "field.blocks.empty": "Nie ma jeszcze żadnych bloków", + "field.blocks.fieldsets.empty": "Nie ma jeszcze zestawów pól", "field.blocks.fieldsets.label": "Wybierz typ bloku …", - "field.blocks.fieldsets.paste": "Wciśnij {{ shortcut }} by wkleić/zaimportować bloki ze schowka", + "field.blocks.fieldsets.paste": "Naciśnij {{ shortcut }}, aby zaimportować układy/bloki ze schowka. Zostaną wstawione tylko te, które są dozwolone w bieżącym polu.", "field.blocks.gallery.name": "Galeria", "field.blocks.gallery.images.empty": "Nie ma jeszcze żadnych obrazków", "field.blocks.gallery.images.label": "Obrazki", @@ -254,11 +317,16 @@ "field.blocks.heading.name": "Nagłówek", "field.blocks.heading.text": "Tekst", "field.blocks.heading.placeholder": "Nagłówek …", + "field.blocks.figure.back.plain": "Plain", + "field.blocks.figure.back.pattern.light": "Pattern (light)", + "field.blocks.figure.back.pattern.dark": "Pattern (dark)", "field.blocks.image.alt": "Tekst alternatywny", "field.blocks.image.caption": "Podpis", "field.blocks.image.crop": "Przytnij", "field.blocks.image.link": "Link", "field.blocks.image.location": "Lokalizacja", + "field.blocks.image.location.internal": "Ta witryna", + "field.blocks.image.location.external": "Zewnętrzne źródło", "field.blocks.image.name": "Obrazek", "field.blocks.image.placeholder": "Wybierz obrazek", "field.blocks.image.ratio": "Proporcje", @@ -275,38 +343,72 @@ "field.blocks.quote.citation.placeholder": "autorstwa …", "field.blocks.text.name": "Tekst", "field.blocks.text.placeholder": "Tekst …", + "field.blocks.video.autoplay": "Autoplay", "field.blocks.video.caption": "Podpis", + "field.blocks.video.controls": "Controls", + "field.blocks.video.location": "Lokalizacja", + "field.blocks.video.loop": "Loop", + "field.blocks.video.muted": "Muted", "field.blocks.video.name": "Video", "field.blocks.video.placeholder": "Wprowadź URL video", + "field.blocks.video.poster": "Poster", + "field.blocks.video.preload": "Preload", "field.blocks.video.url.label": "URL video", "field.blocks.video.url.placeholder": "https://youtube.com/?v=", - "field.files.empty": "Nie wybrano jeszcze żadnych plików", + "field.entries.delete.confirm.all": "Czy na pewno chcesz usunąć wszystkie wpisy?", + "field.entries.empty": "Nie ma jeszcze żadnych wpisów.", + "field.files.empty": "Nie wybrano jeszcze żadnych plików", + "field.files.empty.single": "No file selected yet", + + "field.layout.change": "Zmień układ", "field.layout.delete": "Usuń układ", "field.layout.delete.confirm": "Czy na pewno chcesz usunąć ten układ?", + "field.layout.delete.confirm.all": "Czy na pewno chcesz usunąć wszystkie układy?", "field.layout.empty": "Nie ma jeszcze żadnych rzędów", "field.layout.select": "Wybierz układ", "field.object.empty": "Brak informacji", "field.pages.empty": "Nie wybrano jeszcze żadnych stron", + "field.pages.empty.single": "No page selected yet", "field.structure.delete.confirm": "Czy na pewno chcesz usunąć ten wiersz?", "field.structure.delete.confirm.all": "Czy na pewno chcesz usunąć wszystkie wpisy?", - "field.structure.empty": "Nie ma jeszcze żadnych wpisów.", + "field.structure.empty": "Nie ma jeszcze \u017cadnych wpis\u00f3w.", "field.users.empty": "Nie wybrano jeszcze żadnych użytkowników", + "field.users.empty.single": "No user selected yet", + "fields.empty": "Nie ma jeszcze żadnych pól", + + "file": "Plik", "file.blueprint": "Ten plik nie ma jeszcze wzorca. Możesz go zdefiniować w /site/blueprints/files/{blueprint}.yml", + "file.changeTemplate": "Zmień szablon", + "file.changeTemplate.notice": "Zmiana szablonu pliku spowoduje usunięcie zawartości pól, które nie pasują pod względem typu. Jeżeli nowy szablon określa pewne zasady, np. wymiarów obrazu, one również zostaną zastosowane nieodwracalnie. Używaj ostrożnie.", "file.delete.confirm": "Czy na pewno chcesz usunąć
{filename}?", + "file.focus.placeholder": "Ustaw punkt centralny", + "file.focus.reset": "Usuń punkt centralny", + "file.focus.title": "Punkt centralny", "file.sort": "Zmień pozycję", "files": "Pliki", + "files.delete.confirm.selected": "Do you really want to delete the selected files? This action cannot be undone.", "files.empty": "Nie ma jeszcze żadnych plików", + "filter": "Filtr", + + "form.discard": "Discard changes", + "form.discard.confirm": "Do you really want to discard all your changes?", + "form.locked": "This content is disabled for you as it is currently edited by another user", + "form.unsaved": "The current changes have not yet been saved", + "form.preview": "Preview changes", + "form.preview.draft": "Preview draft", + "hide": "Ukryj", "hour": "Godzina", + "hue": "Odcień", "import": "Importuj", "info": "Informacje", "insert": "Wstaw", @@ -324,14 +426,14 @@ "installation.issues.mbstring": "Wymagane jest rozszerzenie MB String", "installation.issues.media": "Folder /media nie istnieje lub nie ma uprawnień do zapisu", "installation.issues.php": "Upewnij się, że używasz PHP 8+", - "installation.issues.server": "Kirby wymaga Apache, Nginx lub Caddy", "installation.issues.sessions": "Folder /site/sessions nie istnieje lub nie ma uprawnień do zapisu", - "language": "Język", + "language": "J\u0119zyk", "language.code": "Kod", "language.convert": "Ustaw jako domyślny", "language.convert.confirm": "

Czy na pewno chcesz zmienić domyślny język na {name}? Nie można tego cofnąć.

Jeżeli brakuje tłumaczenia jakichś treści na {name}, nie będzie ich czym zastąpić i części witryny mogą być puste.

", "language.create": "Dodaj nowy język", + "language.default": "Domyślny język", "language.delete.confirm": "Czy na pewno chcesz usunąć język {name} i wszystkie tłumaczenia? Tego nie da się cofnąć!", "language.deleted": "Język został usunięty", "language.direction": "Kierunek czytania", @@ -340,7 +442,16 @@ "language.locale": "PHP locale string", "language.locale.warning": "Używasz niestandardowej konfiguracji ustawień regionalnych. Zmodyfikuj to w pliku języka w /site/langugaes", "language.name": "Nazwa", + "language.secondary": "Drugorzędny język", + "language.settings": "Ustawienia języków", "language.updated": "Język został zaktualizowany", + "language.variables": "Zmienne językowe", + "language.variables.empty": "Nie ma jeszcze żadnych tłumaczeń", + + "language.variable.delete.confirm": "Czy na pewno chcesz usunąć zmienną przypisaną do klucza {key}?", + "language.variable.key": "Klucz", + "language.variable.notFound": "Nie udało się odnaleźć zmiennej", + "language.variable.value": "Wartość", "languages": "Języki", "languages.default": "Domyślny język", @@ -349,15 +460,32 @@ "languages.secondary.empty": "Nie ma jeszcze dodatkowych języków", "license": "Licencja", + "license.activate": "Aktywuj teraz", + "license.activate.label": "Aktywuj swoją licencję", + "license.activate.domain": "Twoja licencja zostanie aktywowana na {host}.", + "license.activate.local": "Zamierzasz aktywować licencję Kirby dla domeny lokalnej {host}. Jeżeli ta strona będzie uruchamiana w publicznie dostępnej domenie, należy ją aktywować tam. Jeśli {host} jest domeną, dla której chcesz używać licencji, kontynuuj.", + "license.activated": "Aktywowana", "license.buy": "Kup licencję", - "license.register": "Zarejestruj", + "license.code": "Kod", + "license.code.help": "Po zakupie otrzymałeś emailem kod licencyjny. Skopiuj go i wklej tutaj.", + "license.code.label": "Wprowadź swój kod licencji", + "license.status.active.info": "Zawiera nowe główne wersje do {date}", + "license.status.active.label": "Ważna licencja", + "license.status.demo.info": "This is a demo installation", + "license.status.demo.label": "Demo", + "license.status.inactive.info": "Odnów licencję, by zaktualizować do nowych wersji głównych", + "license.status.inactive.label": "Brak nowych wersji głównych", + "license.status.legacy.bubble": "Gotowy/-a do odnowienia swojej licencji?", + "license.status.legacy.info": "Twoja licencja nie obejmuje tej wersji", + "license.status.legacy.label": "Odnów swoją licencję", + "license.status.missing.bubble": "Gotowy/-a do uruchomienia strony?", + "license.status.missing.info": "Brak ważnej licencji", + "license.status.missing.label": "Aktywuj swoją licencję", + "license.status.unknown.info": "The license status is unknown", + "license.status.unknown.label": "Unknown", "license.manage": "Zarządzaj swoimi licencjami", - "license.register.help": "Po zakupieniu licencji otrzymałaś/-eś mailem klucz. Skopiuj go i wklej tutaj, aby dokonać rejestracji.", - "license.register.label": "Wprowadź swój kod licencji", - "license.register.domain": "Twoja licencja zostanie zarejestrowana na {host}.", - "license.register.local": "Zamierzasz zarejestrować licencję dla swojej domeny lokalnej {host}. Jeśli ta witryna zostanie wdrożona w domenie ogólnodostępnej, zarejestruj ją tam. Jeżeli {host} jest faktycznie domeną, do której chcesz przypisać licencję, kontynuuj.", - "license.register.success": "Dziękujemy za wspieranie Kirby", - "license.unregistered": "To jest niezarejestrowana wersja demonstracyjna Kirby", + "license.purchased": "Zakupiona", + "license.success": "Dziękujemy za wspieranie Kirby", "license.unregistered.label": "Niezarejestrowane", "link": "Link", @@ -367,17 +495,21 @@ "lock.unsaved": "Niezapisane zmiany", "lock.unsaved.empty": "Nie ma już żadnych niezapisanych zmian", - "lock.isLocked": "Niezapisane zmiany autorstwa {email}", - "lock.file.isLocked": "Plik jest aktualnie edytowany przez {email} i nie może zostać zmieniony.", - "lock.page.isLocked": "Strona jest aktualnie edytowana przez {email} i nie może zostać zmieniona.", + "lock.unsaved.files": "Unsaved files", + "lock.unsaved.pages": "Unsaved pages", + "lock.unsaved.users": "Unsaved accounts", + "lock.isLocked": "Niezapisane zmiany autorstwa {email}", "lock.unlock": "Odblokuj", - "lock.isUnlocked": "Twoje niezapisane zmiany zostały nadpisane przez innego użytkownika. Możesz pobrać swoje zmiany, by scalić je ręcznie.", + "lock.unlock.submit": "Odblokuj i nadpisz niezapisane zmiany autorstwa {email}", + "lock.isUnlocked": "Zostało odblokowane przez innego użytkownika", "login": "Zaloguj się", "login.code.label.login": "Kod logowania się", "login.code.label.password-reset": "Kod resetowania hasła", "login.code.placeholder.email": "000 000", + "login.code.placeholder.totp": "000000", "login.code.text.email": "Jeśli Twój adres email jest zarejestrowany, żądany kod został wysłany na Twoją skrzynkę.", + "login.code.text.totp": "Wprowadź jednorazowy kod z aplikacji uwierzytelniającej.", "login.email.login.body": "Cześć {user.nameOrEmail},\n\nNiedawno poprosiłaś/-eś o kod do zalogowania się do panelu strony {site}.\nPoniższy kod do zalogowania się będzie ważny przez {timeout} minut:\n\n{code}\n\nJeżeli nie zażądałaś/-eś kodu do logowania się, zignoruj tę wiadomość e-mail lub skontaktuj się z administratorem, jeśli masz pytania.\nZe względów bezpieczeństwa NIE przesyłaj dalej tego e-maila.", "login.email.login.subject": "Twój kod logowania się", "login.email.password-reset.body": "Cześć {user.nameOrEmail},\n\nNiedawno poprosiłaś/-eś o kod resetowania hasła do panelu strony {site}.\nPoniższy kod resetowania hasła będzie ważny przez {timeout} minut:\n\n{code}\n\nJeżeli nie zażądałaś/-eś kodu resetowania hasła, zignoruj tę wiadomość e-mail lub skontaktuj się z administratorem, jeśli masz pytania. \nZe względów bezpieczeństwa NIE przesyłaj dalej tego e-maila. ", @@ -388,58 +520,80 @@ "login.toggleText.code.email-password": "Zaloguj się za pomocą hasła", "login.toggleText.password-reset.email": "Zapomniałeś/-aś hasła?", "login.toggleText.password-reset.email-password": "← Powrót do logowania", + "login.totp.enable.option": "Ustaw kody jednorazowe", + "login.totp.enable.intro": "Aplikacje uwierzytelniające mogą generować jednorazowe kody, które są używane jako drugi czynnik podczas logowania się na konto.", + "login.totp.enable.qr.label": "1. Zeskanuj ten kod QR", + "login.totp.enable.qr.help": "Nie możesz zeskanować? Dodaj ręcznie klucz instalacyjny {secret} do aplikacji uwierzytelniającej.", + "login.totp.enable.confirm.headline": "2. Potwierdź wygenerowanym kodem", + "login.totp.enable.confirm.text": "Aplikacja generuje nowy kod jednorazowy co 30 sekund. Wprowadź aktualny kod, aby dokończyć konfigurację:", + "login.totp.enable.confirm.label": "Aktualny kod", + "login.totp.enable.confirm.help": "Po tej konfiguracji będziemy prosić o jednorazowy kod przy każdym logowaniu.", + "login.totp.enable.success": "Kody jednorazowe włączone", + "login.totp.disable.option": "Wyłącz kody jednorazowe", + "login.totp.disable.label": "Wprowadź swoje hasło, aby wyłączyć kody jednorazowe", + "login.totp.disable.help": "W przyszłości podczas logowania wymagany będzie inny drugi czynnik, taki jak kod logowania wysłany emailem. Kody jednorazowe możesz zawsze skonfigurować później.", + "login.totp.disable.admin": "

Spowoduje to wyłączenie kodów jednorazowych dla użytkownika {user}.

W przyszłości podczas logowania wymagany będzie inny drugi czynnik, taki jak kod logowania wysłany emailem. {user} może ponownie skonfigurować kody jednorazowe po następnym zalogowaniu.

", + "login.totp.disable.success": "Kody jednorazowe wyłączone", "logout": "Wyloguj się", + "merge": "Połącz", "menu": "Menu", "meridiem": "AM/PM", "mime": "Typ multimediów", "minutes": "Minuty", "month": "Miesiąc", - "months.april": "Kwiecień", - "months.august": "Sierpień", - "months.december": "Grudzień", + "months.april": "Kwiecie\u0144", + "months.august": "Sierpie\u0144", + "months.december": "Grudzie\u0144", "months.february": "Luty", - "months.january": "Styczeń", + "months.january": "Stycze\u0144", "months.july": "Lipiec", "months.june": "Czerwiec", "months.march": "Marzec", "months.may": "Maj", "months.november": "Listopad", - "months.october": "Październik", - "months.september": "Wrzesień", + "months.october": "Pa\u017adziernik", + "months.september": "Wrzesie\u0144", "more": "Więcej", + "move": "Przenieś", "name": "Nazwa", "next": "Następne", + "night": "Noc", "no": "nie", "off": "wyłączone", "on": "włączone", "open": "Otwórz", "open.newWindow": "Otwórz w nowym oknie", + "option": "Opcja", "options": "Opcje", "options.none": "Brak opcji", + "options.all": "Pokaż wszystkie {count} opcje/-i", "orientation": "Orientacja", "orientation.landscape": "Pozioma", "orientation.portrait": "Pionowa", "orientation.square": "Kwadrat", + "page": "Strona", "page.blueprint": "Ta strona nie ma jeszcze wzorca. Możesz go zdefiniować w /site/blueprints/pages/{blueprint}.yml", - "page.changeSlug": "Zmień URL", - "page.changeSlug.fromTitle": "Utwórz na podstawie tytułu", + "page.changeSlug": "Zmie\u0144 URL", + "page.changeSlug.fromTitle": "Utw\u00f3rz na podstawie tytu\u0142u", "page.changeStatus": "Zmień status", "page.changeStatus.position": "Wybierz pozycję", "page.changeStatus.select": "Wybierz nowy status", "page.changeTemplate": "Zmień szablon", + "page.changeTemplate.notice": "Zmiana szablonu strony spowoduje usunięcie treści z pól, które nie pasują pod względem typu. Używaj ostrożnie.", + "page.create": "Utwórz jako {status}", "page.delete.confirm": "Czy na pewno chcesz usunąć {title}?", "page.delete.confirm.subpages": "Ta strona zawiera podstrony.
Wszystkie podstrony również zostaną usunięte.", "page.delete.confirm.title": "Wprowadź tytuł strony, aby potwierdzić", - "page.draft.create": "Utwórz szkic", "page.duplicate.appendix": "Kopiuj", "page.duplicate.files": "Kopiuj pliki", "page.duplicate.pages": "Kopiuj strony", + "page.move": "Przenieś stronę", "page.sort": "Zmień pozycję", "page.status": "Status", "page.status.draft": "Szkic", @@ -450,6 +604,7 @@ "page.status.unlisted.description": "Strona jest dostępna tylko za pośrednictwem adresu URL", "pages": "Strony", + "pages.delete.confirm.selected": "Do you really want to delete the selected pages? This action cannot be undone.", "pages.empty": "Nie ma jeszcze żadnych stron", "pages.status.draft": "Szkice", "pages.status.listed": "Opublikowane", @@ -457,19 +612,26 @@ "pagination.page": "Strona", - "password": "Hasło", + "password": "Has\u0142o", "paste": "Wklej", "paste.after": "Wklej po", + "paste.success": "{count} wklejonych!", "pixel": "Piksel", "plugin": "Wtyczka", "plugins": "Wtyczki", "prev": "Poprzednie", "preview": "Podgląd", + + "publish": "Publish", + "published": "Opublikowane", + "remove": "Usuń", "rename": "Zmień nazwę", - "replace": "Zastąp", - "retry": "Ponów próbę", - "revert": "Odrzuć", + "renew": "Odnów", + "replace": "Zamie\u0144", + "replace.with": "Zamień z", + "retry": "Pon\u00f3w pr\u00f3b\u0119", + "revert": "Odrzu\u0107", "revert.confirm": "Czy na pewno chcesz usunąć wszystkie niezapisane zmiany?", "role": "Rola", @@ -482,11 +644,14 @@ "role.nobody.title": "Nikt", "save": "Zapisz", + "saved": "Saved", "search": "Szukaj", + "searching": "Searching", "search.min": "Aby wyszukać, wprowadź co najmniej {min} znaków", - "search.all": "Pokaż wzystkie", + "search.all": "Pokaż wszystkie {count} wyniki/-ów", "search.results.none": "Brak wyników", + "section.invalid": "Sekcja jest nieprawidłowa", "section.required": "Sekcja jest wymagana", "security": "Bezpieczeństwo", @@ -498,16 +663,25 @@ "size": "Rozmiar", "slug": "Końcówka URL", "sort": "Sortuj", + "sort.drag": "Przeciągnij, aby posortować…", + "split": "Podziel", "stats.empty": "Brak raportów", + "status": "Status", + + "system.info.copy": "Copy info", + "system.info.copied": "System info copied", "system.issues.content": "Zdaje się, że folder „content” jest wystawiony na publiczny dostęp", "system.issues.eol.kirby": "Twoja zainstalowana wersja Kirby osiągnęła koniec okresu wsparcia i nie będzie otrzymywać dalszych aktualizacji zabezpieczeń", "system.issues.eol.plugin": "Twoja zainstalowana wersja wtyczki { plugin } osiągnęła koniec okresu wsparcia i nie będzie otrzymywać dalszych aktualizacji zabezpieczeń", + "system.issues.eol.php": "Zainstalowana wersja PHP { release } osiągnęła koniec okresu eksploatacji i nie będzie otrzymywać dalszych aktualizacji zabezpieczeń.", "system.issues.debug": "Debugowanie musi być wyłączone w środowisku produkcyjnym", "system.issues.git": "Zdaje się, że folder „.git” jest wystawiony na publiczny dostęp", "system.issues.https": "Zalecamy HTTPS dla wszystkich Twoich witryn", "system.issues.kirby": "Zdaje się, że folder „kirby” jest wystawiony na publiczny dostęp", + "system.issues.local": "The site is running locally with relaxed security checks", "system.issues.site": "Zdaje się, że folder „site” jest wystawiony na publiczny dostęp", + "system.issues.vue.compiler": "The Vue template compiler is enabled", "system.issues.vulnerability.kirby": "Twojej instalacji może zagrażać następująca luka w zabezpieczeniach ({ severity } stopień): { description }", "system.issues.vulnerability.plugin": "Twojej instalacji może zagrażać następująca luka w zabezpieczeniach we wtyczce { plugin } ({ severity } poziom): { description }", "system.updateStatus": "Stan aktualizacji", @@ -520,10 +694,19 @@ "system.updateStatus.update": "Dostępna darmowa aktualizacja { version }", "system.updateStatus.upgrade": "Dostępna aktualizacja { version }", - "title": "Tytuł", + "tel": "Telefon", + "tel.placeholder": "+48123456789", "template": "Szablon", + + "theme": "Theme", + "theme.light": "Lights on", + "theme.dark": "Lights off", + "theme.automatic": "Match system default", + + "title": "Tytuł", "today": "Dzisiaj", + "toolbar.button.clear": "Wyczyść formatowanie", "toolbar.button.code": "Kod", "toolbar.button.bold": "Pogrubienie", "toolbar.button.email": "Email", @@ -541,6 +724,8 @@ "toolbar.button.link": "Link", "toolbar.button.paragraph": "Akapit", "toolbar.button.strike": "Przekreślenie", + "toolbar.button.sub": "Indeks dolny", + "toolbar.button.sup": "Indeks górny", "toolbar.button.ol": "Lista numerowana", "toolbar.button.underline": "Podkreślenie", "toolbar.button.ul": "Lista wypunktowana", @@ -550,6 +735,8 @@ "translation.name": "Polski", "translation.locale": "pl_PL", + "type": "Typ", + "upload": "Prześlij", "upload.error.cantMove": "Przesłany plik nie mógł być przeniesiony", "upload.error.cantWrite": "Nie udało się zapisać pliku na dysku", @@ -574,6 +761,7 @@ "user.changeLanguage": "Zmień język", "user.changeName": "Zmień nazwę tego użytkownika", "user.changePassword": "Zmień hasło", + "user.changePassword.current": "Your current password", "user.changePassword.new": "Nowe hasło", "user.changePassword.new.confirm": "Potwierdź nowe hasło…", "user.changeRole": "Zmień rolę", @@ -585,17 +773,20 @@ "users": "Użytkownicy", "version": "Wersja", + "version.changes": "Changed version", + "version.compare": "Compare versions", "version.current": "Obecna wersja", "version.latest": "Ostatnia wersja", "versionInformation": "Informacje o wersji", + "view": "View", "view.account": "Twoje konto", "view.installation": "Instalacja", "view.languages": "Języki", "view.resetPassword": "Zresetuj hasło", "view.site": "Strona", "view.system": "System", - "view.users": "Użytkownicy", + "view.users": "U\u017cytkownicy", "welcome": "Witaj", "year": "Rok", diff --git a/kirby/i18n/translations/pt_BR.json b/kirby/i18n/translations/pt_BR.json index 379711a..aba221d 100644 --- a/kirby/i18n/translations/pt_BR.json +++ b/kirby/i18n/translations/pt_BR.json @@ -3,30 +3,39 @@ "account.delete": "Deletar sua conta", "account.delete.confirm": "Deseja realmente deletar sua conta? Você sairá do site imediatamente. Sua conta não poderá ser recuperada. ", - "add": "Add", + "activate": "Ativar", + "add": "Adicionar", + "alpha": "Alpha", "author": "Autor", "avatar": "Foto do perfil", - "back": "Back", - "cancel": "Cancel", + "back": "Voltar", + "cancel": "Cancelar", "change": "Alterar", - "close": "Close", + "close": "Fechar", + "changes": "Alterações", "confirm": "Salvar", "collapse": "Colapsar", "collapse.all": "Colapsar todos", + "color": "Cor", + "coordinates": "Coordenadas", "copy": "Copiar", "copy.all": "Copiar todos", + "copy.success": "{count} copiados!", + "copy.success.multiple": "{count} copiados!", + "copy.url": "Copiar URL", "create": "Criar", + "custom": "Personalizado", "date": "Data", "date.select": "Selecione uma data", "day": "Dia", "days.fri": "Sex", - "days.mon": "Mon", - "days.sat": "Sáb", + "days.mon": "Seg", + "days.sat": "S\u00e1b", "days.sun": "Dom", "days.thu": "Qui", - "days.tue": "Tue", + "days.tue": "Ter", "days.wed": "Qua", "debugging": "Depuração ", @@ -34,26 +43,35 @@ "delete": "Deletar", "delete.all": "Deletar todos", + "dialog.fields.empty": "Esta caixa de diálogo não tem campos", "dialog.files.empty": "Nenhum arquivo para selecionar", "dialog.pages.empty": "Nenhuma página para selecionar", + "dialog.text.empty": "Esta caixa de diálogo não define nenhum texto", "dialog.users.empty": "Nenhum usuário para selecionar", "dimensions": "Dimensões", + "disable": "Desativar", "disabled": "Desativado", - "discard": "Discard", + "discard": "Descartar", + + "drawer.fields.empty": "Esta janela não tem campos", + + "domain": "Domínio", "download": "Baixar", "duplicate": "Duplicar", - "edit": "Edit", + "edit": "Editar", "email": "Email", - "email.placeholder": "mail@exemplo.pt", + "email.placeholder": "mail@exemplo.com", - "entries": "Entries", - "entry": "Entry", + "enter": "Insira", + "entries": "Registos", + "entry": "Registo", "environment": "Ambiente", + "error": "Erro", "error.access.code": "Código inválido", "error.access.login": "Código de acesso inválido", "error.access.panel": "Você não tem permissão para acessar o painel", @@ -70,17 +88,35 @@ "error.blocks.max.singular": "Você não deve adicionar mais do que um bloco", "error.blocks.min.plural": "Você deve adicionar pelo menos {min} blocos", "error.blocks.min.singular": "Você deve adicionar pelo menos um bloco", - "error.blocks.validation": "There's an error on the \"{field}\" field in block {index} using the \"{fieldset}\" block type", + "error.blocks.validation": "Há um erro no campo \"{field}\" no bloco {index} a usar o tipo de bloco \"{fieldset}\"", - "error.cache.type.invalid": "Invalid cache type \"{type}\"", + "error.cache.type.invalid": "Tipo de cache \"{type}\" inválido", + + "error.content.lock.delete": "A versão está bloqueada e não pode ser eliminada", + "error.content.lock.move": "A versão está bloqueada e não pode ser movida", + "error.content.lock.publish": "Esta versão já se encontra publicada", + "error.content.lock.replace": "A versão está bloqueada e não pode ser substituída", + "error.content.lock.update": "A versão está bloqueada e não pode ser atualizada", + + "error.entries.max.plural": "Não deve adicionar mais do que {max} entradas", + "error.entries.max.singular": "Não deve adicionar mais do que uma entrada", + "error.entries.min.plural": "Deve adicionar pelo menos {min} entradas", + "error.entries.min.singular": "Deve adicionar pelo menos uma entrada", + "error.entries.supports": "O tipo de campo \"{type}\" não é compatível com o campo entries", + "error.entries.validation": "Existe um erro no campo \"{field}\" na linha {index}", "error.email.preset.notFound": "Pré-configuração de email \"{name}\" não foi encontrada", "error.field.converter.invalid": "Conversor \"{converter}\" inválido", - "error.field.type.missing": "Field \"{ name }\": The field type \"{ type }\" does not exist", + "error.field.link.options": "Opções inválidas: {options}", + "error.field.type.missing": "Campo \"{name}\": O tipo de campo \"{type}\" não existe", "error.file.changeName.empty": "O nome não deve ficar em branco", "error.file.changeName.permission": "Você não tem permissão para alterar o nome de \"{filename}\"", + "error.file.changeTemplate.invalid": "O template para o ficheiro \"{id}\" não pode ser alterado para \"{template}\" (válido: \"{blueprints}\")", + "error.file.changeTemplate.permission": "Não tem permissão para alterar o template do ficheiro \"{id}\"", + + "error.file.delete.multiple": "Nem todos os ficheiros puderam ser eliminados. Experimente cada ficheiro restante individualmente para ver o erro específico que impede a sua eliminação.", "error.file.duplicate": "Um arquivo com o nome \"{filename}\" já existe", "error.file.extension.forbidden": "Extensão \"{extension}\" não permitida", "error.file.extension.invalid": "Extensão inválida: {extension}", @@ -95,33 +131,43 @@ "error.file.minheight": "A altura da imagem deve ser pelo menos {height} pixels", "error.file.minsize": "O arquivo é pequeno demais", "error.file.minwidth": "A largura da imagem deve ser pelo menos {width} pixels", + "error.file.name.unique": "O nome do ficheiro deve ser único", "error.file.name.missing": "O nome do arquivo não pode ficar em branco", "error.file.notFound": "Arquivo \"{filename}\" não encontrado", "error.file.orientation": "A orientação da imagem deve ser “{orientation}”", + "error.file.sort.permission": "Não tem permissão para alterar a ordem de \"{filename}\"", "error.file.type.forbidden": "Você não tem permissão para enviar arquivos {type}", "error.file.type.invalid": "Tipo inválido de arquivo: {type}", - "error.file.undefined": "Arquivo não encontrado", + "error.file.undefined": "Arquivo n\u00e3o encontrado", "error.form.incomplete": "Por favor, corrija os erros do formulário…", "error.form.notSaved": "O formulário não pôde ser salvo", "error.language.code": "Por favor entre um código válido para o idioma", + "error.language.create.permission": "Não tem permissões para criar um idioma", + "error.language.delete.permission": "Não tem permissões para eliminar o idioma", "error.language.duplicate": "O idioma já existe", "error.language.name": "Por favor entre um nome válido para o idioma", "error.language.notFound": "O idioma não foi encontrado", + "error.language.update.permission": "Não tem permissões para atualizar o idioma", - "error.layout.validation.block": "There's an error on the \"{field}\" field in block {blockIndex} using the \"{fieldset}\" block type in layout {layoutIndex}", + "error.layout.validation.block": "Há um erro no campo \"{field}\" no bloco {blockIndex} a usar o tipo de bloco \"{fieldset}\" no layout {layoutIndex}", "error.layout.validation.settings": "Há um erro na configuração do layout {index}", - "error.license.format": "Por favor entre uma chave de licensa válida ", + "error.license.domain": "O domínio da licença está em falta", "error.license.email": "Digite um endereço de email válido", + "error.license.format": "Por favor insira um código de licença válido", "error.license.verification": "A licensa não pôde ser verificada", - "error.object.validation": "There’s an error in the \"{label}\" field:\n{message}", + "error.login.totp.confirm.invalid": "Código inválido", + "error.login.totp.confirm.missing": "Por favor insira o código atual", + + "error.object.validation": "Há um erro no campo \"{label}\":\n{message}", "error.offline": "O painel está offline no momento", "error.page.changeSlug.permission": "Você não tem permissão para alterar o anexo de URL de \"{slug}\"", + "error.page.changeSlug.reserved": "O caminho das páginas de nível superior não deve começar com \"{path}\"", "error.page.changeStatus.incomplete": "A página possui erros e não pode ser salva", "error.page.changeStatus.permission": "O estado desta página não pode ser alterado", "error.page.changeStatus.toDraft.invalid": "A página \"{slug}\" não pode ser convertida para rascunho", @@ -133,17 +179,25 @@ "error.page.delete": "A página \"{slug}\" não pode ser deletada", "error.page.delete.confirm": "Por favor, digite o título da página para confirmar", "error.page.delete.hasChildren": "A página possui subpáginas e não pode ser deletada", + "error.page.delete.multiple": "Nem todas as páginas puderam ser eliminadas. Experimente cada página restante individualmente para ver o erro específico que impede a sua eliminação.", "error.page.delete.permission": "Você não tem permissão para deletar \"{slug}\"", "error.page.draft.duplicate": "Uma página rascunho com um anexo de URL \"{slug}\" já existe", "error.page.duplicate": "Uma página com o anexo de URL \"{slug}\" já existe", "error.page.duplicate.permission": "Você não tem permissão para duplicar “{slug}”", + "error.page.move.ancestor": "A página não pode ser movida para dentro dela mesma", + "error.page.move.directory": "A pasta da página não pode ser movida", + "error.page.move.duplicate": "Uma subpágina com o segmento de URL \"{slug}\" já existe", + "error.page.move.noSections": "A página \"{parent}\" não pode ser pai de nenhuma página porque não tem secções de páginas na sua blueprint", + "error.page.move.notFound": "A página movida não foi encontrada", + "error.page.move.permission": "Não tem permissão para mover \"{slug}\"", + "error.page.move.template": "O template \"{template}\" não é aceite como subpágina de \"{parent}\"", "error.page.notFound": "Página \"{slug}\" não encontrada", "error.page.num.invalid": "Digite um número de ordenação válido. Este número não pode ser negativo.", "error.page.slug.invalid": "Por favor entre um anexo de URL válido ", "error.page.slug.maxlength": "O slug deve ter menos de “{length}” caracteres", "error.page.sort.permission": "A página \"{slug}\" não pode ser ordenada", "error.page.status.invalid": "Por favor, defina um estado de página válido", - "error.page.undefined": "Página não encontrada", + "error.page.undefined": "P\u00e1gina n\u00e3o encontrada", "error.page.update.permission": "Você não tem permissão para atualizar \"{slug}\"", "error.section.files.max.plural": "Você não pode adicionar mais do que {max} arquivos à seção \"{section}\"", @@ -163,9 +217,11 @@ "error.site.changeTitle.permission": "Você não tem permissão para alterar o título do site", "error.site.update.permission": "Você não tem permissão para atualizar o site", + "error.structure.validation": "Existe um erro no campo \"{field}\" na linha {index}", + "error.template.default.notFound": "O tema padrão não existe", - "error.unexpected": "An unexpected error occurred! Enable debug mode for more info: https://getkirby.com/docs/reference/system/options/debug", + "error.unexpected": "Ocorreu um erro inesperado! Ative o modo de debug para obter mais informações: https://getkirby.com/docs/reference/system/options/debug", "error.user.changeEmail.permission": "Você não tem permissão para alterar o email do usuário \"{name}\"", "error.user.changeLanguage.permission": "Você não tem permissão para alterar o idioma do usuário \"{name}\"", @@ -183,7 +239,7 @@ "error.user.email.invalid": "Digite um endereço de email válido", "error.user.language.invalid": "Digite um idioma válido", "error.user.notFound": "Usuário \"{name}\" não encontrado", - "error.user.password.excessive": "Please enter a valid password. Passwords must not be longer than 1000 characters.", + "error.user.password.excessive": "Por favor insira uma palavra-passe válida. As palavras-passe não devem ter mais do que 1000 caracteres.", "error.user.password.invalid": "Digite uma senha válida. Sua senha deve ter pelo menos 8 caracteres.", "error.user.password.notSame": "As senhas não combinam", "error.user.password.undefined": "O usuário não possui uma senha", @@ -195,8 +251,10 @@ "error.validation.accepted": "Por favor, confirme", "error.validation.alpha": "Por favor, use apenas caracteres entre a-z", "error.validation.alphanum": "Por favor, use apenas caracteres entre a-z ou 0-9", + "error.validation.anchor": "Por favor insira uma âncora de link correta", "error.validation.between": "Digite um valor entre \"{min}\" e \"{max}\"", "error.validation.boolean": "Por favor, confirme ou rejeite", + "error.validation.color": "Por favor, insira uma cor válida no formato {format}", "error.validation.contains": "Digite um valor que contenha \"{needle}\"", "error.validation.date": "Escolha uma data válida", "error.validation.date.after": "Por favor entre uma data depois de {date}", @@ -211,6 +269,7 @@ "error.validation.integer": "Digite um número inteiro válido", "error.validation.ip": "Digite um endereço de IP válido", "error.validation.less": "Digite um valor menor que {max}", + "error.validation.linkType": "O tipo de link não é permitido", "error.validation.match": "O valor não combina com o padrão esperado", "error.validation.max": "Digite um valor igual ou menor que {max}", "error.validation.maxlength": "Digite um valor curto. (no máximo {max} caracteres)", @@ -227,15 +286,18 @@ "error.validation.same": "Por favor, digite \"{other}\"", "error.validation.size": "O tamanho do valor deve ser \"{size}\"", "error.validation.startswith": "O valor deve começar com \"{start}\"", + "error.validation.tel": "Por favor, insira um número de telefone não formatado", "error.validation.time": "Digite um horário válido", "error.validation.time.after": "Por favor entre um horário depois de {time}", "error.validation.time.before": "Por favor entre um horário antes de {time}", "error.validation.time.between": "Por favor entre um horário entre {min} e {max}", + "error.validation.uuid": "Por favor, insira um UUID válido", "error.validation.url": "Digite uma URL válida", "expand": "Expandir", "expand.all": "Expandir todos", + "field.invalid": "O campo é inválido", "field.required": "Este campo é obrigatório ", "field.blocks.changeType": "Mudar tipo", "field.blocks.code.name": "Código", @@ -245,8 +307,9 @@ "field.blocks.delete.confirm.all": "Deseja realmente deletar todos os blocos?", "field.blocks.delete.confirm.selected": "Deseja realmente deletar os blocos selecionados?", "field.blocks.empty": "Nenhum bloco", + "field.blocks.fieldsets.empty": "Ainda não há tipos de blocos", "field.blocks.fieldsets.label": "Por favor selecione um tipo de bloco …", - "field.blocks.fieldsets.paste": "Digite {{ shortcut }} para colar/importar blocos da sua área de transferência ", + "field.blocks.fieldsets.paste": "Pressione {{ shortcut }} para importar layouts/blocks da sua área de transferência Só serão inseridos aqueles permitidos no campo atual.", "field.blocks.gallery.name": "Galeria", "field.blocks.gallery.images.empty": "Nenhuma imagem", "field.blocks.gallery.images.label": "Imagens", @@ -254,11 +317,16 @@ "field.blocks.heading.name": "Título ", "field.blocks.heading.text": "Texto", "field.blocks.heading.placeholder": "Título …", + "field.blocks.figure.back.plain": "Simples", + "field.blocks.figure.back.pattern.light": "Padrão (claro)", + "field.blocks.figure.back.pattern.dark": "Padrão (escuro)", "field.blocks.image.alt": "Texto alternativo", "field.blocks.image.caption": "Legenda", "field.blocks.image.crop": "Cortar", "field.blocks.image.link": "Link", "field.blocks.image.location": "Localização ", + "field.blocks.image.location.internal": "Este website", + "field.blocks.image.location.external": "Fonte externa", "field.blocks.image.name": "Imagem", "field.blocks.image.placeholder": "Selecionar uma imagem", "field.blocks.image.ratio": "Proporção ", @@ -275,38 +343,72 @@ "field.blocks.quote.citation.placeholder": "de …", "field.blocks.text.name": "Texto", "field.blocks.text.placeholder": "Texto …", + "field.blocks.video.autoplay": "Reprodução automática", "field.blocks.video.caption": "Legenda", + "field.blocks.video.controls": "Controlos", + "field.blocks.video.location": "Localização ", + "field.blocks.video.loop": "Repetir", + "field.blocks.video.muted": "Sem som", "field.blocks.video.name": "Vídeo ", "field.blocks.video.placeholder": "Entre uma URL de vídeo ", + "field.blocks.video.poster": "Poster", + "field.blocks.video.preload": "Pré-carregamento", "field.blocks.video.url.label": "URL-Vídeo", "field.blocks.video.url.placeholder": "https://youtube.com/?v=", - "field.files.empty": "Nenhum arquivo selecionado", + "field.entries.delete.confirm.all": "Tem a certeza que pretende eliminar todos os registos?", + "field.entries.empty": "Nenhum registro", + "field.files.empty": "Nenhum arquivo selecionado", + "field.files.empty.single": "Nenhum ficheiro selecionado ainda", + + "field.layout.change": "Alterar layout", "field.layout.delete": "Deletar layout", "field.layout.delete.confirm": "Deseja realmente deletar este layout?", + "field.layout.delete.confirm.all": "Tem a certeza que pretende remover todos os layouts?", "field.layout.empty": "Nenhuma linha", "field.layout.select": "Selecionar um layout", - "field.object.empty": "No information yet", + "field.object.empty": "Nenhuma informação ainda", "field.pages.empty": "Nenhuma página selecionada", + "field.pages.empty.single": "Nenhuma página selecionada ainda", "field.structure.delete.confirm": "Deseja realmente deletar esta linha?", - "field.structure.delete.confirm.all": "Do you really want to delete all entries?", + "field.structure.delete.confirm.all": "Tem a certeza que pretende eliminar todos os registos?", "field.structure.empty": "Nenhum registro", "field.users.empty": "Nenhum usuário selecionado", + "field.users.empty.single": "Nenhum utilizador selecionado ainda", + "fields.empty": "Nenhum campo ainda", + + "file": "Ficheiro", "file.blueprint": "Este arquivo não tem planta. Você pode definir sua planta em /site/blueprints/files/{blueprint}.yml", + "file.changeTemplate": "Alterar tema", + "file.changeTemplate.notice": "Alterar o template do ficheiro irá remover o conteúdo dos campos que não correspondem ao mesmo tipo. Se o novo template definir certas regras, por exemplo dimensões de imagem, estas também serão aplicadas irreversivelmente. Use com cuidado.", "file.delete.confirm": "Deseja realmente deletar
{filename}?", + "file.focus.placeholder": "Definir ponto de foco", + "file.focus.reset": "Remover ponto de foco", + "file.focus.title": "Foco", "file.sort": "Mudar posição", "files": "Arquivos", + "files.delete.confirm.selected": "Tem a certeza que pretende eliminar os ficheiros selecionados? Esta ação não pode ser revertida.", "files.empty": "Nenhum arquivo", + "filter": "Filtro", + + "form.discard": "Reverter alterações", + "form.discard.confirm": "Tem a certeza que pretende reverter todas as suas alterações?", + "form.locked": "Este conteúdo está desativado para si porque encontra-se a ser editado por outro utilizador", + "form.unsaved": "As alterações atuais ainda não foram guardadas", + "form.preview": "Pré-visualizar alterações", + "form.preview.draft": "Pré-visualizar rascunho", + "hide": "Ocultar", "hour": "Hora", + "hue": "Tonalidade", "import": "Importar", "info": "Info", "insert": "Inserir", @@ -324,7 +426,6 @@ "installation.issues.mbstring": "A extensão MB String é necessária", "installation.issues.media": "A pasta /media não existe ou não possui permissão de escrita", "installation.issues.php": "Certifique-se que você está usando o PHP 8+", - "installation.issues.server": "Kirby necessita do Apache, Nginx ou Caddy", "installation.issues.sessions": "A pasta /site/sessions não existe ou não possui permissão de escrita", "language": "Idioma", @@ -332,6 +433,7 @@ "language.convert": "Tornar padrão", "language.convert.confirm": "

Deseja realmente converter {name} para o idioma padrão? Esta ação não poderá ser revertida.

Se {name} tiver conteúdo não traduzido, partes do seu site poderão ficar sem conteúdo.

", "language.create": "Adicionar novo idioma", + "language.default": "Idioma padrão", "language.delete.confirm": "Deseja realmente deletar o idioma {name} incluíndo todas as traduções. Esta ação não poderá ser revertida!", "language.deleted": "Idioma deletado", "language.direction": "Direção de leitura", @@ -340,7 +442,16 @@ "language.locale": "String de localização do PHP", "language.locale.warning": "Você está usando uma configuração de local customizada. Por favor modifique a configuração no arquivo do idioma em /site/languages", "language.name": "Nome", + "language.secondary": "Idioma secundário", + "language.settings": "Configurações de idioma", "language.updated": "Idioma atualizado", + "language.variables": "Variáveis de idioma", + "language.variables.empty": "Nenhuma tradução ainda", + + "language.variable.delete.confirm": "Tem a certeza que pretende eliminar a variável {key}?", + "language.variable.key": "Chave", + "language.variable.notFound": "A variável não foi encontrada", + "language.variable.value": "Valor", "languages": "Idiomas", "languages.default": "Idioma padrão", @@ -348,17 +459,34 @@ "languages.secondary": "Idiomas secundários", "languages.secondary.empty": "Nenhum idioma secundário", - "license": "Licença do Kirby ", + "license": "Licen\u00e7a do Kirby ", + "license.activate": "Ativar agora", + "license.activate.label": "Por favor, ative a sua licença", + "license.activate.domain": "A sua licença será irá ser ativada para {host}.", + "license.activate.local": "Está prestes a ativar a sua licença Kirby no domínio local {host}. Se este site vai ser alojado num domínio público, por favor ative-o lá. Se o domínio {host} é o o que deseja para usar a sua licença, por favor continue.", + "license.activated": "Ativado", "license.buy": "Comprar licença", - "license.register": "Registrar", - "license.manage": "Manage your licenses", - "license.register.help": "Você recebeu o código da sua licença por email ao efetuar sua compra. Por favor, copie e cole o código para completar seu registro.", - "license.register.label": "Por favor, digite o código da sua licença", - "license.register.domain": "Your license will be registered to {host}.", - "license.register.local": "You are about to register your license for your local domain {host}. If this site will be deployed to a public domain, please register it there instead. If {host} is the domain you want to license Kirby to, please continue.", - "license.register.success": "Obrigado por apoiar o Kirby", - "license.unregistered": "Esta é uma cópia de demonstração não registrada do Kirby", - "license.unregistered.label": "Unregistered", + "license.code": "Código", + "license.code.help": "Recebeu o seu código de licença por e-mail após a compra. Por favor, copie e cole aqui.", + "license.code.label": "Por favor, digite o código da sua licença", + "license.status.active.info": "Inclui novas versões principais até {date}", + "license.status.active.label": "Licença válida", + "license.status.demo.info": "Esta é uma instalação de demonstração", + "license.status.demo.label": "Demonstração", + "license.status.inactive.info": "Renove a licença para atualizar para novas versões principais", + "license.status.inactive.label": "Nenhuma versão principal nova", + "license.status.legacy.bubble": "Pronto para renovar a sua licença?", + "license.status.legacy.info": "A sua licença não abrange esta versão", + "license.status.legacy.label": "Por favor, renove a sua licença", + "license.status.missing.bubble": "Pronto para lançar o seu site?", + "license.status.missing.info": "Nenhuma licença válida", + "license.status.missing.label": "Por favor, ative a sua licença", + "license.status.unknown.info": "O estado da licença é desconhecido", + "license.status.unknown.label": "Desconhecido", + "license.manage": "Gerir as suas licenças", + "license.purchased": "Compradas", + "license.success": "Obrigado por apoiar o Kirby", + "license.unregistered.label": "Não registadas", "link": "Link", "link.text": "Texto do link", @@ -367,17 +495,21 @@ "lock.unsaved": "Mudanças não salvas", "lock.unsaved.empty": "Não há mais mudanças não salvas", - "lock.isLocked": "Mudanças não salvas por {email}", - "lock.file.isLocked": "Este arquivo está sendo editado no momento por {email}, e não pode ser mudado", - "lock.page.isLocked": "Esta página está sendo editada no momento por {email}, e não pode ser mudada", + "lock.unsaved.files": "Ficheiros não guardados", + "lock.unsaved.pages": "Páginas não guardadas", + "lock.unsaved.users": "Contas não guardadas", + "lock.isLocked": "Alterações não guardadas de {email}", "lock.unlock": "Destrancar", - "lock.isUnlocked": "Suas mudanças não salvas foram alteradas por outro usuário, e serão perdidas. Você pode baixar um arquivo com suas mudanças, para depois fundi-las manualmente. ", + "lock.unlock.submit": "Desbloqueie e substitua alterações não guardadas de {email}", + "lock.isUnlocked": "Foi desbloqueado por outro utilizador", - "login": "Log in", + "login": "Entrar", "login.code.label.login": "Código de acesso", "login.code.label.password-reset": "Código de redefinição de senha", "login.code.placeholder.email": "000 0000", + "login.code.placeholder.totp": "000000", "login.code.text.email": "Se seu endereço de email está registrado, o código requisitado será mandado por email.", + "login.code.text.totp": "Por favor, insira o código único da sua aplicação de autenticação.", "login.email.login.body": "Oi, {user.nameOrEmail},\n\nVocê recentemente pediu um código de acesso ao painel administrativo do site {site}.\nO seguinte código será válido por {timeout} minutos:\n\n{code}\n\nSe você não pediu este código de acesso, por favor ignore esta mensagem, ou contate seu Administrador de Sistemas se você tiver dúvidas.\nPor questões de segurança, por favor NÃO compartilhe esta mensagem.", "login.email.login.subject": "Seu código de acesso", "login.email.password-reset.body": "Oi, {user.nameOrEmail},\n\nVocê recentemente pediu um código de redefinição de senha, para o painel administrativo do site {site}.\nO seguinte código de redefinição de senha será válido por {timeout} minutos:\n\n{code}\n\nSe você não pediu este código, por favor ignore esta mensagem, ou contate seu Administrador de Sistemas se você tiver dúvidas.\nPor questões de segurança, por favor NÃO compartilhe esta mensagem.", @@ -388,58 +520,80 @@ "login.toggleText.code.email-password": "Entrar com senha", "login.toggleText.password-reset.email": "Esqueceu sua senha?", "login.toggleText.password-reset.email-password": "← Voltar à entrada", + "login.totp.enable.option": "Configurar códigos únicos", + "login.totp.enable.intro": "As aplicações de autenticação podem gerar códigos únicos que são utilizados como um segundo fator ao iniciar a sessão na sua conta.", + "login.totp.enable.qr.label": "1. Leia este código QR", + "login.totp.enable.qr.help": "Não consegue ler o código? Adicione a chave de configuração {secret} manualmente à sua aplicação de autenticação.", + "login.totp.enable.confirm.headline": "2. Confirme com o código gerado", + "login.totp.enable.confirm.text": "A sua aplicação gera um novo código único a cada 30 segundos. Insira o código atual para concluir a configuração:", + "login.totp.enable.confirm.label": "Código atual", + "login.totp.enable.confirm.help": "Após esta configuração, iremos solicitar um código único sempre que iniciar a sessão.", + "login.totp.enable.success": "Códigos únicos ativados", + "login.totp.disable.option": "Desativar códigos únicos", + "login.totp.disable.label": "Insira a sua palavra-passe para desativar códigos únicos", + "login.totp.disable.help": "No futuro, um segundo fator diferente, como um código de início de sessão enviado por e-mail, será solicitado quando iniciar a sessão. Poderá configurar códigos únicos novamente mais tarde.", + "login.totp.disable.admin": "Isto irá desactivar os códigos únicos para {user}. No futuro, um segundo fator diferente, como um código de início de sessão enviado por e-mail, será solicitado quando eles iniciarem a sessão. {user} poderá configurar códigos únicos novamente após o próximo início de sessão.", + "login.totp.disable.success": "Códigos únicos desativados", - "logout": "Log out", + "logout": "Sair", + "merge": "Unir", "menu": "Menu", "meridiem": "AM/PM", "mime": "Tipo de mídia", "minutes": "Minutos", "month": "Mês", - "months.april": "April", + "months.april": "Abril", "months.august": "Agosto", - "months.december": "December", + "months.december": "Dezembro", "months.february": "Fevereiro", "months.january": "Janeiro", "months.july": "Julho", - "months.june": "June", - "months.march": "Março", + "months.june": "Junho", + "months.march": "Mar\u00e7o", "months.may": "Maio", "months.november": "Novembro", "months.october": "Outubro", "months.september": "Setembro", "more": "Mais", + "move": "Mover", "name": "Nome", "next": "Próximo", + "night": "Noite", "no": "não", "off": "não", "on": "sim", "open": "Abrir", "open.newWindow": "Abrir em nova janela", + "option": "Opção", "options": "Opções", "options.none": "Nenhuma opção", + "options.all": "Mostrar todas as {count} opções", "orientation": "Orientação", "orientation.landscape": "Paisagem", "orientation.portrait": "Retrato", "orientation.square": "Quadrado", + "page": "Página", "page.blueprint": "Esta página não tem planta. Você pode definir sua planta em /site/blueprints/pages/{blueprint}.yml", "page.changeSlug": "Alterar URL", - "page.changeSlug.fromTitle": "Criar a partir do título", + "page.changeSlug.fromTitle": "Criar a partir do t\u00edtulo", "page.changeStatus": "Alterar estado", "page.changeStatus.position": "Selecione uma posição", "page.changeStatus.select": "Selecione um novo estado", "page.changeTemplate": "Alterar tema", + "page.changeTemplate.notice": "Alterar o template da página irá remover o conteúdo dos campos que não correspondem ao mesmo tipo. Use com cuidado.", + "page.create": "Criar como {status}", "page.delete.confirm": "Deseja realmente deletar {title}?", "page.delete.confirm.subpages": "Esta página possui subpáginas.
Todas as subpáginas serão excluídas também.", "page.delete.confirm.title": "Digite o título da página para confirmar", - "page.draft.create": "Criar rascunho", "page.duplicate.appendix": "Copiar", "page.duplicate.files": "Copiar arquivos", "page.duplicate.pages": "Copiar páginas", + "page.move": "Mover página", "page.sort": "Mudar posição", "page.status": "Estado", "page.status.draft": "Rascunho", @@ -449,7 +603,8 @@ "page.status.unlisted": "Não listadas", "page.status.unlisted.description": "Esta página é acessível somente através da URL", - "pages": "Pages", + "pages": "Páginas", + "pages.delete.confirm.selected": "Tem a certeza que pretende eliminar as páginas selecionadas? Esta ação não pode ser revertida.", "pages.empty": "Nenhuma página", "pages.status.draft": "Rascunhos", "pages.status.listed": "Publicadas", @@ -460,19 +615,26 @@ "password": "Senha", "paste": "Colar", "paste.after": "Colar após", + "paste.success": "{count} colados!", "pixel": "Pixel", "plugin": "Plugin", "plugins": "Plugins", "prev": "Anterior", "preview": "Visualizar", + + "publish": "Publicar", + "published": "Publicadas", + "remove": "Remover", "rename": "Renomear", - "replace": "Replace", + "renew": "Renovar", + "replace": "Substituir", + "replace.with": "Substituir por", "retry": "Tentar novamente", "revert": "Descartar", "revert.confirm": "Deseja realmente deletar todas as mudanças não salvas?", - "role": "Função", + "role": "Papel", "role.admin.description": "O administrador tem todos os direitos", "role.admin.title": "Administrador", "role.all": "Todos", @@ -482,14 +644,17 @@ "role.nobody.title": "Ninguém", "save": "Salvar", + "saved": "Guardado", "search": "Buscar", + "searching": "À procura", "search.min": "Digite {min} caracteres para fazer uma busca", - "search.all": "Mostrar todos", + "search.all": "Mostrar todos os {count} resultados", "search.results.none": "Nenhum resultado", + "section.invalid": "A secção é inválida", "section.required": "Esta seção é obrigatória", - "security": "Security", + "security": "Segurança", "select": "Selecionar", "server": "Servidor", "settings": "Configurações", @@ -498,32 +663,50 @@ "size": "Tamanho", "slug": "Anexo de URL", "sort": "Ordenar", + "sort.drag": "Arraste para ordenar ...", + "split": "Dividir", - "stats.empty": "No reports", - "system.issues.content": "The content folder seems to be exposed", - "system.issues.eol.kirby": "Your installed Kirby version has reached end-of-life and will not receive further security updates", - "system.issues.eol.plugin": "Your installed version of the { plugin } plugin is has reached end-of-life and will not receive further security updates", - "system.issues.debug": "Debugging must be turned off in production", - "system.issues.git": "The .git folder seems to be exposed", - "system.issues.https": "We recommend HTTPS for all your sites", - "system.issues.kirby": "The kirby folder seems to be exposed", - "system.issues.site": "The site folder seems to be exposed", - "system.issues.vulnerability.kirby": "Your installation might be affected by the following vulnerability ({ severity } severity): { description }", - "system.issues.vulnerability.plugin": "Your installation might be affected by the following vulnerability in the { plugin } plugin ({ severity } severity): { description }", - "system.updateStatus": "Update status", - "system.updateStatus.error": "Could not check for updates", - "system.updateStatus.not-vulnerable": "No known vulnerabilities", - "system.updateStatus.security-update": "Free security update { version } available", - "system.updateStatus.security-upgrade": "Upgrade { version } with security fixes available", - "system.updateStatus.unreleased": "Unreleased version", - "system.updateStatus.up-to-date": "Up to date", - "system.updateStatus.update": "Free update { version } available", - "system.updateStatus.upgrade": "Upgrade { version } available", + "stats.empty": "Nenhum relatório", + "status": "Estado", + + "system.info.copy": "Copiar informação", + "system.info.copied": "Informação de sistema copiada", + "system.issues.content": "A pasta \"content\" parece não estar protegida", + "system.issues.eol.kirby": "A versão instalada do Kirby chegou ao fim da sua vida útil e não irá receber mais atualizações de segurança", + "system.issues.eol.plugin": "A versão instalada do plugin {plugin} chegou ao fim da sua vida útil e não irá receber mais atualizações de segurança", + "system.issues.eol.php": "A versão instalada {release} de PHP chegou ao fim da sua vida útil e não irá receber mais atualizações de segurança", + "system.issues.debug": "O modo debug deve ser desativado em produção", + "system.issues.git": "A pasta \".git\" parece não estar protegida", + "system.issues.https": "Nós recomendamos HTTPS para todos os seus sites", + "system.issues.kirby": "A pasta \"kirby\" parece não estar protegida", + "system.issues.local": "O site está a correr localmente com verificações de segurança relaxadas", + "system.issues.site": "A pasta \"site\" parece não estar protegida", + "system.issues.vue.compiler": "O compilador de templates Vue está ativado", + "system.issues.vulnerability.kirby": "A sua instalação poderá ser afetada pela seguinte vulnerabilidade ({ severity } gravidade): { description }", + "system.issues.vulnerability.plugin": "A sua instalação poderá ser afetada pela seguinte vulnerabilidade no plugin { plugin } ({ severity } gravidade): { description }", + "system.updateStatus": "Atualizar estado", + "system.updateStatus.error": "Não foi possível verificar se havia atualizações", + "system.updateStatus.not-vulnerable": "Nenhuma vulnerabilidade conhecida", + "system.updateStatus.security-update": "Atualização de segurança gratuita { version } disponível", + "system.updateStatus.security-upgrade": "Atualização { version } com correções de segurança disponível", + "system.updateStatus.unreleased": "Versão não lançada", + "system.updateStatus.up-to-date": "Atualizado", + "system.updateStatus.update": "Atualização gratuita { version } disponível", + "system.updateStatus.upgrade": "Atualização { version } disponível", + + "tel": "Telefone", + "tel.placeholder": "+351 123456789", + "template": "Tema", + + "theme": "Tema", + "theme.light": "Luzes ligadas", + "theme.dark": "Luzes desligadas", + "theme.automatic": "Ajustar ao tema do sistema", "title": "Título", - "template": "Tema", "today": "Hoje", + "toolbar.button.clear": "Limpar formatação", "toolbar.button.code": "Código", "toolbar.button.bold": "Negrito", "toolbar.button.email": "Email", @@ -535,12 +718,14 @@ "toolbar.button.heading.5": "Título 5", "toolbar.button.heading.6": "Título 6", "toolbar.button.italic": "Itálico", - "toolbar.button.file": "File", + "toolbar.button.file": "Arquivo", "toolbar.button.file.select": "Selecionar arquivo", "toolbar.button.file.upload": "Carregar arquivo", "toolbar.button.link": "Link", "toolbar.button.paragraph": "Parágrafo", "toolbar.button.strike": "Riscado", + "toolbar.button.sub": "Subscrito", + "toolbar.button.sup": "Sobrescrito", "toolbar.button.ol": "Lista ordenada", "toolbar.button.underline": "Sublinhado", "toolbar.button.ul": "Lista não-ordenada", @@ -550,6 +735,8 @@ "translation.name": "Português do Brasil", "translation.locale": "pt_BR", + "type": "Tipo", + "upload": "Enviar", "upload.error.cantMove": "O arquivo carregado não pôde ser movido", "upload.error.cantWrite": "Falha ao escrever o arquivo no disco", @@ -562,7 +749,7 @@ "upload.error.noFiles": "Nenhum arquivo foi carregado", "upload.error.partial": "O arquivo foi só parcialmente carregado", "upload.error.tmpDir": "Falta uma pasta temporária", - "upload.errors": "Error", + "upload.errors": "Erro", "upload.progress": "Enviando…", "url": "Url", @@ -574,28 +761,32 @@ "user.changeLanguage": "Alterar idioma", "user.changeName": "Renomear usuário", "user.changePassword": "Alterar senha", + "user.changePassword.current": "A sua palavra-passe atual", "user.changePassword.new": "Nova senha", "user.changePassword.new.confirm": "Confirme a nova senha…", "user.changeRole": "Alterar papel", "user.changeRole.select": "Selecione um novo papel", - "user.create": "Add a new user", + "user.create": "Adicionar novo usuário", "user.delete": "Deletar este usuário", "user.delete.confirm": "Deseja realmente deletar
{email}?", "users": "Usuários", - "version": "Versão do Kirby", - "version.current": "Current version", - "version.latest": "Latest version", - "versionInformation": "Version information", + "version": "Vers\u00e3o do Kirby", + "version.changes": "Versão alterada", + "version.compare": "Comparar versões", + "version.current": "Versão atual", + "version.latest": "Versão mais recente", + "versionInformation": "Informação da versão", + "view": "Visualizar", "view.account": "Sua conta", - "view.installation": "Instalação", + "view.installation": "Instala\u00e7\u00e3o", "view.languages": "Idiomas", "view.resetPassword": "Redefinir senha", "view.site": "Site", "view.system": "Sistema", - "view.users": "Usuários", + "view.users": "Usu\u00e1rios", "welcome": "Bem-vindo", "year": "Ano", diff --git a/kirby/i18n/translations/pt_PT.json b/kirby/i18n/translations/pt_PT.json index aafd148..a9fc33b 100644 --- a/kirby/i18n/translations/pt_PT.json +++ b/kirby/i18n/translations/pt_PT.json @@ -1,264 +1,332 @@ { - "account.changeName": "Mudar seu nome", - "account.delete": "Deletar sua conta", - "account.delete.confirm": "Deseja realmente deletar sua conta? Você sairá do site imediatamente. Sua conta não poderá ser recuperada. ", + "account.changeName": "Altere o seu nome", + "account.delete": "Elimine a sua conta", + "account.delete.confirm": "Tem a certeza que pretende eliminar a sua conta? A sessão será terminada imediatamente. A sua conta não poderá ser recuperada. ", - "add": "Add", + "activate": "Ativar", + "add": "Adicionar", + "alpha": "Alpha", "author": "Autor", - "avatar": "Foto do perfil", - "back": "Back", - "cancel": "Cancel", + "avatar": "Foto de perfil", + "back": "Voltar", + "cancel": "Cancelar", "change": "Alterar", - "close": "Close", - "confirm": "Salvar", + "close": "Fechar", + "changes": "Alterações", + "confirm": "Ok", "collapse": "Colapsar", "collapse.all": "Colapsar todos", + "color": "Cor", + "coordinates": "Coordenadas", "copy": "Copiar", "copy.all": "Copiar todos", + "copy.success": "{count} copiados!", + "copy.success.multiple": "{count} copiados!", + "copy.url": "Copiar URL", "create": "Criar", + "custom": "Personalizado", "date": "Data", "date.select": "Selecione uma data", "day": "Dia", "days.fri": "Sex", - "days.mon": "Mon", - "days.sat": "Sáb", + "days.mon": "Seg", + "days.sat": "S\u00e1b", "days.sun": "Dom", "days.thu": "Qui", - "days.tue": "Tue", + "days.tue": "Ter", "days.wed": "Qua", "debugging": "Depuração ", - "delete": "Excluir", - "delete.all": "Deletar todos", + "delete": "Eliminar", + "delete.all": "Eliminar todos", - "dialog.files.empty": "Sem arquivos para selecionar", + "dialog.fields.empty": "Esta caixa de diálogo não tem campos", + "dialog.files.empty": "Sem ficheiros para selecionar", "dialog.pages.empty": "Sem páginas para selecionar", + "dialog.text.empty": "Esta caixa de diálogo não define nenhum texto", "dialog.users.empty": "Sem utilizadores para selecionar", "dimensions": "Dimensões", - "disabled": "Inativo", - "discard": "Discard", + "disable": "Desativar", + "disabled": "Desativado", + "discard": "Descartar", + + "drawer.fields.empty": "Esta janela não tem campos", + + "domain": "Domínio", "download": "Descarregar", "duplicate": "Duplicar", - "edit": "Edit", + "edit": "Editar", "email": "Email", "email.placeholder": "mail@exemplo.pt", - "entries": "Entries", - "entry": "Entry", + "enter": "Insira", + "entries": "Entradas", + "entry": "Entrada", "environment": "Ambiente", + "error": "Erro", "error.access.code": "Código inválido", - "error.access.login": "Login inválido", + "error.access.login": "Dados de acesso inválidos", "error.access.panel": "Não tem permissões para aceder ao painel", - "error.access.view": "Não tem permissões para aceder a esta área do Painel", + "error.access.view": "Não tem permissões para aceder a esta área do painel", - "error.avatar.create.fail": "A foto de perfil não foi enviada", - "error.avatar.delete.fail": "A foto do perfil não foi excluída", + "error.avatar.create.fail": "Não foi possível enviar a foto de perfil", + "error.avatar.delete.fail": "Não foi possível eliminar a foto de perfil", "error.avatar.dimensions.invalid": "Por favor, use uma foto de perfil com largura e altura menores que 3000 pixels", - "error.avatar.mime.forbidden": "A foto de perfil deve ser um arquivo JPEG ou PNG", + "error.avatar.mime.forbidden": "A foto de perfil deve ser um ficheiro JPEG ou PNG", - "error.blueprint.notFound": "O blueprint \"{name}\" não pode ser carregado", + "error.blueprint.notFound": "Não foi possível carregar o blueprint \"{name}\"", - "error.blocks.max.plural": "Você não deve adicionar mais do que {max} blocos", - "error.blocks.max.singular": "Você não deve adicionar mais do que um bloco", - "error.blocks.min.plural": "Você deve adicionar pelo menos {min} blocos", - "error.blocks.min.singular": "Você deve adicionar pelo menos um bloco", - "error.blocks.validation": "There's an error on the \"{field}\" field in block {index} using the \"{fieldset}\" block type", + "error.blocks.max.plural": "Não pode adicionar mais do que {max} blocos", + "error.blocks.max.singular": "Não pode adicionar mais do que um bloco", + "error.blocks.min.plural": "Tem de adicionar pelo menos {min} blocos", + "error.blocks.min.singular": "Tem de adicionar pelo menos um bloco", + "error.blocks.validation": "Há um erro no campo \"{field}\" no bloco {index} a usar o tipo de bloco \"{fieldset}\"", - "error.cache.type.invalid": "Invalid cache type \"{type}\"", + "error.cache.type.invalid": "Tipo de cache \"{type}\" inválido", - "error.email.preset.notFound": "Preset de email \"{name}\" não encontrado", + "error.content.lock.delete": "A versão está bloqueada e não pode ser eliminada", + "error.content.lock.move": "A versão está bloqueada e não pode ser movida", + "error.content.lock.publish": "Esta versão já se encontra publicada", + "error.content.lock.replace": "A versão está bloqueada e não pode ser substituída", + "error.content.lock.update": "A versão está bloqueada e não pode ser atualizada", + + "error.entries.max.plural": "Não deve adicionar mais do que {max} entradas", + "error.entries.max.singular": "Não deve adicionar mais do que uma entrada", + "error.entries.min.plural": "Deve adicionar pelo menos {min} entradas", + "error.entries.min.singular": "Deve adicionar pelo menos uma entrada", + "error.entries.supports": "O tipo de campo \"{type}\" não é compatível com o campo entries", + "error.entries.validation": "Existe um erro no campo \"{field}\" na linha {index}", + + "error.email.preset.notFound": "A predefinição de email \"{name}\" não foi encontrada", "error.field.converter.invalid": "Conversor \"{converter}\" inválido", - "error.field.type.missing": "Field \"{ name }\": The field type \"{ type }\" does not exist", + "error.field.link.options": "Opções inválidas: {options}", + "error.field.type.missing": "Campo \"{name}\": O tipo de campo \"{type}\" não existe", "error.file.changeName.empty": "O nome não pode ficar em branco", "error.file.changeName.permission": "Não tem permissões para alterar o nome de \"{filename}\"", - "error.file.duplicate": "Um arquivo com o nome \"{filename}\" já existe", - "error.file.extension.forbidden": "Extensão \"{extension}\" não permitida", + "error.file.changeTemplate.invalid": "O template para o ficheiro \"{id}\" não pode ser alterado para \"{template}\" (válido: \"{blueprints}\")", + "error.file.changeTemplate.permission": "Não tem permissão para alterar o template do ficheiro \"{id}\"", + + "error.file.delete.multiple": "Nem todos os ficheiros puderam ser eliminados. Experimente cada ficheiro restante individualmente para ver o erro específico que impede a sua eliminação.", + "error.file.duplicate": "Um ficheiro com o nome \"{filename}\" já existe", + "error.file.extension.forbidden": "A extensão \"{extension}\" não é permitida", "error.file.extension.invalid": "Extensão inválida: {extension}", - "error.file.extension.missing": "Extensão de \"{filename}\" em falta", - "error.file.maxheight": "A altura da imagem não deve exceder {height} pixels", - "error.file.maxsize": "O arquivo é muito grande", - "error.file.maxwidth": "A largura da imagem não deve exceder {width} pixels", - "error.file.mime.differs": "O arquivo enviado precisa ser do tipo \"{mime}\"", - "error.file.mime.forbidden": "Tipo de mídia \"{mime}\" não permitido", + "error.file.extension.missing": "As extensões de \"{filename}\" estão em falta", + "error.file.maxheight": "A altura da imagem não deve exceder {height} píxeis", + "error.file.maxsize": "O ficheiro é demasiado grande", + "error.file.maxwidth": "A largura da imagem não deve exceder {width} píxeis", + "error.file.mime.differs": "O ficheiro enviado precisa de ser do tipo \"{mime}\"", + "error.file.mime.forbidden": "O tipo de mídia \"{mime}\" não é permitido", "error.file.mime.invalid": "Tipo de mídia inválido: {mime}", - "error.file.mime.missing": "Tipo de mídia de \"{filename}\" não detectado", - "error.file.minheight": "A altura da imagem deve ser pelo menos {height} pixels", - "error.file.minsize": "O ficheiro é muito pequeno", - "error.file.minwidth": "A largura da imagem deve ser pelo menos {width} pixels", - "error.file.name.missing": "O nome do arquivo não pode ficar em branco", - "error.file.notFound": "Arquivo \"{filename}\" não encontrado", + "error.file.mime.missing": "Não foi possível detectar o tipo de mídia de \"{filename}\"", + "error.file.minheight": "A altura da imagem deve ter pelo menos {height} píxeis", + "error.file.minsize": "O ficheiro é demasiado pequeno", + "error.file.minwidth": "A largura da imagem deve ter pelo menos {width} píxeis", + "error.file.name.unique": "O nome do ficheiro deve ser único", + "error.file.name.missing": "O nome do ficheiro não pode ficar em branco", + "error.file.notFound": "Não foi possível encontrar o ficheiro \"{filename}\"", "error.file.orientation": "A orientação da imagem deve ser \"{orientation}\"", - "error.file.type.forbidden": "Não tem permissões para enviar arquivos {type}", - "error.file.type.invalid": "Tipo inválido de arquivo: {type}", - "error.file.undefined": "Arquivo não encontrado", + "error.file.sort.permission": "Não tem permissão para alterar a ordem de \"{filename}\"", + "error.file.type.forbidden": "Não tem permissões para enviar ficheiros {type}", + "error.file.type.invalid": "Tipo de ficheiro inválido: {type}", + "error.file.undefined": "Não foi possível encontrar o ficheiro", - "error.form.incomplete": "Por favor, corrija os erros do formulário…", - "error.form.notSaved": "O formulário não foi guardado", + "error.form.incomplete": "Por favor, corrija todos os erros do formulário…", + "error.form.notSaved": "Não foi possível guardar o formulário", - "error.language.code": "Insira um código de idioma válido", + "error.language.code": "Por favor, insira um código válido para o idioma", + "error.language.create.permission": "Não tem permissões para criar um idioma", + "error.language.delete.permission": "Não tem permissões para eliminar o idioma", "error.language.duplicate": "O idioma já existe", - "error.language.name": "Insira um nome válido para o idioma", - "error.language.notFound": "O idioma não foi encontrado", + "error.language.name": "Por favor, insira um nome válido para o idioma", + "error.language.notFound": "Não foi possível encontrar o idioma", + "error.language.update.permission": "Não tem permissões para atualizar o idioma", - "error.layout.validation.block": "There's an error on the \"{field}\" field in block {blockIndex} using the \"{fieldset}\" block type in layout {layoutIndex}", + "error.layout.validation.block": "Há um erro no campo \"{field}\" no bloco {blockIndex} a usar o tipo de bloco \"{fieldset}\" no layout {layoutIndex}", "error.layout.validation.settings": "Há um erro na configuração do layout {index}", - "error.license.format": "Insira uma chave de licença válida", - "error.license.email": "Digite um endereço de email válido", + "error.license.domain": "O domínio da licença está em falta", + "error.license.email": "Por favor, insira um endereço de email válido", + "error.license.format": "Por favor, insira um código de licença válido", "error.license.verification": "Não foi possível verificar a licença", - "error.object.validation": "There’s an error in the \"{label}\" field:\n{message}", + "error.login.totp.confirm.invalid": "Código inválido", + "error.login.totp.confirm.missing": "Por favor, insira o código atual", - "error.offline": "O painel está offline no momento", + "error.object.validation": "Há um erro no campo \"{label}\":\n{message}", - "error.page.changeSlug.permission": "Não tem permissões para alterar a URL de \"{slug}\"", - "error.page.changeStatus.incomplete": "A página possui erros e não pode ser guardada", + "error.offline": "O painel encontra-se offline de momento", + + "error.page.changeSlug.permission": "Não tem permissões para alterar o URL de \"{slug}\"", + "error.page.changeSlug.reserved": "O caminho das páginas de nível superior não deve começar com \"{path}\"", + "error.page.changeStatus.incomplete": "A página tem erros e não pode ser publicada", "error.page.changeStatus.permission": "O estado desta página não pode ser alterado", "error.page.changeStatus.toDraft.invalid": "A página \"{slug}\" não pode ser convertida para rascunho", - "error.page.changeTemplate.invalid": "O tema da página \"{slug}\" não pode ser alterado", - "error.page.changeTemplate.permission": "Não tem permissões para alterar o tema de \"{slug}\"", + "error.page.changeTemplate.invalid": "O template da página \"{slug}\" não pode ser alterado", + "error.page.changeTemplate.permission": "Não tem permissões para alterar o template de \"{slug}\"", "error.page.changeTitle.empty": "O título não pode ficar em branco", "error.page.changeTitle.permission": "Não tem permissões para alterar o título de \"{slug}\"", "error.page.create.permission": "Não tem permissões para criar \"{slug}\"", - "error.page.delete": "A página \"{slug}\" não pode ser excluída", - "error.page.delete.confirm": "Por favor, digite o título da página para confirmar", - "error.page.delete.hasChildren": "A página possui subpáginas e não pode ser excluída", - "error.page.delete.permission": "Não tem permissões para excluir \"{slug}\"", - "error.page.draft.duplicate": "Um rascunho de página com a URL \"{slug}\" já existe", - "error.page.duplicate": "Uma página com a URL \"{slug}\" já existe", - "error.page.duplicate.permission": "Não tem permissão para duplicar \"{slug}\"", - "error.page.notFound": "Página\"{slug}\" não encontrada", - "error.page.num.invalid": "Digite um número de ordenação válido. Este número não pode ser negativo.", - "error.page.slug.invalid": "Por favor entre um anexo de URL válido ", - "error.page.slug.maxlength": "O slug não pode conter mais do que \"{length}\" caracteres", - "error.page.sort.permission": "A página \"{slug}\" não pode ser ordenada", + "error.page.delete": "A página \"{slug}\" não pode ser eliminada", + "error.page.delete.confirm": "Por favor, insira o título da página para confirmar", + "error.page.delete.hasChildren": "A página tem subpáginas e não pode ser eliminada", + "error.page.delete.multiple": "Nem todas as páginas puderam ser eliminadas. Experimente cada página restante individualmente para ver o erro específico que impede a sua eliminação.", + "error.page.delete.permission": "Não tem permissões para eliminar \"{slug}\"", + "error.page.draft.duplicate": "Uma página de rascunho com o URL \"{slug}\" já existe", + "error.page.duplicate": "Uma página com o URL \"{slug}\" já existe", + "error.page.duplicate.permission": "Não tem permissões para duplicar \"{slug}\"", + "error.page.move.ancestor": "A página não pode ser movida para dentro dela mesma", + "error.page.move.directory": "A pasta da página não pode ser movida", + "error.page.move.duplicate": "Já existe uma subpágina com o URL \"{slug}\"", + "error.page.move.noSections": "A página \"{parent}\" não pode ser pai de nenhuma página porque não tem secções de páginas na sua blueprint", + "error.page.move.notFound": "A página movida não foi encontrada", + "error.page.move.permission": "Não tem permissões para mover \"{slug}\"", + "error.page.move.template": "O template \"{template}\" não é aceite como subpágina de \"{parent}\"", + "error.page.notFound": "Não foi possível encontrar a página \"{slug}\"", + "error.page.num.invalid": "Por favor, insira um número de ordenação válido. Este número não pode ser negativo.", + "error.page.slug.invalid": "Por favor, insira um caminho de URL válido ", + "error.page.slug.maxlength": "O URL não pode conter mais do que \"{length}\" caracteres", + "error.page.sort.permission": "Não é possível ordenar a página \"{slug}\"", "error.page.status.invalid": "Por favor, defina um estado de página válido", - "error.page.undefined": "Página não encontrada", + "error.page.undefined": "Não foi possível encontrar a página", "error.page.update.permission": "Não tem permissões para atualizar \"{slug}\"", - "error.section.files.max.plural": "Não pode adicionar mais do que {max} arquivos à seção \"{section}\"", - "error.section.files.max.singular": "Não pode adicionar mais do que um arquivo à seção \"{section}\"", - "error.section.files.min.plural": "A secção \"{section}\" requer no mínimo {min} arquivos", - "error.section.files.min.singular": "A secção \"{section}\" requer no mínimo um arquivo", + "error.section.files.max.plural": "Não pode adicionar mais do que {max} ficheiros à secção \"{section}\"", + "error.section.files.max.singular": "Não pode adicionar mais do que um ficheiro à secção \"{section}\"", + "error.section.files.min.plural": "A secção \"{section}\" requer no mínimo {min} ficheiros", + "error.section.files.min.singular": "A secção \"{section}\" requer no mínimo um ficheiro", - "error.section.pages.max.plural": "Não pode adicionar mais do que {max} página à seção \"{section}\"", - "error.section.pages.max.singular": "Não pode adicionar mais do que uma página à seção \"{section}\"", + "error.section.pages.max.plural": "Não pode adicionar mais do que {max} páginas à secção \"{section}\"", + "error.section.pages.max.singular": "Não pode adicionar mais do que uma página à secção \"{section}\"", "error.section.pages.min.plural": "A secção \"{section}\" requer no mínimo {min} páginas", "error.section.pages.min.singular": "A secção \"{section}\" requer no mínimo uma página", - "error.section.notLoaded": "A seção \"{name}\" não pôde ser carregada", - "error.section.type.invalid": "O tipo da seção \"{type}\" não é válido", + "error.section.notLoaded": "Não foi possível carregar a secção \"{name}\"", + "error.section.type.invalid": "O tipo de secção \"{type}\" não é válido", "error.site.changeTitle.empty": "O título não pode ficar em branco", "error.site.changeTitle.permission": "Não tem permissões para alterar o título do site", "error.site.update.permission": "Não tem permissões para atualizar o site", - "error.template.default.notFound": "O tema padrão não existe", + "error.structure.validation": "Existe um erro no campo \"{field}\" na linha {index}", - "error.unexpected": "An unexpected error occurred! Enable debug mode for more info: https://getkirby.com/docs/reference/system/options/debug", + "error.template.default.notFound": "O template \"default\" não existe", + + "error.unexpected": "Ocorreu um erro inesperado! Ative o modo de debug para obter mais informações: https://getkirby.com/docs/reference/system/options/debug", "error.user.changeEmail.permission": "Não tem permissões para alterar o email do utilizador \"{name}\"", "error.user.changeLanguage.permission": "Não tem permissões para alterar o idioma do utilizador \"{name}\"", "error.user.changeName.permission": "Não tem permissões para alterar o nome do utilizador \"{name}\"", "error.user.changePassword.permission": "Não tem permissões para alterar a palavra-passe do utilizador \"{name}\"", - "error.user.changeRole.lastAdmin": "A função do último administrador não pode ser alterado", + "error.user.changeRole.lastAdmin": "A função do último administrador não pode ser alterada", "error.user.changeRole.permission": "Não tem permissões para alterar a função do utilizador \"{name}\"", "error.user.changeRole.toAdmin": "Não tem permissões para promover utilizadores à função de administrador", "error.user.create.permission": "Não tem permissões para criar este utilizador", - "error.user.delete": "O utilizador \"{name}\" não pode ser excluído", - "error.user.delete.lastAdmin": "O último administrador não pode ser excluído", - "error.user.delete.lastUser": "O último utilizador não pode ser excluído", - "error.user.delete.permission": "Não tem permissões para excluir o utilizador \"{name}\"", - "error.user.duplicate": "Um utilizador com o email \"{email}\" já existe", - "error.user.email.invalid": "Digite um endereço de email válido", - "error.user.language.invalid": "Digite um idioma válido", - "error.user.notFound": "Utilizador \"{name}\" não encontrado", - "error.user.password.excessive": "Please enter a valid password. Passwords must not be longer than 1000 characters.", - "error.user.password.invalid": "Digite uma palavra-passe válida. A sua palavra-passe deve ter pelo menos 8 caracteres.", - "error.user.password.notSame": "As palavras-passe não combinam", - "error.user.password.undefined": "O utilizador não possui uma palavra-passe", - "error.user.password.wrong": "Senha errada", - "error.user.role.invalid": "Digite uma função válida", - "error.user.undefined": "Usuário não encontrado", + "error.user.delete": "Não é possível eliminar o utilizador \"{name}\"", + "error.user.delete.lastAdmin": "Não é possível eliminar o último administrador", + "error.user.delete.lastUser": "Não é possível eliminar o último utilizador", + "error.user.delete.permission": "Não tem permissões para eliminar o utilizador \"{name}\"", + "error.user.duplicate": "Já existe um utilizador com o email \"{email}\"", + "error.user.email.invalid": "Por favor, insira um endereço de email válido", + "error.user.language.invalid": "Por favor, insira um idioma válido", + "error.user.notFound": "Não foi possível encontrar o utilizador \"{name}\"", + "error.user.password.excessive": "Por favor, insira uma palavra-passe válida. As palavras-passe não devem ter mais do que 1000 caracteres.", + "error.user.password.invalid": "Por favor, insira uma palavra-passe válida. As palavras-passe devem ter pelo menos 8 caracteres.", + "error.user.password.notSame": "As palavras-passe não coincidem", + "error.user.password.undefined": "O utilizador não tem uma palavra-passe", + "error.user.password.wrong": "Palavra-passe errada", + "error.user.role.invalid": "Por favor, insira uma função válida", + "error.user.undefined": "Não foi possível encontrar o utilizador", "error.user.update.permission": "Não tem permissões para atualizar o utilizador \"{name}\"", "error.validation.accepted": "Por favor, confirme", - "error.validation.alpha": "Por favor, use apenas caracteres entre a-z", - "error.validation.alphanum": "Por favor, use apenas caracteres entre a-z ou 0-9", - "error.validation.between": "Digite um valor entre \"{min}\" e \"{max}\"", + "error.validation.alpha": "Por favor, insira apenas caracteres entre a-z", + "error.validation.alphanum": "Por favor, insira apenas caracteres entre a-z ou 0-9", + "error.validation.anchor": "Por favor, insira uma âncora de link correta", + "error.validation.between": "Por favor, insira um valor entre \"{min}\" e \"{max}\"", "error.validation.boolean": "Por favor, confirme ou rejeite", - "error.validation.contains": "Digite um valor que contenha \"{needle}\"", - "error.validation.date": "Escolha uma data válida", - "error.validation.date.after": "Escolha uma data posterior a {date}", - "error.validation.date.before": "Escolha uma data anterior a {date}", - "error.validation.date.between": "Escolha uma data compreendida entre {min} e {max}", - "error.validation.denied": "Por favor, cancele", - "error.validation.different": "O valor deve ser diferente de \"{other}\"", - "error.validation.email": "Digite um endereço de email válido", - "error.validation.endswith": "O valor deve terminar com \"{end}\"", - "error.validation.filename": "Digite um nome de arquivo válido", - "error.validation.in": "Digite um destes valores: ({in})", - "error.validation.integer": "Digite um número inteiro válido", - "error.validation.ip": "Digite um endereço de IP válido", - "error.validation.less": "Digite um valor menor que {max}", - "error.validation.match": "O valor não combina com o padrão esperado", - "error.validation.max": "Digite um valor igual ou menor que {max}", - "error.validation.maxlength": "Digite um valor curto. (no máximo {max} caracteres)", - "error.validation.maxwords": "Digite menos que {max} palavra(s)", - "error.validation.min": "Digite um valor igual ou maior que {min}", - "error.validation.minlength": "Digite um valor maior. (no mínimo {min} caracteres)", - "error.validation.minwords": "Digite ao menos {min} palavra(s)", - "error.validation.more": "Digite um valor maior que {min}", - "error.validation.notcontains": "Digite um valor que não contenha \"{needle}\"", - "error.validation.notin": "Não digite nenhum destes valores: ({notIn})", - "error.validation.option": "Escolha uma opção válida", - "error.validation.num": "Digite um número válido", - "error.validation.required": "Digite algo", - "error.validation.same": "Por favor, digite \"{other}\"", - "error.validation.size": "O tamanho do valor deve ser \"{size}\"", - "error.validation.startswith": "O valor deve começar com \"{start}\"", - "error.validation.time": "Digite uma hora válida", - "error.validation.time.after": "Por favor entre um horário depois de {time}", - "error.validation.time.before": "Por favor entre um horário antes de {time}", - "error.validation.time.between": "Por favor entre um horário entre {min} e {max}", - "error.validation.url": "Digite uma URL válida", + "error.validation.color": "Por favor, insira uma cor válida no formato {format}", + "error.validation.contains": "Por favor, insira um valor que contenha \"{needle}\"", + "error.validation.date": "Por favor, insira uma data válida", + "error.validation.date.after": "Por favor, insira uma data posterior a {date}", + "error.validation.date.before": "Por favor, insira uma data anterior a {date}", + "error.validation.date.between": "Por favor, insira uma data entre {min} e {max}", + "error.validation.denied": "Por favor, rejeite", + "error.validation.different": "O valor tem de ser diferente de \"{other}\"", + "error.validation.email": "Por favor, insira um endereço de email válido", + "error.validation.endswith": "O valor tem de terminar com \"{end}\"", + "error.validation.filename": "Por favor, insira um nome de ficheiro válido", + "error.validation.in": "Por favor, insira um dos seguintes valores: ({in})", + "error.validation.integer": "Por favor, insira um número inteiro válido", + "error.validation.ip": "Por favor, insira um endereço de IP válido", + "error.validation.less": "Por favor, insira um valor menor que {max}", + "error.validation.linkType": "O tipo de link não é permitido", + "error.validation.match": "O valor não corresponde ao padrão esperado", + "error.validation.max": "Por favor, insira um valor igual ou menor que {max}", + "error.validation.maxlength": "Por favor, insira um valor mais curto. (máximo {max} caracteres)", + "error.validation.maxwords": "Por favor, não insira mais que {max} palavra(s)", + "error.validation.min": "Por favor, insira um valor igual ou maior que {min}", + "error.validation.minlength": "Por favor, insira um valor mais longo. (mínimo {min} caracteres)", + "error.validation.minwords": "Por favor, insira pelo menos {min} palavra(s)", + "error.validation.more": "Por favor, insira um valor maior que {min}", + "error.validation.notcontains": "Por favor, insira um valor que não contenha \"{needle}\"", + "error.validation.notin": "Por favor, não insira nenhum destes valores: ({notIn})", + "error.validation.option": "Por favor, selecione uma opção válida", + "error.validation.num": "Por favor, insira um número válido", + "error.validation.required": "Por favor, insira algo", + "error.validation.same": "Por favor, insira \"{other}\"", + "error.validation.size": "O tamanho do valor tem de ser \"{size}\"", + "error.validation.startswith": "O valor tem de começar com \"{start}\"", + "error.validation.tel": "Por favor, insira um número de telefone não formatado", + "error.validation.time": "Por favor, insira uma hora válida", + "error.validation.time.after": "Por favor, insira uma hora posterior a {time}", + "error.validation.time.before": "Por favor, insira uma hora anterior a {time}", + "error.validation.time.between": "Por favor, insira uma hora entre {min} e {max}", + "error.validation.uuid": "Por favor, insira um UUID válido", + "error.validation.url": "Por favor, insira um URL válido", "expand": "Expandir", "expand.all": "Expandir todos", - "field.required": "Este campo é necessário", - "field.blocks.changeType": "Mudar tipo", + "field.invalid": "O campo é inválido", + "field.required": "O campo é obrigatório", + "field.blocks.changeType": "Alterar tipo", "field.blocks.code.name": "Código", "field.blocks.code.language": "Idioma", - "field.blocks.code.placeholder": "Seu código …", - "field.blocks.delete.confirm": "Deseja realmente deletar este bloco?", - "field.blocks.delete.confirm.all": "Deseja realmente deletar todos os blocos?", - "field.blocks.delete.confirm.selected": "Deseja realmente deletar os blocos selecionados?", - "field.blocks.empty": "Nenhum bloco", - "field.blocks.fieldsets.label": "Por favor selecione um tipo de bloco …", - "field.blocks.fieldsets.paste": "Digite {{ shortcut }} para colar/importar blocos da sua área de transferência ", + "field.blocks.code.placeholder": "O seu código …", + "field.blocks.delete.confirm": "Tem a certeza que pretende eliminar este bloco?", + "field.blocks.delete.confirm.all": "Tem a certeza que pretende eliminar todos os blocos?", + "field.blocks.delete.confirm.selected": "Tem a certeza que pretende eliminar os blocos selecionados?", + "field.blocks.empty": "Nenhum bloco ainda", + "field.blocks.fieldsets.empty": "Nenhum tipo de bloco ainda", + "field.blocks.fieldsets.label": "Por favor, selecione um tipo de bloco …", + "field.blocks.fieldsets.paste": "Pressione {{ shortcut }} para importar layouts/blocks da sua área de transferência Só serão inseridos aqueles permitidos no campo atual.", "field.blocks.gallery.name": "Galeria", - "field.blocks.gallery.images.empty": "Nenhuma imagem", + "field.blocks.gallery.images.empty": "Nenhuma imagem ainda", "field.blocks.gallery.images.label": "Imagens", "field.blocks.heading.level": "Nível ", "field.blocks.heading.name": "Título ", "field.blocks.heading.text": "Texto", "field.blocks.heading.placeholder": "Título …", + "field.blocks.figure.back.plain": "Simples", + "field.blocks.figure.back.pattern.light": "Padrão (claro)", + "field.blocks.figure.back.pattern.dark": "Padrão (escuro)", "field.blocks.image.alt": "Texto alternativo", "field.blocks.image.caption": "Legenda", "field.blocks.image.crop": "Cortar", "field.blocks.image.link": "Link", "field.blocks.image.location": "Localização ", + "field.blocks.image.location.internal": "Este website", + "field.blocks.image.location.external": "Fonte externa", "field.blocks.image.name": "Imagem", "field.blocks.image.placeholder": "Selecionar uma imagem", "field.blocks.image.ratio": "Proporção ", @@ -275,38 +343,72 @@ "field.blocks.quote.citation.placeholder": "de …", "field.blocks.text.name": "Texto", "field.blocks.text.placeholder": "Texto …", + "field.blocks.video.autoplay": "Reprodução automática", "field.blocks.video.caption": "Legenda", + "field.blocks.video.controls": "Controlos", + "field.blocks.video.location": "Localização ", + "field.blocks.video.loop": "Repetir", + "field.blocks.video.muted": "Sem som", "field.blocks.video.name": "Vídeo ", - "field.blocks.video.placeholder": "Entre uma URL de vídeo ", + "field.blocks.video.placeholder": "Insira um URL de vídeo ", + "field.blocks.video.poster": "Poster", + "field.blocks.video.preload": "Pré-carregamento", "field.blocks.video.url.label": "URL-Vídeo", "field.blocks.video.url.placeholder": "https://youtube.com/?v=", - "field.files.empty": "Nenhum arquivo selecionado", + "field.entries.delete.confirm.all": "Tem a certeza que pretende eliminar todas as entradas?", + "field.entries.empty": "Nenhuma entrada ainda", - "field.layout.delete": "Deletar layout", - "field.layout.delete.confirm": "Deseja realmente deletar este layout?", - "field.layout.empty": "Nenhuma linha", + "field.files.empty": "Nenhum ficheiro selecionado ainda", + "field.files.empty.single": "Nenhum ficheiro selecionado ainda", + + "field.layout.change": "Alterar layout", + "field.layout.delete": "Eliminar layout", + "field.layout.delete.confirm": "Tem a certeza que pretende eliminar este layout?", + "field.layout.delete.confirm.all": "Tem a certeza que pretende eliminar todos os layouts?", + "field.layout.empty": "Nenhuma linha ainda", "field.layout.select": "Selecionar um layout", - "field.object.empty": "No information yet", + "field.object.empty": "Nenhuma informação ainda", - "field.pages.empty": "Nenhuma página selecionada", + "field.pages.empty": "Nenhuma página selecionada ainda", + "field.pages.empty.single": "Nenhuma página selecionada ainda", - "field.structure.delete.confirm": "Deseja realmente excluir este registro?", - "field.structure.delete.confirm.all": "Do you really want to delete all entries?", - "field.structure.empty": "Nenhum registro", + "field.structure.delete.confirm": "Tem a certeza que pretende eliminar esta linha?", + "field.structure.delete.confirm.all": "Tem a certeza que pretende eliminar todas as entradas?", + "field.structure.empty": "Nenhuma entrada ainda", - "field.users.empty": "Nenhum utilizador selecionado", + "field.users.empty": "Nenhum utilizador selecionado ainda", + "field.users.empty.single": "Nenhum utilizador selecionado ainda", - "file.blueprint": "Este arquivo não tem planta. Você pode definir sua planta em /site/blueprints/files/{blueprint}.yml", - "file.delete.confirm": "Deseja realmente excluir
{filename}?", - "file.sort": "Mudar posição", + "fields.empty": "Nenhum campo ainda", - "files": "Arquivos", - "files.empty": "Nenhum arquivo", + "file": "Ficheiro", + "file.blueprint": "Este ficheiro ainda não tem blueprint. Pode configurar o blueprint em /site/blueprints/files/{blueprint}.yml", + "file.changeTemplate": "Alterar template", + "file.changeTemplate.notice": "Alterar o template do ficheiro irá remover o conteúdo dos campos que não correspondem ao mesmo tipo. Se o novo template definir certas regras, por exemplo dimensões de imagem, estas também serão aplicadas irreversivelmente. Use com cuidado.", + "file.delete.confirm": "Tem a certeza que pretende eliminar
{filename}?", + "file.focus.placeholder": "Definir ponto de foco", + "file.focus.reset": "Remover ponto de foco", + "file.focus.title": "Foco", + "file.sort": "Alterar posição", + + "files": "Ficheiros", + "files.delete.confirm.selected": "Tem a certeza que pretende eliminar os ficheiros selecionados? Esta ação não pode ser revertida.", + "files.empty": "Nenhum ficheiro ainda", + + "filter": "Filtro", + + "form.discard": "Reverter alterações", + "form.discard.confirm": "Tem a certeza que pretende reverter todas as suas alterações?", + "form.locked": "Este conteúdo está desativado para si porque encontra-se a ser editado por outro utilizador", + "form.unsaved": "As alterações atuais ainda não foram guardadas", + "form.preview": "Pré-visualizar alterações", + "form.preview.draft": "Pré-visualizar rascunho", "hide": "Ocultar", "hour": "Hora", + "hue": "Tonalidade", "import": "Importar", "info": "Info", "insert": "Inserir", @@ -315,215 +417,296 @@ "install": "Instalar", "installation": "Instalação", - "installation.completed": "Painel instalado com sucesso", - "installation.disabled": "Por padrão, o instalador do painel está desabilitado em servidores públicos. Por favor, execute o instalador numa máquina local ou habilite a opção panel.install.", - "installation.issues.accounts": "A pasta /site/accounts não existe ou não possui permissão de escrita", - "installation.issues.content": "A pasta /content não existe ou não possui permissão de escrita", + "installation.completed": "O painel foi instalado com sucesso", + "installation.disabled": "A instalação do painel está desativada em servidores públicos por defeito. Execute a instalação numa máquina local ou ative-a com a opção panel.install.", + "installation.issues.accounts": "A pasta /site/accounts não existe ou não tem permissão de escrita", + "installation.issues.content": "A pasta /content não existe ou não tem permissão de escrita", "installation.issues.curl": "A extensão CURL é necessária", - "installation.issues.headline": "O painel não pôde ser instalado", + "installation.issues.headline": "Não foi possível instalar o painel", "installation.issues.mbstring": "A extensão MB String é necessária", - "installation.issues.media": "A pasta /media não existe ou não possui permissão de escrita", - "installation.issues.php": "Certifique-se que você está usando o PHP 8+", - "installation.issues.server": "O Kirby necessita do Apache, Nginx ou Caddy", - "installation.issues.sessions": "A pasta /site/sessions não existe ou não possui permissão de escrita", + "installation.issues.media": "A pasta /media não existe ou não tem permissão de escrita", + "installation.issues.php": "Certifique-se que está a usar o PHP 8+", + "installation.issues.sessions": "A pasta /site/sessions não existe ou não tem permissão de escrita", "language": "Idioma", "language.code": "Código", - "language.convert": "Tornar padrão", - "language.convert.confirm": "

Deseja realmente converter {name} para o idioma padrão? Esta ação não poderá ser revertida.

Se {name} tiver conteúdo não traduzido, partes do seu site poderão ficar sem conteúdo.

", - "language.create": "Adicionar novo idioma", - "language.delete.confirm": "Deseja realmente excluir o idioma {name} incluíndo todas as traduções? Esta ação não poderá ser revertida!", - "language.deleted": "Idioma excluído", + "language.convert": "Definir como por defeito", + "language.convert.confirm": "

Tem a certeza que pretende converter {name} para o idioma por defeito? Esta ação não pode ser revertida.

Se {name} tiver conteúdo não traduzido, partes do site podem ficar sem conteúdo.

", + "language.create": "Adicionar um novo idioma", + "language.default": "Idioma por defeito", + "language.delete.confirm": "Tem a certeza que pretende eliminar o idioma {name} incluindo todas as traduções? Esta ação não pode ser revertida!", + "language.deleted": "O idioma foi eliminado", "language.direction": "Direção de leitura", "language.direction.ltr": "Esquerda para direita", "language.direction.rtl": "Direita para esquerda", "language.locale": "String de localização do PHP", "language.locale.warning": "Está a usar configurações de localização personalizadas. Corrija as mesmas no ficheiro /site/languages", "language.name": "Nome", - "language.updated": "Idioma atualizado", + "language.secondary": "Idioma secundário", + "language.settings": "Configurações de idioma", + "language.updated": "O idioma foi atualizado", + "language.variables": "Variáveis de idioma", + "language.variables.empty": "Nenhuma tradução ainda", + + "language.variable.delete.confirm": "Tem a certeza que pretende eliminar a variável {key}?", + "language.variable.key": "Chave", + "language.variable.notFound": "Não foi possível encontrar a variável", + "language.variable.value": "Valor", "languages": "Idiomas", - "languages.default": "Idioma padrão", - "languages.empty": "Nenhum idioma", + "languages.default": "Idioma por defeito", + "languages.empty": "Nenhum idioma ainda", "languages.secondary": "Idiomas secundários", - "languages.secondary.empty": "Nenhum idioma secundário", + "languages.secondary.empty": "Nenhum idioma secundário ainda", - "license": "Licença do Kirby ", - "license.buy": "Comprar uma licença", - "license.register": "Registrar", - "license.manage": "Manage your licenses", - "license.register.help": "Recebeu o código da sua licença por email após a compra. Por favor, copie e cole-o para completar o registro.", - "license.register.label": "Por favor, digite o código da sua licença", - "license.register.domain": "Your license will be registered to {host}.", - "license.register.local": "You are about to register your license for your local domain {host}. If this site will be deployed to a public domain, please register it there instead. If {host} is the domain you want to license Kirby to, please continue.", - "license.register.success": "Obrigado por apoiar o Kirby", - "license.unregistered": "Esta é uma demonstração não registrada do Kirby", - "license.unregistered.label": "Unregistered", + "license": "Licença ", + "license.activate": "Ative-a agora", + "license.activate.label": "Por favor, ative a sua licença", + "license.activate.domain": "A sua licença será ativada para {host}.", + "license.activate.local": "Está prestes a ativar a sua licença Kirby no domínio local {host}. Se este site vai ser alojado num domínio público, por favor ative-o lá. Se o domínio {host} é o que deseja para usar a sua licença, por favor continue.", + "license.activated": "Ativada", + "license.buy": "Compre uma licença", + "license.code": "Código", + "license.code.help": "Recebeu o seu código de licença por email após a compra. Por favor, copie e cole aqui.", + "license.code.label": "Por favor, insira o código da sua licença", + "license.status.active.info": "Inclui novas versões principais até {date}", + "license.status.active.label": "Licença válida", + "license.status.demo.info": "Esta é uma instalação de demonstração", + "license.status.demo.label": "Demonstração", + "license.status.inactive.info": "Renove a licença para atualizar para novas versões principais", + "license.status.inactive.label": "Nenhuma versão principal nova", + "license.status.legacy.bubble": "Pronto para renovar a sua licença?", + "license.status.legacy.info": "A sua licença não abrange esta versão", + "license.status.legacy.label": "Por favor, renove a sua licença", + "license.status.missing.bubble": "Pronto para lançar o seu site?", + "license.status.missing.info": "Sem licença válida", + "license.status.missing.label": "Por favor, ative a sua licença", + "license.status.unknown.info": "O estado da licença é desconhecido", + "license.status.unknown.label": "Desconhecido", + "license.manage": "Gerir as suas licenças", + "license.purchased": "Comprada", + "license.success": "Obrigado por apoiar o Kirby", + "license.unregistered.label": "Não registada", "link": "Link", "link.text": "Texto do link", "loading": "A carregar", - "lock.unsaved": "Alterações por guardar", - "lock.unsaved.empty": "Não existem alterações por guardar", - "lock.isLocked": "Alterações por guardar de {email}", - "lock.file.isLocked": "O arquivo está a ser editado por {email} e não pode ser alterado.", - "lock.page.isLocked": "A página está a ser editada por {email} e não pode ser alterada.", + "lock.unsaved": "Alterações não guardadas", + "lock.unsaved.empty": "Não existem mais alterações não guardadas", + "lock.unsaved.files": "Ficheiros não guardados", + "lock.unsaved.pages": "Páginas não guardadas", + "lock.unsaved.users": "Contas não guardadas", + "lock.isLocked": "Alterações não guardadas de {email}", "lock.unlock": "Desbloquear", - "lock.isUnlocked": "As suas alterações foram sobrepostas por outro utilizador. Pode descarregar as suas alterações e combiná-las manualmente.", + "lock.unlock.submit": "Desbloqueie e substitua alterações não guardadas de {email}", + "lock.isUnlocked": "Foi desbloqueado por outro utilizador", - "login": "Log in", - "login.code.label.login": "Código de acesso", - "login.code.label.password-reset": "Código de redefinição de senha", + "login": "Entrar", + "login.code.label.login": "Código de início de sessão", + "login.code.label.password-reset": "Código de redefinição de palavra-passe", "login.code.placeholder.email": "000 0000", - "login.code.text.email": "Se seu endereço de email está registrado, o código requisitado será mandado por email.", - "login.email.login.body": "Oi, {user.nameOrEmail},\n\nVocê recentemente pediu um código de acesso ao painel administrativo do site {site}.\nO seguinte código será válido por {timeout} minutos:\n\n{code}\n\nSe você não pediu este código de acesso, por favor ignore esta mensagem, ou contate seu Administrador de Sistemas se você tiver dúvidas.\nPor questões de segurança, por favor NÃO compartilhe esta mensagem.", - "login.email.login.subject": "Seu código de acesso", - "login.email.password-reset.body": "Oi, {user.nameOrEmail},\n\nVocê recentemente pediu um código de redefinição de senha, para o painel administrativo do site {site}.\nO seguinte código de redefinição de senha será válido por {timeout} minutos:\n\n{code}\n\nSe você não pediu este código, por favor ignore esta mensagem, ou contate seu Administrador de Sistemas se você tiver dúvidas.\nPor questões de segurança, por favor NÃO compartilhe esta mensagem.", - "login.email.password-reset.subject": "Seu código de redefinição de senha", - "login.remember": "Manter-me conectado", - "login.reset": "Redefinir senha", - "login.toggleText.code.email": "Entrar com email", - "login.toggleText.code.email-password": "Entrar com senha", - "login.toggleText.password-reset.email": "Esqueceu sua senha?", - "login.toggleText.password-reset.email-password": "← Voltar à entrada", + "login.code.placeholder.totp": "000000", + "login.code.text.email": "Se o seu endereço de email está registado, o código solicitado foi enviado por email.", + "login.code.text.totp": "Por favor, insira o código de segurança da sua aplicação de autenticação.", + "login.email.login.body": "Olá {user.nameOrEmail},\n\nRecentemente solicitou um código de início de sessão para o painel de {site}.\nO seguinte código de início de sessão será válido por {timeout} minutos:\n\n{code}\n\nSe não solicitou um código de início de sessão, por favor ignore este e-mail ou entre em contacto com o administrador se tiver dúvidas.\nPor motivos de segurança, por favor NÃO reencaminhe este e-mail.", + "login.email.login.subject": "O seu código de início de sessão", + "login.email.password-reset.body": "Olá {user.nameOrEmail},\n\nRecentemente solicitou um código de redefinição de palavra-passe para o painel de {site}.\nO seguinte código de redefinição de palavra-passe será válido por {timeout} minutos:\n\n{code}\n\nSe não solicitou um código de redefinição de palavra-passe, por favor ignore este e-mail ou entre em contacto com o administrador se tiver dúvidas.\nPor motivos de segurança, por favor NÃO reencaminhe este e-mail.", + "login.email.password-reset.subject": "O seu código de redefinição de palavra-passe", + "login.remember": "Manter sessão iniciada", + "login.reset": "Redefinir palavra-passe", + "login.toggleText.code.email": "Iniciar sessão com email", + "login.toggleText.code.email-password": "Iniciar sessão com palavra-passe", + "login.toggleText.password-reset.email": "Esqueceu a sua palavra-passe?", + "login.toggleText.password-reset.email-password": "← Voltar ao início de sessão", + "login.totp.enable.option": "Configurar códigos de segurança", + "login.totp.enable.intro": "As aplicações de autenticação podem gerar códigos de segurança que são usados como um segundo fator ao iniciar a sessão na sua conta.", + "login.totp.enable.qr.label": "1. Leia este código QR", + "login.totp.enable.qr.help": "Não consegue ler o código? Adicione a chave de configuração {secret} manualmente à sua aplicação de autenticação.", + "login.totp.enable.confirm.headline": "2. Confirme com o código gerado", + "login.totp.enable.confirm.text": "A sua aplicação gera um novo código de segurança a cada 30 segundos. Insira o código atual para concluir a configuração:", + "login.totp.enable.confirm.label": "Código atual", + "login.totp.enable.confirm.help": "Após esta configuração, iremos solicitar um código de segurança sempre que iniciar a sessão.", + "login.totp.enable.success": "Códigos de segurança ativados", + "login.totp.disable.option": "Desativar códigos de segurança", + "login.totp.disable.label": "Insira a sua palavra-passe para desativar códigos de segurança", + "login.totp.disable.help": "No futuro, um segundo fator diferente, como um código de início de sessão enviado por e-mail, será solicitado quando iniciar a sessão. Poderá configurar códigos de segurança novamente mais tarde.", + "login.totp.disable.admin": "

Isto irá desativar os códigos de segurança para {user}.

No futuro, um segundo fator diferente, como um código de início de sessão enviado por e-mail, será solicitado quando eles iniciarem a sessão. {user} poderá configurar códigos de segurança novamente após o próximo início de sessão.

", + "login.totp.disable.success": "Códigos de segurança desativados", - "logout": "Log out", + "logout": "Sair", + "merge": "Unir", "menu": "Menu", "meridiem": "AM/PM", - "mime": "Tipo de mídia", + "mime": "Tipo de Mídia", "minutes": "Minutos", "month": "Mês", - "months.april": "April", + "months.april": "Abril", "months.august": "Agosto", - "months.december": "December", + "months.december": "Dezembro", "months.february": "Fevereiro", "months.january": "Janeiro", "months.july": "Julho", - "months.june": "June", - "months.march": "Março", + "months.june": "Junho", + "months.march": "Mar\u00e7o", "months.may": "Maio", "months.november": "Novembro", "months.october": "Outubro", "months.september": "Setembro", "more": "Mais", + "move": "Mover", "name": "Nome", "next": "Próximo", + "night": "Noite", "no": "não", "off": "off", "on": "on", "open": "Abrir", - "open.newWindow": "Abrir em nova janela", + "open.newWindow": "Abrir numa nova janela", + "option": "Opção", "options": "Opções", "options.none": "Sem opções", + "options.all": "Mostrar todas as {count} opções", "orientation": "Orientação", "orientation.landscape": "Paisagem", "orientation.portrait": "Retrato", "orientation.square": "Quadrado", - "page.blueprint": "Esta página não tem planta. Você pode definir sua planta em /site/blueprints/pages/{blueprint}.yml", + "page": "Página", + "page.blueprint": "Esta página não tem blueprint ainda. Pode configurar o blueprint em /site/blueprints/pages/{blueprint}.yml", "page.changeSlug": "Alterar URL", - "page.changeSlug.fromTitle": "Criar a partir do título", + "page.changeSlug.fromTitle": "Criar a partir do t\u00edtulo", "page.changeStatus": "Alterar estado", "page.changeStatus.position": "Selecione uma posição", "page.changeStatus.select": "Selecione um novo estado", - "page.changeTemplate": "Alterar tema", - "page.delete.confirm": "Deseja realmente excluir {title}?", - "page.delete.confirm.subpages": "Esta página possui subpáginas.
Todas as subpáginas serão excluídas também.", - "page.delete.confirm.title": "Digite o título da página para confirmar", - "page.draft.create": "Criar rascunho", + "page.changeTemplate": "Alterar template", + "page.changeTemplate.notice": "Alterar o template da página irá remover o conteúdo dos campos que não correspondem ao mesmo tipo. Use com cuidado.", + "page.create": "Criar como {status}", + "page.delete.confirm": "Tem a certeza que pretende eliminar {title}?", + "page.delete.confirm.subpages": "Esta página tem subpáginas.
Todas as subpáginas serão eliminadas também.", + "page.delete.confirm.title": "Por favor, insira o título da página para confirmar", "page.duplicate.appendix": "Copiar", - "page.duplicate.files": "Copiar arquivos", + "page.duplicate.files": "Copiar ficheiros", "page.duplicate.pages": "Copiar páginas", - "page.sort": "Mudar posição", + "page.move": "Mover página", + "page.sort": "Alterar posição", "page.status": "Estado", "page.status.draft": "Rascunho", - "page.status.draft.description": "A página está em modo de rascunho e é visível somente para editores", + "page.status.draft.description": "A página está em modo de rascunho e é visível apenas para editores com sessão iniciada ou através de um link secreto", "page.status.listed": "Pública", "page.status.listed.description": "A página é pública para todos", - "page.status.unlisted": "Não listadas", - "page.status.unlisted.description": "Esta página é acessível somente através da URL", + "page.status.unlisted": "Não listada", + "page.status.unlisted.description": "Esta página é acessível apenas através de URL", - "pages": "Pages", - "pages.empty": "Nenhuma página", + "pages": "Páginas", + "pages.delete.confirm.selected": "Tem a certeza que pretende eliminar as páginas selecionadas? Esta ação não pode ser revertida.", + "pages.empty": "Nenhuma página ainda", "pages.status.draft": "Rascunhos", "pages.status.listed": "Publicadas", "pages.status.unlisted": "Não listadas", "pagination.page": "Página", - "password": "Senha", + "password": "Palavra-passe", "paste": "Colar", "paste.after": "Colar após", - "pixel": "Pixel", + "paste.success": "{count} colados!", + "pixel": "Píxel", "plugin": "Plugin", "plugins": "Plugins", "prev": "Anterior", - "preview": "Visualizar", + "preview": "Pré-visualizar", + + "publish": "Publicar", + "published": "Publicadas", + "remove": "Remover", - "rename": "Renomear", - "replace": "Replace", + "rename": "Alterar nome", + "renew": "Renovar", + "replace": "Substituir", + "replace.with": "Substituir por", "retry": "Tentar novamente", - "revert": "Descartar", - "revert.confirm": "Tem a certeza que pretende eliminar todas as alterações por guardar?", + "revert": "Reverter", + "revert.confirm": "Tem a certeza que pretende eliminar todas as alterações não guardadas?", "role": "Função", - "role.admin.description": "O administrador tem todas as permissões.", + "role.admin.description": "O administrador tem todas as permissões", "role.admin.title": "Administrador", "role.all": "Todos", "role.empty": "Não há utilizadores com esta função", "role.description.placeholder": "Sem descrição", - "role.nobody.description": "Esta é uma função de salvaguarda sem permissões.", + "role.nobody.description": "Esta é uma função de recurso sem permissões", "role.nobody.title": "Ninguém", - "save": "Salvar", - "search": "Buscar", - "search.min": "Introduza {min} caracteres para pesquisar", - "search.all": "Mostrar todos", + "save": "Guardar", + "saved": "Guardado", + "search": "Pesquisar", + "searching": "À procura", + "search.min": "Insira {min} caracteres para pesquisar", + "search.all": "Mostrar todos os {count} resultados", "search.results.none": "Sem resultados", - "section.required": "Esta seção é necessária", + "section.invalid": "A secção é inválida", + "section.required": "A secção é obrigatória", - "security": "Security", + "security": "Segurança", "select": "Selecionar", "server": "Servidor", "settings": "Configurações", "show": "Mostrar", - "site.blueprint": "Este site não tem planta. Você pode definir sua planta em /site/blueprints/site.yml", + "site.blueprint": "O site não tem blueprint ainda. Pode configurar o blueprint em /site/blueprints/site.yml", "size": "Tamanho", "slug": "URL", "sort": "Ordenar", + "sort.drag": "Arraste para ordenar ...", + "split": "Dividir", - "stats.empty": "No reports", - "system.issues.content": "The content folder seems to be exposed", - "system.issues.eol.kirby": "Your installed Kirby version has reached end-of-life and will not receive further security updates", - "system.issues.eol.plugin": "Your installed version of the { plugin } plugin is has reached end-of-life and will not receive further security updates", - "system.issues.debug": "Debugging must be turned off in production", - "system.issues.git": "The .git folder seems to be exposed", - "system.issues.https": "We recommend HTTPS for all your sites", - "system.issues.kirby": "The kirby folder seems to be exposed", - "system.issues.site": "The site folder seems to be exposed", - "system.issues.vulnerability.kirby": "Your installation might be affected by the following vulnerability ({ severity } severity): { description }", - "system.issues.vulnerability.plugin": "Your installation might be affected by the following vulnerability in the { plugin } plugin ({ severity } severity): { description }", - "system.updateStatus": "Update status", - "system.updateStatus.error": "Could not check for updates", - "system.updateStatus.not-vulnerable": "No known vulnerabilities", - "system.updateStatus.security-update": "Free security update { version } available", - "system.updateStatus.security-upgrade": "Upgrade { version } with security fixes available", - "system.updateStatus.unreleased": "Unreleased version", - "system.updateStatus.up-to-date": "Up to date", - "system.updateStatus.update": "Free update { version } available", - "system.updateStatus.upgrade": "Upgrade { version } available", + "stats.empty": "Sem relatórios", + "status": "Estado", + + "system.info.copy": "Copiar informação", + "system.info.copied": "Informação de sistema copiada", + "system.issues.content": "A pasta content parece não estar protegida", + "system.issues.eol.kirby": "A versão instalada do Kirby chegou ao fim da sua vida útil e não irá receber mais atualizações de segurança", + "system.issues.eol.plugin": "A versão instalada do plugin { plugin } chegou ao fim da sua vida útil e não irá receber mais atualizações de segurança", + "system.issues.eol.php": "A versão instalada { release } de PHP chegou ao fim da sua vida útil e não irá receber mais atualizações de segurança", + "system.issues.debug": "O modo debug deve ser desativado em produção", + "system.issues.git": "A pasta .git parece não estar protegida", + "system.issues.https": "Nós recomendamos HTTPS para todos os seus sites", + "system.issues.kirby": "A pasta kirby parece não estar protegida", + "system.issues.local": "O site está a correr localmente com verificações de segurança relaxadas", + "system.issues.site": "A pasta site parece não estar protegida", + "system.issues.vue.compiler": "O compilador de templates Vue está ativado", + "system.issues.vulnerability.kirby": "A sua instalação poderá ser afetada pela seguinte vulnerabilidade ({ severity } gravidade): { description }", + "system.issues.vulnerability.plugin": "A sua instalação poderá ser afetada pela seguinte vulnerabilidade no plugin { plugin } ({ severity } gravidade): { description }", + "system.updateStatus": "Atualizar estado", + "system.updateStatus.error": "Não foi possível verificar se havia atualizações", + "system.updateStatus.not-vulnerable": "Nenhuma vulnerabilidade conhecida", + "system.updateStatus.security-update": "Atualização de segurança gratuita { version } disponível", + "system.updateStatus.security-upgrade": "Atualização { version } com correções de segurança disponível", + "system.updateStatus.unreleased": "Versão não lançada", + "system.updateStatus.up-to-date": "Atualizado", + "system.updateStatus.update": "Atualização gratuita { version } disponível", + "system.updateStatus.upgrade": "Atualização { version } disponível", + + "tel": "Telefone", + "tel.placeholder": "+351912345678", + "template": "Template", + + "theme": "Tema", + "theme.light": "Luzes ligadas", + "theme.dark": "Luzes desligadas", + "theme.automatic": "Ajustar ao tema do sistema", "title": "Título", - "template": "Tema", "today": "Hoje", + "toolbar.button.clear": "Limpar formatação", "toolbar.button.code": "Código", "toolbar.button.bold": "Negrito", "toolbar.button.email": "Email", @@ -535,67 +718,75 @@ "toolbar.button.heading.5": "Título 5", "toolbar.button.heading.6": "Título 6", "toolbar.button.italic": "Itálico", - "toolbar.button.file": "File", - "toolbar.button.file.select": "Selecione o arquivo", - "toolbar.button.file.upload": "Carregue o arquivo", + "toolbar.button.file": "Ficheiro", + "toolbar.button.file.select": "Selecione um ficheiro", + "toolbar.button.file.upload": "Envie um ficheiro", "toolbar.button.link": "Link", "toolbar.button.paragraph": "Parágrafo", - "toolbar.button.strike": "Riscado", + "toolbar.button.strike": "Rasurado", + "toolbar.button.sub": "Subscrito", + "toolbar.button.sup": "Sobrescrito", "toolbar.button.ol": "Lista ordenada", "toolbar.button.underline": "Sublinhado", "toolbar.button.ul": "Lista não-ordenada", - "translation.author": "Kirby Team", + "translation.author": "Equipa Kirby", "translation.direction": "ltr", - "translation.name": "Português (Europeu)", + "translation.name": "Português (Portugal)", "translation.locale": "pt_PT", + "type": "Tipo", + "upload": "Enviar", - "upload.error.cantMove": "Não foi possível mover o arquivo carregado", - "upload.error.cantWrite": "Não foi possível guardar o arquivo no sistema de ficheiros.", - "upload.error.default": "Não foi possível carregar o arquivo", - "upload.error.extension": "A extensão do arquivo não permite o carregamento", - "upload.error.formSize": "O arquivo excede o tamanho MAX_FILE_SIZE", - "upload.error.iniPostSize": "O arquivo excede o tamanho post_max_size", - "upload.error.iniSize": "O arquivo carregado excede a definição upload_max_filesize do php.ini", - "upload.error.noFile": "Nenhum arquivo carregado", - "upload.error.noFiles": "Nenhuns arquivos carregados", - "upload.error.partial": "O arquivo foi parcialmente carregado", + "upload.error.cantMove": "Não foi possível mover o ficheiro enviado", + "upload.error.cantWrite": "Não foi possível guardar o ficheiro em disco", + "upload.error.default": "Não foi possível enviar o ficheiro", + "upload.error.extension": "O envio do ficheiro foi interrompido devido à extensão", + "upload.error.formSize": "O ficheiro enviado excede a diretiva MAX_FILE_SIZE especificada no formulário", + "upload.error.iniPostSize": "O ficheiro enviado excede a diretiva post_max_size do php.ini", + "upload.error.iniSize": "O ficheiro enviado excede a diretiva upload_max_filesize do php.ini", + "upload.error.noFile": "Nenhum ficheiro foi enviado", + "upload.error.noFiles": "Nenhum ficheiro foi enviado", + "upload.error.partial": "O ficheiro foi enviado apenas parcialmente", "upload.error.tmpDir": "Pasta temporária em falta", - "upload.errors": "Error", + "upload.errors": "Erro", "upload.progress": "A enviar…", - "url": "Url", + "url": "URL", "url.placeholder": "https://exemplo.pt", "user": "Utilizador", - "user.blueprint": "Você pode definir seções e campos de formulário adicionais para este papel de usuário em /site/blueprints/users/{blueprint}.yml", + "user.blueprint": "Pode definir secções adicionais e campos de formulário para esta função de utilizador em /site/blueprints/users/{blueprint}.yml", "user.changeEmail": "Alterar email", "user.changeLanguage": "Alterar idioma", - "user.changeName": "Renomear este utilizador", + "user.changeName": "Alterar o nome deste utilizador", "user.changePassword": "Alterar palavra-passe", + "user.changePassword.current": "A sua palavra-passe atual", "user.changePassword.new": "Nova palavra-passe", "user.changePassword.new.confirm": "Confirme a nova palavra-passe…", - "user.changeRole": "Alterar Função", + "user.changeRole": "Alterar função", "user.changeRole.select": "Selecione uma nova função", - "user.create": "Add a new user", - "user.delete": "Excluir este utilizador", - "user.delete.confirm": "Deseja realmente excluir
{email}?", + "user.create": "Adicionar um novo utilizador", + "user.delete": "Eliminar este utilizador", + "user.delete.confirm": "Tem a certeza que pretende eliminar
{email}?", - "users": "Usuários", + "users": "Utilizadores", - "version": "Versão do Kirby", - "version.current": "Current version", - "version.latest": "Latest version", - "versionInformation": "Version information", + "version": "Versão", + "version.changes": "Versão alterada", + "version.compare": "Comparar versões", + "version.current": "Versão atual", + "version.latest": "Versão mais recente", + "versionInformation": "Informação da versão", + "view": "Visualizar", "view.account": "A sua conta", - "view.installation": "Instalação", + "view.installation": "Instala\u00e7\u00e3o", "view.languages": "Idiomas", - "view.resetPassword": "Redefinir senha", + "view.resetPassword": "Redefinir palavra-passe", "view.site": "Site", "view.system": "Sistema", - "view.users": "Usuários", + "view.users": "Utilizadores", "welcome": "Bem-vindo", "year": "Ano", diff --git a/kirby/i18n/translations/ro.json b/kirby/i18n/translations/ro.json index 0db8db1..120852a 100644 --- a/kirby/i18n/translations/ro.json +++ b/kirby/i18n/translations/ro.json @@ -3,19 +3,28 @@ "account.delete": "Șterge-ți contul", "account.delete.confirm": "Chiar vrei să îți ștergi contul? Vei fi deconectat imediat. Contul nu poate fi recuperat.", - "add": "Adaugă", + "activate": "Activează", + "add": "Adaug\u0103", + "alpha": "Alfa", "author": "Autor", "avatar": "Imagine de profil", "back": "Înapoi", "cancel": "Anulează", - "change": "Schimbă", - "close": "Închide", + "change": "Modific\u0103", + "close": "\u00cenchide", + "changes": "Schimbări", "confirm": "Ok", "collapse": "Pliază", "collapse.all": "Pliază toate", + "color": "Culoare", + "coordinates": "Coordonate", "copy": "Copiază", "copy.all": "Copiază toate", + "copy.success": "Copiat {count}!", + "copy.success.multiple": "Copiat {count}!", + "copy.url": "Copy URL", "create": "Creează", + "custom": "Personalizat", "date": "Data", "date.select": "Alege o dată", @@ -23,7 +32,7 @@ "day": "Ziua", "days.fri": "Vin", "days.mon": "Lun", - "days.sat": "Sâm", + "days.sat": "S\u00e2m", "days.sun": "Dum", "days.thu": "Joi", "days.tue": "Mar", @@ -31,31 +40,40 @@ "debugging": "Depanare", - "delete": "Șterge", + "delete": "\u0218terge", "delete.all": "Șterge toate", + "dialog.fields.empty": "Acest dialog nu are niciun câmp", "dialog.files.empty": "Nu există fișiere de selectat", "dialog.pages.empty": "Nu există pagini de selectat", + "dialog.text.empty": "Acest dialog nu definește niciun text", "dialog.users.empty": "Nu există utilizatori de selectat", "dimensions": "Dimensiuni", + "disable": "Dezactivați", "disabled": "Dezactivat", - "discard": "Renunță", + "discard": "Renun\u021b\u0103", + + "drawer.fields.empty": "Acest sertar nu are niciun câmp", + + "domain": "Domeniu", "download": "Descarcă", "duplicate": "Duplică", - "edit": "Editează", + "edit": "Editeaz\u0103", - "email": "Adresă e-mail", - "email.placeholder": "email@exemplu.ro", + "email": "E-mail", + "email.placeholder": "email@exemplu.com", + "enter": "Introdu", "entries": "Întregistrări", "entry": "Înregistrare", "environment": "Mediu", - "error.access.code": "Cod invalid", - "error.access.login": "Conectare invalidă", + "error": "Eroare", + "error.access.code": "Cod nevalid", + "error.access.login": "Conectare nevalidă", "error.access.panel": "Nu ai voie să accesezi panoul", "error.access.view": "Nu ai voie să accesezi această parte a panoului", @@ -72,77 +90,113 @@ "error.blocks.min.singular": "Trebuie să adaugi cel puțin un bloc", "error.blocks.validation": "Există o eroare la câmpul \"{field}\" în blocul {index} care folosește tipul de bloc \"{fieldset}\"", - "error.cache.type.invalid": "Tipul de cache \"{type}\" este invalid", + "error.cache.type.invalid": "Tipul de cache \"{type}\" este nevalid", + + "error.content.lock.delete": "The version is locked and cannot be deleted", + "error.content.lock.move": "The source version is locked and cannot be moved", + "error.content.lock.publish": "This version is already published", + "error.content.lock.replace": "The version is locked and cannot be replaced", + "error.content.lock.update": "The version is locked and cannot be updated", + + "error.entries.max.plural": "You must not add more than {max} entries", + "error.entries.max.singular": "You must not add more than one entry", + "error.entries.min.plural": "You must add at least {min} entries", + "error.entries.min.singular": "You must add at least one entry", + "error.entries.supports": "\"{type}\" field type is not supported for the entries field", + "error.entries.validation": "Există o eroare la câmpul \"{field}\" pe rândul {index}", "error.email.preset.notFound": "Preset-ul de e-mail \"{name}\" nu a fost găsit", - "error.field.converter.invalid": "Convertorul \"{converter}\" invalid", + "error.field.converter.invalid": "Convertorul \"{converter}\" nu este valid", + "error.field.link.options": "Invalid options: {options}", "error.field.type.missing": "Câmpul \"{ name }\": Tipul de câmp \"{ type }\" nu există", "error.file.changeName.empty": "Numele nu trebuie să fie gol", "error.file.changeName.permission": "Nu ai voie să schimbi numele fișierului \"{filename}\"", + "error.file.changeTemplate.invalid": "Șablonul pentru fișierul \"{id}\" nu poate fi schimbat la \"{template}\" (valide: \"{blueprints}\")", + "error.file.changeTemplate.permission": "Nu ai voie să schimbi șablonul pentru fișierul \"{id}\"", + + "error.file.delete.multiple": "Not all files could be deleted. Try each remaining file individually to see the specific error that prevents deletion.", "error.file.duplicate": "Există deja un fișier cu numele \"{filename}\"", "error.file.extension.forbidden": "Extensia de fișier \"{extension}\" nu este permisă", - "error.file.extension.invalid": "Extensie de fișier invalidă: {extension}", + "error.file.extension.invalid": "Extensie de fișier nevalidă: {extension}", "error.file.extension.missing": "Extensia de fișier pentru \"{filename}\" lipsește", "error.file.maxheight": "Înălțimea imaginii nu poate depăși {height} pixeli", "error.file.maxsize": "Fișierul este prea mare", "error.file.maxwidth": "Lățimea imaginii nu poate depăși {width} pixeli", "error.file.mime.differs": "Fișierul încărcat trebuie să aibă același tip mime \"{mime}\"", "error.file.mime.forbidden": "Tipul media \"{mime}\" nu este permis", - "error.file.mime.invalid": "Tip mime invalid: {mime}", + "error.file.mime.invalid": "Tip mime nevalid: {mime}", "error.file.mime.missing": "Tipul media pentru \"{filename}\" nu poate fi detectat", "error.file.minheight": "Imaginea trebuie să aibă înălțimea de minim {height} pixeli", "error.file.minsize": "Fișierul este prea mic", "error.file.minwidth": "Imaginea trebuie să aibă lățimea de minim {width} pixeli", + "error.file.name.unique": "Numele fișierului trebuie să fie unic", "error.file.name.missing": "Numele fișierului nu poate fi gol", "error.file.notFound": "Fișierul \"{filename}\" nu a fost găsit", "error.file.orientation": "Orientarea imaginii trebuie să fie \"{orientation}\"", - "error.file.type.forbidden": "Nu ai permisiunea să încarci fișiere {type}", - "error.file.type.invalid": "Tip invalid de fișier: {type}", + "error.file.sort.permission": "You are not allowed to change the sorting of \"{filename}\"", + "error.file.type.forbidden": "Nu ai voie să încarci fișiere {type}", + "error.file.type.invalid": "Tip nevalid de fișier: {type}", "error.file.undefined": "Fișierul nu a fost găsit", "error.form.incomplete": "Te rog repară toate erorile din formular…", "error.form.notSaved": "Formularul nu a putut fi salvat", "error.language.code": "Te rog introdu un cod valid pentru limbă", + "error.language.create.permission": "You are not allowed to create a language", + "error.language.delete.permission": "You are not allowed to delete the language", "error.language.duplicate": "Limba există deja", "error.language.name": "Te rog introdu un nume valid pentru limbă", "error.language.notFound": "Limba nu a fost găsită", + "error.language.update.permission": "You are not allowed to update the language", "error.layout.validation.block": "Există o eroare la câmpul \"{field}\" în blocul {blockIndex} care utilizează tipul de bloc \"{fieldset}\" în aranjamentul {layoutIndex}", "error.layout.validation.settings": "Există o eroare la setările aranjamentului {index}", - "error.license.format": "Te rog introdu o cheie de licență validă", + "error.license.domain": "Domeniul pentru licență lipsește", "error.license.email": "Te rog introdu o adresă de e-mail validă", + "error.license.format": "Te rog introdu un cod de licență valid", "error.license.verification": "Licența nu a putut fi verificată", + "error.login.totp.confirm.invalid": "Cod nevalid", + "error.login.totp.confirm.missing": "Vă rugăm să introduceți codul curent", + "error.object.validation": "Există o eroare la câmpul \"{label}\":\n{message}", "error.offline": "Panoul este momentan offline", "error.page.changeSlug.permission": "Nu ai voie să schimbi apendicele URL pentru \"{slug}\"", + "error.page.changeSlug.reserved": "Calea paginilor de la primul nivel nu poate să înceapă cu \"{path}\"", "error.page.changeStatus.incomplete": "Pagina are erori și nu poate fi publicată", - "error.page.changeStatus.permission": "Statusul acestei pagini nu poate fi schimbat", + "error.page.changeStatus.permission": "Starea acestei pagini nu poate fi schimbată", "error.page.changeStatus.toDraft.invalid": "Pagina \"{slug}\" nu poate fi schimbată în ciornă", "error.page.changeTemplate.invalid": "Șablonul paginii \"{slug}\" nu poate fi schimbat", "error.page.changeTemplate.permission": "Nu ai voie să schimbi șablonul pentru \"{slug}\"", - "error.page.changeTitle.empty": "Titlul nu poate să rămână gol", + "error.page.changeTitle.empty": "Titlul nu poate rămâne gol", "error.page.changeTitle.permission": "Nu ai voie să schimbi titlul pentru \"{slug}\"", "error.page.create.permission": "Nu ai voie să creezi \"{slug}\"", "error.page.delete": "Pagina \"{slug}\" nu poate fi ștearsă", "error.page.delete.confirm": "Te rog introdu titlul paginii pentru a confirma", "error.page.delete.hasChildren": "Pagina are subpagini și nu poate fi ștearsă", + "error.page.delete.multiple": "Not all pages could be deleted. Try each remaining page individually to see the specific error that prevents deletion.", "error.page.delete.permission": "Nu ai voie să ștergi \"{slug}\"", "error.page.draft.duplicate": "Există deja o ciornă cu apendicele URL \"{slug}\"", "error.page.duplicate": "Există deja o pagină cu apendicele URL \"{slug}\"", "error.page.duplicate.permission": "Nu ai voie să duplici \"{slug}\"", + "error.page.move.ancestor": "Pagina nu poate fi mutată în ea însăși", + "error.page.move.directory": "Directorul de pagini nu poate fi mutat", + "error.page.move.duplicate": "Există deja o sub-pagină cu apendicele URL \"{slug}\"", + "error.page.move.noSections": "The page \"{parent}\" cannot be a parent of any page because it lacks any pages sections in its blueprint", + "error.page.move.notFound": "Pagina mutată nu a fost găsită", + "error.page.move.permission": "Nu ai voie să muți \"{slug}\"", + "error.page.move.template": "Șablonul \"{template}\" nu este acceptat ca sub-pagină a \"{parent}\"", "error.page.notFound": "Pagina \"{slug}\" nu a fost găsită", "error.page.num.invalid": "Te rog introdu un număr de sortare valid. Numerele nu pot fi negative.", "error.page.slug.invalid": "Te rog introdu un apendice URL valid", "error.page.slug.maxlength": "Lungimea slug-ului nu poate depăși \"{length}\"", "error.page.sort.permission": "Pagina \"{slug}\" nu poate fi sortată", - "error.page.status.invalid": "Te rog stabilește un status valid pentru pagină", + "error.page.status.invalid": "Te rog stabilește o stare validă pentru pagină", "error.page.undefined": "Pagina nu a fost găsită", "error.page.update.permission": "Nu ai voie să actualizezi \"{slug}\"", @@ -163,6 +217,8 @@ "error.site.changeTitle.permission": "Nu ai voie să schimbi titlul site-ului", "error.site.update.permission": "Nu ai voie să actualizezi site-ul", + "error.structure.validation": "Există o eroare la câmpul \"{field}\" pe rândul {index}", + "error.template.default.notFound": "Șablonul implicit nu există", "error.unexpected": "S-a produs o eroare neașteptată! Activează modul depanare pentru mai multe informații: https://getkirby.com/docs/reference/system/options/debug", @@ -195,8 +251,10 @@ "error.validation.accepted": "Te rog confirmă", "error.validation.alpha": "Te rog introdu doar caractere din intervalul a-z", "error.validation.alphanum": "Te rog introdu doar caractere din intervalul a-z sau cifre 0-9", + "error.validation.anchor": "Te rog introdu o ancoră corectă pentru legătură", "error.validation.between": "Te rog introdu o valoare între \"{min}\" și \"{max}\"", "error.validation.boolean": "Te rog confirmă sau refuză", + "error.validation.color": "Te rog introdu o culoare validă în formatul {format}", "error.validation.contains": "Te rog introdu o valoare care conține \"{needle}\"", "error.validation.date": "Te rog introdu o dată validă", "error.validation.date.after": "Te rog introdu o dată după {date}", @@ -211,6 +269,7 @@ "error.validation.integer": "Te rog introdu un număr întreg valid", "error.validation.ip": "Te rog introdu o adresă IP validă", "error.validation.less": "Te rog introdu o valoare mai mică decât {max}", + "error.validation.linkType": "Tipul de legătură nu este permis", "error.validation.match": "Valoarea nu se potrivește cu forma așteptată", "error.validation.max": "Te rog introdu o valoare mai mică sau egală cu {max}", "error.validation.maxlength": "Te rog introdu o valoare mai scurtă. (max. {max} caractere)", @@ -227,15 +286,18 @@ "error.validation.same": "Te rog introdu \"{other}\"", "error.validation.size": "Dimensiunea valorii trebuie să fie \"{size}\"", "error.validation.startswith": "Valoarea trebuie să înceapă cu \"{start}\"", + "error.validation.tel": "Te rog introdu un număr de telefon neformatat", "error.validation.time": "Te rog introdu un timp valid", "error.validation.time.after": "Te rog introdu un timp după {time}", "error.validation.time.before": "Te rog introdu un timp înainte de {time}", "error.validation.time.between": "Te rog introdu un timp între {min} și {max}", + "error.validation.uuid": "Te rog introdu un UUID valid", "error.validation.url": "Te rog introdu un URL valid", "expand": "Extinde", "expand.all": "Extinde toate", + "field.invalid": "Câmpul este nevalid", "field.required": "Acest câmp este necesar", "field.blocks.changeType": "Schimbă tipul", "field.blocks.code.name": "Cod", @@ -245,8 +307,9 @@ "field.blocks.delete.confirm.all": "Chiar vrei să ștergi toate blocurile?", "field.blocks.delete.confirm.selected": "Chiar vrei să ștergi blocurile selectate?", "field.blocks.empty": "Niciun bloc deocamdată", + "field.blocks.fieldsets.empty": "Niciun set de câmpuri încă", "field.blocks.fieldsets.label": "Te rog alege un tip de bloc …", - "field.blocks.fieldsets.paste": "Apasă {{ shortcut }} pentru a insera/aduce blocuri din clipboard-ul tău", + "field.blocks.fieldsets.paste": "Apasă {{ shortcut }} pentru a importa aranjamente/blocuri din clipboard Doar cele permise pentru câmpul curent vor fi inserate.", "field.blocks.gallery.name": "Galerie", "field.blocks.gallery.images.empty": "Nicio imagine deocamdată", "field.blocks.gallery.images.label": "Imagini", @@ -254,11 +317,16 @@ "field.blocks.heading.name": "Subtitlu", "field.blocks.heading.text": "Text", "field.blocks.heading.placeholder": "Subtitlu …", + "field.blocks.figure.back.plain": "Plain", + "field.blocks.figure.back.pattern.light": "Pattern (light)", + "field.blocks.figure.back.pattern.dark": "Pattern (dark)", "field.blocks.image.alt": "Text alternativ", "field.blocks.image.caption": "Etichetă", "field.blocks.image.crop": "Decupaj", "field.blocks.image.link": "Legătură", "field.blocks.image.location": "Localizare", + "field.blocks.image.location.internal": "Acest website", + "field.blocks.image.location.external": "Sursă externă", "field.blocks.image.name": "Imagine", "field.blocks.image.placeholder": "Alege o imagine", "field.blocks.image.ratio": "Raport", @@ -275,38 +343,72 @@ "field.blocks.quote.citation.placeholder": "de …", "field.blocks.text.name": "Text", "field.blocks.text.placeholder": "Text …", + "field.blocks.video.autoplay": "Autoredare", "field.blocks.video.caption": "Etichetă", + "field.blocks.video.controls": "Controale", + "field.blocks.video.location": "Localizare", + "field.blocks.video.loop": "În buclă", + "field.blocks.video.muted": "Fără sonor", "field.blocks.video.name": "Video", "field.blocks.video.placeholder": "Introdu URL-ul video-ului", + "field.blocks.video.poster": "Poster", + "field.blocks.video.preload": "Preîncarcă", "field.blocks.video.url.label": "URL-ul video-ului", "field.blocks.video.url.placeholder": "https://youtube.com/?v=", - "field.files.empty": "Niciun fișier selectat deocamdată", + "field.entries.delete.confirm.all": "Chiar vrei să ștergi toate înregistrările?", + "field.entries.empty": "Nicio înregistrare deocamdată", + "field.files.empty": "Niciun fișier selectat deocamdată", + "field.files.empty.single": "No file selected yet", + + "field.layout.change": "Schimbă aranjament", "field.layout.delete": "Șterge aranjamentul", "field.layout.delete.confirm": "Chiar vrei să ștergi acest aranjament?", + "field.layout.delete.confirm.all": "Chiar vrei să ștergi toate aranjamentele?", "field.layout.empty": "Niciun rând deocamdată", "field.layout.select": "Alege un aranjament", "field.object.empty": "Nicio informație deocamdată", "field.pages.empty": "Nicio pagină aleasă deocamdată", + "field.pages.empty.single": "No page selected yet", "field.structure.delete.confirm": "Chiar vrei să ștergi acest rând?", "field.structure.delete.confirm.all": "Chiar vrei să ștergi toate înregistrările?", "field.structure.empty": "Nicio înregistrare deocamdată", "field.users.empty": "Niciun utilizator ales deocamdată", + "field.users.empty.single": "No user selected yet", + "fields.empty": "Niciun câmp deocamdată", + + "file": "Fișier", "file.blueprint": "Acest fișier nu are încă un Blueprint. Poți să-l definești în /site/blueprints/files/{blueprint}.yml", + "file.changeTemplate": "Schimbă șablonul", + "file.changeTemplate.notice": "Schimbarea șablonului fișierului va înlătura conținutul câmpurilor care nu se potrivesc ca tip. Dacă noul șablon definește anumite reguli, de ex. dimensiuni de imagini, acestea vor fi de asemenea aplicate în mod ireversibil. Folosiți cu prudență.", "file.delete.confirm": "Chiar vrei să ștergi
{filename}?", + "file.focus.placeholder": "Stabilește punct focal", + "file.focus.reset": "Înlătură punct focal", + "file.focus.title": "Focalizare", "file.sort": "Schimbă poziția", "files": "Fișiere", + "files.delete.confirm.selected": "Do you really want to delete the selected files? This action cannot be undone.", "files.empty": "Niciun fișier deocamdată", + "filter": "Filtru", + + "form.discard": "Discard changes", + "form.discard.confirm": "Do you really want to discard all your changes?", + "form.locked": "This content is disabled for you as it is currently edited by another user", + "form.unsaved": "The current changes have not yet been saved", + "form.preview": "Preview changes", + "form.preview.draft": "Preview draft", + "hide": "Ascunde", "hour": "Ora", + "hue": "Nuanță", "import": "Importă", "info": "Informații", "insert": "Inserează", @@ -324,7 +426,6 @@ "installation.issues.mbstring": "Extensia MB String este necesară", "installation.issues.media": "Directorul /media nu există sau nu are permisiuni de scriere", "installation.issues.php": "Asigură-te că folosești PHP 8+", - "installation.issues.server": "Kirby are nevoie de Apache, Nginx sau Caddy", "installation.issues.sessions": "Directorul /site/sessions folder nu există sau nu are permisiuni de scriere", "language": "Limba", @@ -332,6 +433,7 @@ "language.convert": "Stabilește ca implicit", "language.convert.confirm": "

Chiar vrei să transformi {name} în limba implicită? Această modificare este ireversibilă.

Dacă {name} are conținut netradus, unele părți din site s-ar putea să nu mai aibă conținut de rezervă, și vor apărea goale.

", "language.create": "Adaugă o limbă nouă", + "language.default": "Limba implicită", "language.delete.confirm": "Chiar vrei să ștergi limba {name}, inclusiv toate traducerile? Această operațiune este ireversibilă.", "language.deleted": "Limba a fost ștearsă", "language.direction": "Direcția de citire", @@ -340,7 +442,16 @@ "language.locale": "String-ul PHP locale", "language.locale.warning": "Folosești pentru localizare o formulă manuală. Modificările le poți face în fișierul de limbă în /site/languages", "language.name": "Nume", + "language.secondary": "Limbă secundară", + "language.settings": "Reglaje limbă", "language.updated": "Limba a fost actualizată", + "language.variables": "Variabile limbă", + "language.variables.empty": "Nicio traducere deocamdată", + + "language.variable.delete.confirm": "Chiar vrei să ștergi variabila pentru {key}?", + "language.variable.key": "Cheie", + "language.variable.notFound": "Variabila nu a fost găsită", + "language.variable.value": "Valoare", "languages": "Limbi", "languages.default": "Limba implicită", @@ -349,15 +460,32 @@ "languages.secondary.empty": "Nu există limbi secundare deocamdată.", "license": "Licența", + "license.activate": "Activați acum", + "license.activate.label": "Vă rugăm să activați licența", + "license.activate.domain": "Licența va fi activată pentru {host}.", + "license.activate.local": "Sunteți pe cale să activați licența Kirby pentru domeniul local {host}. Dacă acest site va fi implementat pe un domeniu public, vă rugăm să o activați acolo în schimb. Dacă {host} este domeniul pentru care doriți să utilizați licența, vă rugăm să continuați.", + "license.activated": "Activată", "license.buy": "Cumpără o licență", - "license.register": "Înregistrează-te", + "license.code": "Cod", + "license.code.help": "Ați primit codul de licență după achiziție prin e-mail. Vă rugăm să-l copiați și să-l inserezi aici.", + "license.code.label": "Te rog introdu codul tău de licență", + "license.status.active.info": "Include noi versiuni majore până la data de {date}", + "license.status.active.label": "Licență validă", + "license.status.demo.info": "Aceasta este o instalare demo", + "license.status.demo.label": "Demo", + "license.status.inactive.info": "Reînnoiți licența pentru a actualiza la noile versiuni majore", + "license.status.inactive.label": "Fără noi versiuni majore", + "license.status.legacy.bubble": "Sunteți pregătit să reînnoiți licența?", + "license.status.legacy.info": "Licența dvs. nu acoperă această versiune", + "license.status.legacy.label": "Vă rugăm să reînnoiți licența", + "license.status.missing.bubble": "Sunteți pregătit să lansați site-ul?", + "license.status.missing.info": "Licență nevalidă", + "license.status.missing.label": "Vă rugăm să activați licența", + "license.status.unknown.info": "The license status is unknown", + "license.status.unknown.label": "Unknown", "license.manage": "Gestionează-ți licențele", - "license.register.help": "Ai primit codul de licență pe adresa de e-mail după cumpărare. Te rog copiaz-o și insereaz-o pentru a te înregistra.", - "license.register.label": "Te rog introdu codul tău de licență", - "license.register.domain": "Licența îți va fi înregistrată pentru {host}.", - "license.register.local": "Ești pe punctul de a-ți înregistra licența pentru domeniul tău local {host}. Dacă acest site va fi instalat pe un domeniu public, te rog înregistrează licența acolo, nu aici. Dacă {host} este domeniul pentru care vrei licența Kirby, te rog continuă.", - "license.register.success": "Mulțumim că susții Kirby", - "license.unregistered": "Acesta este un demo Kirby neînregistrat", + "license.purchased": "Achiziționat", + "license.success": "Mulțumim că susții Kirby", "license.unregistered.label": "Neînregistrat", "link": "Legătură", @@ -367,17 +495,21 @@ "lock.unsaved": "Schimbări nesalvate", "lock.unsaved.empty": "Nu mai există nicio schimbare nesalvată", - "lock.isLocked": "Schimbări nesalvate de {email}", - "lock.file.isLocked": "Fișierul este editat momentan de {email} și nu poate fi schimbat.", - "lock.page.isLocked": "Pagina este editată momentan de {email} și nu poate fi schimbată.", + "lock.unsaved.files": "Unsaved files", + "lock.unsaved.pages": "Unsaved pages", + "lock.unsaved.users": "Unsaved accounts", + "lock.isLocked": "Schimbări nesalvate de {email}", "lock.unlock": "Deblochează", - "lock.isUnlocked": "Schimbările tale nesalvate au fost suprascrise de un alt utilizator. Poți să-ți descarci schimbările pentru a le încorpora manual.", + "lock.unlock.submit": "Deblochează și suprascrie schimbările nesalvate de {email}", + "lock.isUnlocked": "A fost deblocată de alt utilizator", "login": "Conectează-te", "login.code.label.login": "Cod de conectare", "login.code.label.password-reset": "Cod de restabilire parolă", "login.code.placeholder.email": "000 000", + "login.code.placeholder.totp": "000000", "login.code.text.email": "Dacă adresa de e-mail este înregistrată, codul cerut a fost trimis pe adresă.", + "login.code.text.totp": "Vă rugăm să introduceți codul unic de pe aplicația dvs. de autentificare.", "login.email.login.body": "Salut {user.nameOrEmail},\n\nAi cerut recent un cod de conectare pentru Panoul site-ului {site}.\nCodul de conectare de mai jos va fi valid pentru următoarele {timeout} minute:\n\n{code}\n\nDacă nu tu ai cerut un cod de conectare, te rog ignoră acest e-mail sau ia legătura cu administratorul site-ului dacă ai întrebări.\nDin motive de siguranță, te rog să NU trimiți acest email mai departe.", "login.email.login.subject": "Codul tău de conectare", "login.email.password-reset.body": "Salut {user.nameOrEmail},\n\nAi cerut recent un cod de restabilire a parolei pentru Panoul site-ului {site}.\nCodul de restabilire a parolei de mai jos este valabil pentru următoarele {timeout} minute:\n\n{code}\n\nDacă nu tu ai cerut codul de restabilire a parolei, te rog ignoră acest e-mail sau ia legătura cu administratorul site-ului dacă ai întrebări.\nDin motive de securitate, te rog să NU trimiți acest e-mail mai departe.", @@ -388,9 +520,24 @@ "login.toggleText.code.email-password": "Conectare cu parola", "login.toggleText.password-reset.email": "Ți-ai uitat parola?", "login.toggleText.password-reset.email-password": "← Înapoi la conectare", + "login.totp.enable.option": "Configurați codurile de unică folosință", + "login.totp.enable.intro": "Aplicațiile de autentificare pot genera coduri de unică folosință utilizate ca al doilea factor la autentificarea în contul dvs.", + "login.totp.enable.qr.label": "1. Scanați acest cod QR", + "login.totp.enable.qr.help": "Nu puteți scana? Adăugați manual cheia de configurare {secret} în aplicația dvs. de autentificare.", + "login.totp.enable.confirm.headline": "2. Confirmați cu codul generat", + "login.totp.enable.confirm.text": "Aplicația dvs. generează un nou cod de unică folosință la fiecare 30 de secunde. Introduceți codul curent pentru a finaliza configurarea:", + "login.totp.enable.confirm.label": "Cod curent", + "login.totp.enable.confirm.help": "După această configurare, vă vom solicita un cod de unică folosință de fiecare dată când vă autentificați.", + "login.totp.enable.success": "Codurile de unică folosință activate", + "login.totp.disable.option": "Dezactivați codurile de unică folosință", + "login.totp.disable.label": "Introduceți parola pentru a dezactiva codurile de unică folosință", + "login.totp.disable.help": "În viitor, va fi solicitat un al doilea factor diferit, cum ar fi un cod de autentificare trimis prin e-mail, atunci când vă autentificați. Puteți configura din nou codurile de unică folosință oricând mai târziu.", + "login.totp.disable.admin": "

Această acțiune va dezactiva codurile de unică folosință pentru {user}.

În viitor, se va solicita un al doilea factor diferit, cum ar fi un cod de autentificare trimis prin e-mail, atunci când se autentifică. {user} poate configura din nou codurile de unică folosință după următoarea autentificare.", + "login.totp.disable.success": "Codurile de unică folosință dezactivate", - "logout": "Deconectează-te", + "logout": "Deconectare", + "merge": "Îmbină", "menu": "Meniu", "meridiem": "AM/PM", "mime": "Tipul media", @@ -411,45 +558,53 @@ "months.september": "Septembrie", "more": "Mai multe", + "move": "Mută", "name": "Nume", "next": "Următoarea", + "night": "Noapte", "no": "nu", "off": "oprit", "on": "pornit", "open": "Deschide", "open.newWindow": "Deschide în fereastră nouă", + "option": "Opțiune", "options": "Opțiuni", "options.none": "Nicio opțiune", + "options.all": "Afișați toate {count} opțiunile", "orientation": "Orientare", "orientation.landscape": "Landscape", "orientation.portrait": "Portrait", "orientation.square": "Pătrată", + "page": "Pagină", "page.blueprint": "Această pagină nu are încă un Blueprint. Poți să-l definești în /site/blueprints/pages/{blueprint}.yml", "page.changeSlug": "Schimbă URL-ul", "page.changeSlug.fromTitle": "Creează din titlu", - "page.changeStatus": "Schimbă statusul", + "page.changeStatus": "Schimbă starea", "page.changeStatus.position": "Te rog alege o poziție", - "page.changeStatus.select": "Alege un status nou", + "page.changeStatus.select": "Alege o stare nouă", "page.changeTemplate": "Schimbă șablonul", + "page.changeTemplate.notice": "Schimbarea șablonului paginii va înlătura conținutul câmpurilor care nu se potrivesc ca tip. Folosește cu prudență.", + "page.create": "Creează ca {status}", "page.delete.confirm": "Chiar vrei să ștergi {title}?", "page.delete.confirm.subpages": "Această pagină are subpagini.
Subpaginile vor fi de asemenea toate șterse.", "page.delete.confirm.title": "Introdu titlul paginii pentru a confirma", - "page.draft.create": "Creează ciornă", "page.duplicate.appendix": "Copiază", "page.duplicate.files": "Copiază fișierele", "page.duplicate.pages": "Copiază paginile", + "page.move": "Mută pagina", "page.sort": "Schimbă poziția", - "page.status": "Status", + "page.status": "Stare", "page.status.draft": "Ciornă", "page.status.draft.description": "Pagina este în modul ciornă și va fi vizibilă doar editorilor conectați sau printr-un link secret", "page.status.listed": "Publică", "page.status.listed.description": "Pagina este publică, accesibilă oricui", - "page.status.unlisted": "Nelistate", + "page.status.unlisted": "Nelistată", "page.status.unlisted.description": "Pagina este accesibilă doar prin URL", "pages": "Pagini", + "pages.delete.confirm.selected": "Do you really want to delete the selected pages? This action cannot be undone.", "pages.empty": "Nicio pagină deocamdată", "pages.status.draft": "Ciorne", "pages.status.listed": "Publicate", @@ -460,14 +615,21 @@ "password": "Parola", "paste": "Inserează", "paste.after": "Inserează după", + "paste.success": "inserate {count}!", "pixel": "Pixel", "plugin": "Plugin", "plugins": "Plugin-uri", "prev": "Precedenta", "preview": "Previzualizează", + + "publish": "Publish", + "published": "Publicate", + "remove": "Înlătură", "rename": "Redenumește", - "replace": "Înlocuiește", + "renew": "Reînnoiți", + "replace": "\u00cenlocuie\u0219te", + "replace.with": "Înlocuiește cu", "retry": "Încearcă din nou", "revert": "Renunță", "revert.confirm": "Chiar vrei să ștergi toate schimbările nesalvate?", @@ -481,12 +643,15 @@ "role.nobody.description": "Acesta este un rol de rezervă fără nicio permisiune.", "role.nobody.title": "Nimeni", - "save": "Salvează", + "save": "Salveaz\u0103", + "saved": "Saved", "search": "Caută", + "searching": "Searching", "search.min": "Introdu {min} caractere pentru a căuta", - "search.all": "Arată toate", + "search.all": "Afișați toate {count} rezultatele", "search.results.none": "Niciun rezultat", + "section.invalid": "Secțiunea este nevalidă", "section.required": "Această secțiune este necesară", "security": "Securitate", @@ -498,19 +663,28 @@ "size": "Dimensiune", "slug": "Apendicele URL", "sort": "Sortare", + "sort.drag": "Trage pt. a sorta …", + "split": "Împarte", "stats.empty": "Niciun raport", + "status": "Stare", + + "system.info.copy": "Copy info", + "system.info.copied": "System info copied", "system.issues.content": "Directorul de conținut pare să fie expus", "system.issues.eol.kirby": "Versiunea instalată de Kirby a ajuns la sfârșitul vieții utile și nu va mai primi actualizări de securitate.", "system.issues.eol.plugin": "Versiunea instalată a plugin-ului { plugin } a ajuns la sfârșitul vieții utile și nu va mai primi actualizări de securitate.", + "system.issues.eol.php": "Versiunea PHP instalată { release } a ajuns la sfârșitul vieții și nu va mai primi actualizări de securitate", "system.issues.debug": "Modul depanare trebuie să fie oprit în producție", "system.issues.git": "Directorul .git pare să fie expus", "system.issues.https": "Recomandăm HTTPS pentru toate site-urile.", "system.issues.kirby": "Directorul Kirby pare să fie expus", + "system.issues.local": "The site is running locally with relaxed security checks", "system.issues.site": "Directorul site pare să fie expus", + "system.issues.vue.compiler": "The Vue template compiler is enabled", "system.issues.vulnerability.kirby": "Instalarea ta ar putea fi afectată de următoarea vulnerabilitate ({ severity } severity): { description }", "system.issues.vulnerability.plugin": "Instalarea ta ar putea fi afectată de următoarea vulnerabilitate în plugin-ul { plugin } ({ severity } severity): { description }", - "system.updateStatus": "Status actualizare", + "system.updateStatus": "Starea actualizării", "system.updateStatus.error": "Nu am putut căuta actualizări", "system.updateStatus.not-vulnerable": "Nicio vulnerabilitate cunoscută", "system.updateStatus.security-update": "Actualizare gratuită de securitate { version } disponibilă", @@ -520,10 +694,19 @@ "system.updateStatus.update": "Actualizare gratuită { version } disponibilă", "system.updateStatus.upgrade": "Actualizare { version } disponibilă", - "title": "Titlu", + "tel": "Telefon", + "tel.placeholder": "+40123456789", "template": "Șablon", + + "theme": "Theme", + "theme.light": "Lights on", + "theme.dark": "Lights off", + "theme.automatic": "Match system default", + + "title": "Titlu", "today": "Astăzi", + "toolbar.button.clear": "Elimină formatarea", "toolbar.button.code": "Cod", "toolbar.button.bold": "Bold", "toolbar.button.email": "Adresă e-mail", @@ -538,18 +721,22 @@ "toolbar.button.file": "Fișier", "toolbar.button.file.select": "Alege un fișier", "toolbar.button.file.upload": "Încarcă un fișier", - "toolbar.button.link": "Legătură", + "toolbar.button.link": "Link", "toolbar.button.paragraph": "Paragraf", "toolbar.button.strike": "Tăiat", + "toolbar.button.sub": "Indice", + "toolbar.button.sup": "Exponent", "toolbar.button.ol": "Listă ordonată", "toolbar.button.underline": "Subliniat", "toolbar.button.ul": "Listă cu puncte", "translation.author": "Echipa Kirby", "translation.direction": "ltr", - "translation.name": "Română", + "translation.name": "Rom\u00e2n\u0103", "translation.locale": "ro_RO", + "type": "Tip", + "upload": "Încarcă", "upload.error.cantMove": "Fișierul încărcat nu a putut fi mutat", "upload.error.cantWrite": "Nu s-a putut scrie fișierul pe disc", @@ -574,6 +761,7 @@ "user.changeLanguage": "Schimbă limba", "user.changeName": "Redenumește acest utilizator", "user.changePassword": "Schimbă parola", + "user.changePassword.current": "Your current password", "user.changePassword.new": "Parola nouă", "user.changePassword.new.confirm": "Confirmă parola nouă...", "user.changeRole": "Schimbă rolul", @@ -585,11 +773,14 @@ "users": "Utilizatori", "version": "Versiune", + "version.changes": "Changed version", + "version.compare": "Compare versions", "version.current": "Versiunea curentă", "version.latest": "Ultima versiune", "versionInformation": "Informații despre versiune", - "view.account": "Contul tău", + "view": "View", + "view.account": "Contul t\u0103u", "view.installation": "Instalare", "view.languages": "Limbi", "view.resetPassword": "Restabilește parola", diff --git a/kirby/i18n/translations/ru.json b/kirby/i18n/translations/ru.json index 4500b0d..dc10016 100644 --- a/kirby/i18n/translations/ru.json +++ b/kirby/i18n/translations/ru.json @@ -3,64 +3,82 @@ "account.delete": "Удалить пользователя", "account.delete.confirm": "Вы действительно хотите удалить свой аккаунт? Вы сразу покинете панель управления, а аккаунт нельзя будет восстановить.", - "add": "Добавить", + "activate": "Активировать", + "add": "\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c", + "alpha": "Альфа", "author": "Автор", - "avatar": "Аватар", + "avatar": "\u0410\u0432\u0430\u0442\u0430\u0440 (\u0444\u043e\u0442\u043e)", "back": "Назад", - "cancel": "Отменить", - "change": "Изменить", - "close": "Закрыть", + "cancel": "\u041e\u0442\u043c\u0435\u043d\u0438\u0442\u044c", + "change": "\u0418\u0437\u043c\u0435\u043d\u0438\u0442\u044c", + "close": "\u0417\u0430\u043a\u0440\u044b\u0442\u044c", + "changes": "Изменения", "confirm": "Ок", "collapse": "Свернуть", "collapse.all": "Свернуть все", - "copy": "(копия)", + "color": "Цвет", + "coordinates": "Координаты", + "copy": "Скопировать", "copy.all": "Копировать все", + "copy.success": "{count} скопировано", + "copy.success.multiple": "{count} скопировано", + "copy.url": "Скопировать ссылку", "create": "Создать", + "custom": "Другое", "date": "Дата", "date.select": "Выберите дату", "day": "День", - "days.fri": "Пт", - "days.mon": "Пн", - "days.sat": "Сб", - "days.sun": "Сб", - "days.thu": "Чт", - "days.tue": "Вт", - "days.wed": "Ср", + "days.fri": "\u041f\u0442", + "days.mon": "\u041f\u043d", + "days.sat": "\u0421\u0431", + "days.sun": "\u0412\u0441", + "days.thu": "\u0427\u0442", + "days.tue": "\u0412\u0442", + "days.wed": "\u0421\u0440", "debugging": "Отладка", - "delete": "Удалить", + "delete": "\u0423\u0434\u0430\u043b\u0438\u0442\u044c", "delete.all": "Удалить все", + "dialog.fields.empty": "Для этого окна нет полей", "dialog.files.empty": "Нет файлов для выбора", "dialog.pages.empty": "Нет страниц для выбора", + "dialog.text.empty": "Окно не содержит никакого текста", "dialog.users.empty": "Нет пользователей для выбора", "dimensions": "Размеры", + "disable": "Отключить", "disabled": "Отключено", - "discard": "Сброс", + "discard": "\u0421\u0431\u0440\u043e\u0441", + + "drawer.fields.empty": "Нет полей", + + "domain": "Домен", "download": "Скачать", "duplicate": "Дублировать", - "edit": "Редактировать", + "edit": "\u041d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c", "email": "Email", "email.placeholder": "mail@example.com", + "enter": "Введите", "entries": "Записи", "entry": "Запись", "environment": "Среда", + "error": "Ошибка", "error.access.code": "Неверный код", - "error.access.login": "Неправильный логин", + "error.access.login": "Неверный логин или пароль", "error.access.panel": "У вас нет права доступа к панели", "error.access.view": "У вас нет прав доступа к этой части панели", "error.avatar.create.fail": "Не удалось загрузить фотографию профиля", - "error.avatar.delete.fail": "Аватар (фото) к аккаунту не может быть удален", + "error.avatar.delete.fail": "\u0410\u0432\u0430\u0442\u0430\u0440 (\u0444\u043e\u0442\u043e) \u043a \u0430\u043a\u043a\u0430\u0443\u043d\u0442\u0443 \u043d\u0435 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0443\u0434\u0430\u043b\u0435\u043d", "error.avatar.dimensions.invalid": "Пожалуйста, сделайте чтобы ширина или высота фотографии была меньше 3000 пикселей", "error.avatar.mime.forbidden": "Фотография профиля должна быть JPEG или PNG", @@ -74,13 +92,31 @@ "error.cache.type.invalid": "Неверный тип кэша: \"{type}\"", + "error.content.lock.delete": "Версия заблокирована и не может быть удалена", + "error.content.lock.move": "Исходная версия заблокирована и не может быть перемещена", + "error.content.lock.publish": "Эта версия уже опубликована", + "error.content.lock.replace": "Версия заблокирована и не может быть заменена", + "error.content.lock.update": "Версия заблокирована и не может быть обновлена", + + "error.entries.max.plural": "Вы не должны добавлять более {max} записей", + "error.entries.max.singular": "Вы должны добавить не более одной записи", + "error.entries.min.plural": "Вы должны добавить не менее {min} записей", + "error.entries.min.singular": "Вы должны добавить хотя бы одну запись", + "error.entries.supports": "\"{type}\" тип поля не поддерживается для поля записи", + "error.entries.validation": "Ошибка в поле \"{field}\" в строке {index}", + "error.email.preset.notFound": "Email-шаблон \"{name}\" не найден", "error.field.converter.invalid": "Неверный конвертер \"{converter}\"", + "error.field.link.options": "Недопустимые параметры: {options}", "error.field.type.missing": "Поле \"{ name }\": тип поля \"{ type }\" не существует", "error.file.changeName.empty": "Название не может быть пустым", "error.file.changeName.permission": "У вас нет права изменить название \"{filename}\"", + "error.file.changeTemplate.invalid": "Шаблон для файла \"{id}\" не может быть изменен на \"{template}\" (допускается: \"{blueprints}\")", + "error.file.changeTemplate.permission": "У вас нет права изменять шаблон для файла \"{id}\"", + + "error.file.delete.multiple": "Не все файлы удалось удалить. Попробуйте удалить каждый оставшийся файл по отдельности, чтобы увидеть конкретную ошибку, которая мешает удалению.", "error.file.duplicate": "Файл с названием \"{filename}\" уже есть", "error.file.extension.forbidden": "Расширение файла \"{extension}\" неразрешено", "error.file.extension.invalid": "Неверное разрешение: {extension}", @@ -95,33 +131,43 @@ "error.file.minheight": "Высота файла должна быть хотя бы {height} px", "error.file.minsize": "Файл слишком маленький", "error.file.minwidth": "Ширина файла должна быть хотя бы {width} px", + "error.file.name.unique": "Название файла должно быть уникальным", "error.file.name.missing": "Название файла не может быть пустым", - "error.file.notFound": "Файл не найден", + "error.file.notFound": "\u0424\u0430\u0439\u043b \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d", "error.file.orientation": "Ориентация изображения должна быть \"{orientation}\"", + "error.file.sort.permission": "Вам не разрешается изменять сортировку \"{filename}\"", "error.file.type.forbidden": "У вас нет права загружать файлы {type}", "error.file.type.invalid": "Неверный тип файла: {type}", - "error.file.undefined": "Файл не найден", + "error.file.undefined": "\u0424\u0430\u0439\u043b \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d", "error.form.incomplete": "Пожалуйста, исправьте все ошибки в форме", "error.form.notSaved": "Форма не может быть сохранена", "error.language.code": "Пожалуйста, впишите правильный код языка", + "error.language.create.permission": "You are not allowed to create a language", + "error.language.delete.permission": "You are not allowed to delete the language", "error.language.duplicate": "Язык уже есть", "error.language.name": "Пожалуйста, впишите правильное название языка", "error.language.notFound": "Не получилось найти этот язык", + "error.language.update.permission": "You are not allowed to update the language", "error.layout.validation.block": "Ошибка в поле \"{field}\" в блоке {blockIndex} типа \"{fieldset}\" внутри разметки {layoutIndex}", "error.layout.validation.settings": "Ошибка в настройках макета {index}", - "error.license.format": "Пожалуйста, введите правильный лицензионный код", + "error.license.domain": "Лицензия на этот домен отсутствует", "error.license.email": "Пожалуйста, введите правильный Email", + "error.license.format": "Пожалуйста, введите правильный лицензионный код", "error.license.verification": "Лицензия не подтверждена", + "error.login.totp.confirm.invalid": "Неверный код", + "error.login.totp.confirm.missing": "Пожалуйста, введите текущий код", + "error.object.validation": "Ошибка в поле \"{label}\":\n{message}", "error.offline": "Панель управления не в сети", - "error.page.changeSlug.permission": "Вы не можете изменить URL этой страницы", + "error.page.changeSlug.permission": "\u0412\u044b \u043d\u0435 \u043c\u043e\u0436\u0435\u0442\u0435 \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u044c URL \u044d\u0442\u043e\u0439 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b", + "error.page.changeSlug.reserved": "Путь к страницам верхнего уровня не должен начинаться с \"{path}\"", "error.page.changeStatus.incomplete": "На странице есть ошибки и поэтому ее нельзя опубликовать", "error.page.changeStatus.permission": "Невозможно изменить статус для этой страницы", "error.page.changeStatus.toDraft.invalid": "Невозможно конвертировать в черновик страницу \"{slug}\"", @@ -133,17 +179,25 @@ "error.page.delete": "Невозможно удалить страницу \"{slug}\"", "error.page.delete.confirm": "Впишите название страницы чтобы подтвердить", "error.page.delete.hasChildren": "У страницы есть внутренние страницы, поэтому ее невозможно удалить", + "error.page.delete.multiple": "Не все страницы удалось удалить. Попробуйте удалить каждую оставшуюся страницу по отдельности, чтобы увидеть конкретную ошибку, которая мешает удалению.", "error.page.delete.permission": "У вас нет права удалить \"{slug}\"", "error.page.draft.duplicate": "Черновик страницы с URL \"{slug}\" уже есть", "error.page.duplicate": "Страница с URL \"{slug}\" уже есть", "error.page.duplicate.permission": "У вас нет права дублировать \"{slug}\"", - "error.page.notFound": "Страница не найдена", + "error.page.move.ancestor": "Невозможно переместить страницу саму в себя", + "error.page.move.directory": "Невозможно перенести каталог страницы", + "error.page.move.duplicate": "Подстраница с URL \"{slug}\" уже существует", + "error.page.move.noSections": "Страница \"{parent}\" не может быть родительской для какой-либо страницы, поскольку в ее разметке отсутствуют какие-либо разделы страниц", + "error.page.move.notFound": "Перемещенная страница не найдена", + "error.page.move.permission": "У вас нет права переместить \"{slug}\"", + "error.page.move.template": "Шаблон \"{template}\" не разрешен для подстраниц \"{parent}\"", + "error.page.notFound": "\u0421\u0442\u0440\u0430\u043d\u0438\u0446\u0430 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u0430", "error.page.num.invalid": "Пожалуйста, впишите правильное число сортировки. Число не может быть отрицательным.", "error.page.slug.invalid": "Пожалуйста, введите правильный URL", "error.page.slug.maxlength": "Длина ссылки должна быть короче \"{length}\" символов", "error.page.sort.permission": "Невозможно сортировать страницу \"{slug}\"", "error.page.status.invalid": "Пожалуйста, установите верный статус страницы", - "error.page.undefined": "Страница не найдена", + "error.page.undefined": "\u0421\u0442\u0440\u0430\u043d\u0438\u0446\u0430 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u0430", "error.page.update.permission": "У вас нет права обновить \"{slug}\"", "error.section.files.max.plural": "Нельзя добавить больше чем {max} файлов в секции \"{section}\"", @@ -163,6 +217,8 @@ "error.site.changeTitle.permission": "У вас нет права изменять название сайта", "error.site.update.permission": "У вас нет права обновить сайт", + "error.structure.validation": "Ошибка в поле \"{field}\" в строке {index}", + "error.template.default.notFound": "Нет шаблона по умолчанию", "error.unexpected": "Произошла непредвиденная ошибка! Включите режим отладки для получения дополнительной информации: https://getkirby.com/docs/reference/system/options/debug", @@ -175,8 +231,8 @@ "error.user.changeRole.permission": "У вас нет права изменять роль пользователя \"{name}\"", "error.user.changeRole.toAdmin": "У вас нет прав предоставить роль администратора", "error.user.create.permission": "У вас нет права создать этого пользователя", - "error.user.delete": "Аккаунт не может быть удален", - "error.user.delete.lastAdmin": "Вы не можете удалить единственного администратора", + "error.user.delete": "\u0410\u043a\u043a\u0430\u0443\u043d\u0442 \u043d\u0435 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0443\u0434\u0430\u043b\u0435\u043d", + "error.user.delete.lastAdmin": "\u0412\u044b \u043d\u0435 \u043c\u043e\u0436\u0435\u0442\u0435 \u0443\u0434\u0430\u043b\u0438\u0442\u044c \u0435\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0433\u043e \u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440\u0430", "error.user.delete.lastUser": "Нельзя удалить единственного пользователя", "error.user.delete.permission": "У вас нет права удалить пользователя \"{name}\"", "error.user.duplicate": "Пользователь с Email \"{email}\" уже есть", @@ -185,7 +241,7 @@ "error.user.notFound": "Пользователь \"{name}\" не найден", "error.user.password.excessive": "Пожалуйста, введите верный пароль. Длина паролей не должна превышать 1000 символов.", "error.user.password.invalid": "Пожалуйста, введите правильный пароль. Он должен состоять минимум из 8 символов.", - "error.user.password.notSame": "Пожалуйста, подтвердите пароль", + "error.user.password.notSame": "\u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u0435 \u043f\u0430\u0440\u043e\u043b\u044c", "error.user.password.undefined": "У пользователя нет пароля", "error.user.password.wrong": "Неверный пароль", "error.user.role.invalid": "Введите правильную роль", @@ -195,8 +251,10 @@ "error.validation.accepted": "Пожалуйста, подтвердите", "error.validation.alpha": "Пожалуйста, введите только буквы a-z", "error.validation.alphanum": "Пожалуйста, введите только буквы a-z или числа 0-9", + "error.validation.anchor": "Пожалуйста, введите правильную ссылку на якорь", "error.validation.between": "Пожалуйста, введите значение от \"{min}\" до \"{max}\"", "error.validation.boolean": "Пожалуйста, подтвердите или отмените", + "error.validation.color": "Пожалуйста, введите верное значение цвета в формате {format}", "error.validation.contains": "Пожалуйста, впишите значение, которое содержит \"{needle}\"", "error.validation.date": "Пожалуйста, укажите правильную дату", "error.validation.date.after": "Пожалуйста, укажите дату после {date}", @@ -211,6 +269,7 @@ "error.validation.integer": "Пожалуйста, введите правильное целое число", "error.validation.ip": "Пожалуйста, введите правильный IP адрес", "error.validation.less": "Пожалуйста, введите значение меньше чем {max}", + "error.validation.linkType": "Тип ссылки не допускается", "error.validation.match": "Значение не соответствует ожидаемому шаблону", "error.validation.max": "Пожалуйста, введите значение равное или больше чем {max}", "error.validation.maxlength": "Пожалуйста, введите значение короче (макс. {max} символов)", @@ -227,15 +286,18 @@ "error.validation.same": "Пожалуйста, введите \"{other}\"", "error.validation.size": "Значение размера должно быть \"{size}\"", "error.validation.startswith": "Значение должно начинаться с \"{start}\"", + "error.validation.tel": "Пожалуйста, введите неформатированный номер телефона", "error.validation.time": "Пожалуйста, введите правильную дату", "error.validation.time.after": "Пожалуйста, укажите время после {time}", "error.validation.time.before": "Пожалуйста, укажите время до {time}", "error.validation.time.between": "Пожалуйста, укажите время между {min} и {max}", + "error.validation.uuid": "Пожалуйста, введите правильный UUID", "error.validation.url": "Пожалуйста, введите правильный URL", "expand": "Развернуть", "expand.all": "Развернуть все", + "field.invalid": "Неверное поле", "field.required": "Поле обязательно", "field.blocks.changeType": "Изменить тип", "field.blocks.code.name": "Код", @@ -245,8 +307,9 @@ "field.blocks.delete.confirm.all": "Вы действительно хотите удалить все блоки?", "field.blocks.delete.confirm.selected": "Вы действительно хотите удалить эти блоки?", "field.blocks.empty": "Блоков нет", + "field.blocks.fieldsets.empty": "Пока нет наборов полей", "field.blocks.fieldsets.label": "Пожалуйста, выберите тип блока…", - "field.blocks.fieldsets.paste": "Нажмите {{ shortcut }} чтобы вставить/импортировать блоки из буфера памяти", + "field.blocks.fieldsets.paste": "Нажмите {{ shortcut }} чтобы импортировать макеты/блоки из буфера обмена Будут вставлены только те, которые разрешены в текущем поле.", "field.blocks.gallery.name": "Галерея", "field.blocks.gallery.images.empty": "Изображений нет", "field.blocks.gallery.images.label": "Изображения", @@ -254,11 +317,16 @@ "field.blocks.heading.name": "Заголовок", "field.blocks.heading.text": "Текст", "field.blocks.heading.placeholder": "Заголовок …", + "field.blocks.figure.back.plain": "Неформатированный", + "field.blocks.figure.back.pattern.light": "Паттерн (светлый)", + "field.blocks.figure.back.pattern.dark": "Паттерн (темный)", "field.blocks.image.alt": "Альтернативный текст", "field.blocks.image.caption": "Подпись", "field.blocks.image.crop": "Обрезать", "field.blocks.image.link": "Ссылка", "field.blocks.image.location": "Расположение", + "field.blocks.image.location.internal": "Этот сайт", + "field.blocks.image.location.external": "Внешний источник", "field.blocks.image.name": "Картинка", "field.blocks.image.placeholder": "Выберите изображение", "field.blocks.image.ratio": "Соотношение", @@ -275,41 +343,75 @@ "field.blocks.quote.citation.placeholder": "Автор …", "field.blocks.text.name": "Текст", "field.blocks.text.placeholder": "Текст …", + "field.blocks.video.autoplay": "Автовоспроизведение", "field.blocks.video.caption": "Подпись", + "field.blocks.video.controls": "Элементы управления", + "field.blocks.video.location": "Расположение", + "field.blocks.video.loop": "Зациклить", + "field.blocks.video.muted": "Без звука", "field.blocks.video.name": "Видео", "field.blocks.video.placeholder": "Введите ссылку на видео", + "field.blocks.video.poster": "Обложка", + "field.blocks.video.preload": "Предзагрузка", "field.blocks.video.url.label": "Ссылка на видео", "field.blocks.video.url.placeholder": "https://youtube.com/?v=", - "field.files.empty": "Файлы не выбраны", + "field.entries.delete.confirm.all": "Вы действительно хотите удалить все значения?", + "field.entries.empty": "Записей нет", + "field.files.empty": "Файлы не выбраны", + "field.files.empty.single": "Файл не выбран", + + "field.layout.change": "Изменить разметку", "field.layout.delete": "Удалить разметку", "field.layout.delete.confirm": "Вы действительно хотите удалить эту разметку?", + "field.layout.delete.confirm.all": "Вы действительно хотите удалить всю разметку?", "field.layout.empty": "Строк нет", "field.layout.select": "Выберите разметку", "field.object.empty": "Пока нет информации", "field.pages.empty": "Страницы не выбраны", + "field.pages.empty.single": "Страница не выбрана", "field.structure.delete.confirm": "Вы точно хотите удалить эту запись?", "field.structure.delete.confirm.all": "Вы действительно хотите удалить все значения?", "field.structure.empty": "Записей нет", "field.users.empty": "Пользователей нет", + "field.users.empty.single": "Пользователь не выбран", + "fields.empty": "Ещё нет полей", + + "file": "Файл", "file.blueprint": "У файла пока нет разметки. Вы можете определить новые секции и поля разметки в /site/blueprints/files/{blueprint}.yml", + "file.changeTemplate": "Изменить шаблон", + "file.changeTemplate.notice": "Изменение шаблона файла приведет к удалению содержимого полей, которые не совпадут по типу. Если у нового шаблона есть определенные условия, например размер изображения, они также будут применены. Используйте с осторожностью.", "file.delete.confirm": "Вы точно хотите удалить файл
{filename}?", + "file.focus.placeholder": "Установить фокусную точку", + "file.focus.reset": "Удалить фокусную точку", + "file.focus.title": "Фокусная точка", "file.sort": "Изменить позицию", "files": "Файлы", - "files.empty": "Ещё нет файлов", + "files.delete.confirm.selected": "Вы действительно хотите удалить выбранные файлы? Это действие невозможно отменить.", + "files.empty": "Еще нет файлов", + + "filter": "Фильтр", + + "form.discard": " Отменить изменения", + "form.discard.confirm": " Вы действительно хотите отменить все свои изменения?", + "form.locked": "Этот контент для вас недоступен, так как в сейчас он редактируется другим пользователем", + "form.unsaved": "Текущие изменения не сохранены", + "form.preview": "Предпросмотр", + "form.preview.draft": "Посмотреть черновик", "hide": "Скрыть", "hour": "Час", + "hue": "Оттенок", "import": "Импортировать", "info": "Информация", - "insert": "Вставить", + "insert": "\u0412\u0441\u0442\u0430\u0432\u0438\u0442\u044c", "insert.after": "Вставить ниже", "insert.before": "Вставить выше", "install": "Установить", @@ -324,14 +426,14 @@ "installation.issues.mbstring": "Расширение MB String необходимо", "installation.issues.media": "Каталог /media не существует или нет прав записи", "installation.issues.php": "Убедитесь, что используется PHP 8+", - "installation.issues.server": "Kirby требует Apache, Nginx или Caddy ", "installation.issues.sessions": "Каталог /site/sessions не существует или нет прав записи", - "language": "Язык", + "language": "\u042f\u0437\u044b\u043a", "language.code": "Код", "language.convert": "Установить по умолчанию", "language.convert.confirm": "

Вы точно хотите конвертировать {name} в главный язык? Это нельзя будет отменить.

Если {name} имеет непереведенный контент, то больше не будет верного каскада и части вашего сайта могут быть пустыми.

", "language.create": "Добавить новый язык", + "language.default": "Главный язык", "language.delete.confirm": "Вы точно хотите удалить {name} язык, включая все переводы? Это нельзя будет вернуть.", "language.deleted": "Язык удален", "language.direction": "Направление чтения", @@ -340,7 +442,16 @@ "language.locale": "PHP locale string", "language.locale.warning": "Вы используете кастомную локаль. Пожалуйста измените ее в файле языка в /site/languages", "language.name": "Название", + "language.secondary": "Второстепенный язык", + "language.settings": "Настройки языка", "language.updated": "Язык обновлен", + "language.variables": "Языковые переменные", + "language.variables.empty": "Пока нет переводов", + + "language.variable.delete.confirm": "Вы действительно хотите удалить переменную для {key}?", + "language.variable.key": "Ключ", + "language.variable.notFound": "Переменная не найдена", + "language.variable.value": "Значение", "languages": "Языки", "languages.default": "Главный язык", @@ -349,35 +460,56 @@ "languages.secondary.empty": "Дополнительных языков нет", "license": "Лицензия", + "license.activate": "Активировать сейчас", + "license.activate.label": "Пожалуйста, активируйте Вашу лицензию", + "license.activate.domain": "Ваша лицензия будет активирована на {host}.", + "license.activate.local": "Вы собираетесь активировать лицензию на локальный домен {host}. Если этот сайт будет размещен на общедоступном домене, то, пожалуйста, укажите его вместо {host}.", + "license.activated": "Активировано", "license.buy": "Купить лицензию", - "license.register": "Зарегистрировать", + "license.code": "Код", + "license.code.help": "Вставьте код лицензии, который вы получили Email после покупки.", + "license.code.label": "Пожалуйста вставьте код лицензии", + "license.status.active.info": "Включает обновления до {date}", + "license.status.active.label": "Действительная лицензия", + "license.status.demo.info": "Это демонстрационная установка", + "license.status.demo.label": "Демо", + "license.status.inactive.info": "Обновите лицензию для перехода на новые версии", + "license.status.inactive.label": "Нет новых обновлений", + "license.status.legacy.bubble": "Вы готовы обновить вашу лицензию?", + "license.status.legacy.info": "Ваша лицензия не покрывает эту версию", + "license.status.legacy.label": "Пожалуйста, обновите вашу лицензию", + "license.status.missing.bubble": "Готовы запустить Ваш сайт?", + "license.status.missing.info": "Нет действительной лицензии", + "license.status.missing.label": "Пожалуйста, активируйте Вашу лицензию", + "license.status.unknown.info": "Статус лицензии неизвестен", + "license.status.unknown.label": "Неизвестно", "license.manage": "Управление лицензиями", - "license.register.help": "После покупки вы получили на Email код лицензии. Вставьте его сюда, чтобы зарегистрировать копию.", - "license.register.label": "Пожалуйста вставьте код лицензии", - "license.register.domain": "Ваша лицензия будет зарегистрирована на {host}.", - "license.register.local": "Вы собираетесь зарегистрировать лицензию на локальный домен {host}. Если этот сайт будет размещен на общедоступном домене, то, пожалуйста, укажите его вместо {host}.", - "license.register.success": "Спасибо за поддержку Kirby", - "license.unregistered": "Это незарегистрированная версия Kirby", + "license.purchased": "Приобретено", + "license.success": "Спасибо за поддержку Kirby", "license.unregistered.label": "Не зарегистрировано", - "link": "Ссылка", - "link.text": "Текст ссылки", + "link": "\u0421\u0441\u044b\u043b\u043a\u0430", + "link.text": "\u0422\u0435\u043a\u0441\u0442 \u0441\u0441\u044b\u043b\u043a\u0438", "loading": "Загрузка", "lock.unsaved": "Несохраненные изменения", "lock.unsaved.empty": "Несохраненных изменений нет", - "lock.isLocked": "Несохраненные изменения пользователя {email}", - "lock.file.isLocked": "В данный момент этот файл редактирует {email}, поэтому его нельзя изменить.", - "lock.page.isLocked": "В данный момент эту страницу редактирует {email}, поэтому его нельзя изменить.", + "lock.unsaved.files": "Несохраненные файлы", + "lock.unsaved.pages": "Несохраненные страницы", + "lock.unsaved.users": "Несохраненные аккаунты", + "lock.isLocked": "Несохраненные изменения {email}", "lock.unlock": "Разблокировать", - "lock.isUnlocked": "Ваши несохраненные изменения были перезаписаны другим пользователем. Вы можете загрузить ваши изменения и объединить их вручную.", + "lock.unlock.submit": "Разблокируйте и перезапишите несохраненные изменения {email}", + "lock.isUnlocked": "Были перезаписаны другим пользователем", "login": "Войти", "login.code.label.login": "Код для входа", "login.code.label.password-reset": "Код для сброса пароля", "login.code.placeholder.email": "000 000", + "login.code.placeholder.totp": "000000", "login.code.text.email": "Если ваш Email уже зарегистрирован, запрашиваемый код был отправлен на него.", + "login.code.text.totp": "Пожалуйста, введите одноразовый пароль из вашего приложения-аутентификатора.", "login.email.login.body": "{code} — код для входа на сайт {site}. Код действителен {timeout} минут.\n\nЗдравствуйте, {user.nameOrEmail}!\n\nЕсли вы не запрашивали код для входа, проигнорируйте это письмо или обратитесь к администратору, если у вас есть вопросы.\nВ целях безопасности НЕ ПЕРЕСЫЛАЙТЕ это письмо.", "login.email.login.subject": "Ваш код для входа", "login.email.password-reset.body": "{code} — код для сброса пароля на сайт «{site}». Код действителен {timeout} минут.\n\nЗдравствуйте, {user.nameOrEmail}!\n\nЕсли вы не запрашивали сброс пароля, проигнорируйте это письмо или обратитесь к администратору, если у вас есть вопросы.\nВ целях безопасности НЕ ПЕРЕСЫЛАЙТЕ это письмо.", @@ -388,44 +520,64 @@ "login.toggleText.code.email-password": "Вход с паролем", "login.toggleText.password-reset.email": "Забыли ваш пароль?", "login.toggleText.password-reset.email-password": "← Вернуться к форме входа", + "login.totp.enable.option": "Настроить одноразовые пароли", + "login.totp.enable.intro": "Приложения‑аутентификаторы могут генерировать одноразовые коды, которые используются в качестве второго фактора при входе в вашу учетную запись.", + "login.totp.enable.qr.label": "1. Отсканируйте этот QR-код", + "login.totp.enable.qr.help": "Не удается выполнить сканирование? Добавьте ключ настройки {secret} вручную в ваше приложение для проверки подлинности.", + "login.totp.enable.confirm.headline": "2. Подтвердите с помощью генерированного кода", + "login.totp.enable.confirm.text": "Ваше приложение генерирует новый одноразовый код каждые 30 секунд. Введите текущий код для завершения настройки:", + "login.totp.enable.confirm.label": "Текущий код", + "login.totp.enable.confirm.help": "После этой настройки мы будем запрашивать у вас одноразовый код при каждом входе.", + "login.totp.enable.success": "Одноразовые коды включены", + "login.totp.disable.option": "Отключить одноразовые коды", + "login.totp.disable.label": "Введите ваш пароль для отключения одноразовых паролей", + "login.totp.disable.help": "Теперь при входе в систему будет запрашиваться второй фактор, например, код для входа, отправленный по Email. Вы всегда можете повторно настроить одноразовые коды позже.", + "login.totp.disable.admin": "

Вы отключаете одноразовые коды для{user}.

Теперь при входе в систему будет запрашиваться другой второй фактор, например код для входа, отправленный по Email. {user} может повторно настроить одноразовые коды после следующего входа в систему.

", + "login.totp.disable.success": "Одноразовые коды выключены", "logout": "Выйти", + "merge": "Объединить", "menu": "Меню", "meridiem": "До полудня / После полудня", "mime": "Тип медиа", "minutes": "Минуты", "month": "Месяц", - "months.april": "Апрель", - "months.august": "Август", - "months.december": "Декабрь", + "months.april": "\u0410\u043f\u0440\u0435\u043b\u044c", + "months.august": "\u0410\u0432\u0433\u0443\u0441\u0442", + "months.december": "\u0414\u0435\u043a\u0430\u0431\u0440\u044c", "months.february": "Февраль", - "months.january": "Январь", - "months.july": "Июль", - "months.june": "Июнь", - "months.march": "Март", - "months.may": "Май", - "months.november": "Ноябрь", - "months.october": "Октябрь", - "months.september": "Сентябрь", + "months.january": "\u042f\u043d\u0432\u0430\u0440\u044c", + "months.july": "\u0418\u044e\u043b\u044c", + "months.june": "\u0418\u044e\u043d\u044c", + "months.march": "\u041c\u0430\u0440\u0442", + "months.may": "\u041c\u0430\u0439", + "months.november": "\u041d\u043e\u044f\u0431\u0440\u044c", + "months.october": "\u041e\u043a\u0442\u044f\u0431\u0440\u044c", + "months.september": "\u0421\u0435\u043d\u0442\u044f\u0431\u0440\u044c", "more": "Еще", + "move": "Переместить", "name": "Название", "next": "Дальше", + "night": "Ночь", "no": "нет", "off": "выключено", "on": "включено", "open": "Открыть", "open.newWindow": "Открывать в новом окне", - "options": "Опции", + "option": "Опция", + "options": "Параметры", "options.none": "Параметров нет", + "options.all": "Показать все параметры ({count})", "orientation": "Ориентация", "orientation.landscape": "Горизонтальная", "orientation.portrait": "Портретная", "orientation.square": "Квадрат", + "page": "Страница", "page.blueprint": "У страницы пока нет разметки. Вы можете определить новые секции и поля разметки в /site/blueprints/pages/{blueprint}.yml", "page.changeSlug": "Изменить ссылку", "page.changeSlug.fromTitle": "Создать из названия", @@ -433,13 +585,15 @@ "page.changeStatus.position": "Пожалуйста, выберите позицию", "page.changeStatus.select": "Выбрать новый статус", "page.changeTemplate": "Изменить шаблон", + "page.changeTemplate.notice": "Изменение шаблона страницы приведет к удалению содержимого полей, которые не совпадут по типу. Используйте с осторожностью.", + "page.create": "Создать как {status}", "page.delete.confirm": "Вы точно хотите удалить страницу {title}?", "page.delete.confirm.subpages": "У этой страницы есть внутренние страницы.
Все внутренние страницы так же будут удалены.", "page.delete.confirm.title": "Напишите название страницы, чтобы подтвердить", - "page.draft.create": "Создать черновик", "page.duplicate.appendix": "(копия)", "page.duplicate.files": "Копировать файлы", "page.duplicate.pages": "Копировать страницы", + "page.move": "Переместить", "page.sort": "Изменить позицию", "page.status": "Статус", "page.status.draft": "Черновик", @@ -450,29 +604,37 @@ "page.status.unlisted.description": "Страница доступна только по URL", "pages": "Страницы", + "pages.delete.confirm.selected": "Вы действительно хотите удалить выбранные страницы? Это действие невозможно отменить.", "pages.empty": "Страниц нет", "pages.status.draft": "Черновики", "pages.status.listed": "Опубликовано", - "pages.status.unlisted": "Скрыта", + "pages.status.unlisted": "Скрытая", "pagination.page": "Страница", - "password": "Пароль", + "password": "\u041f\u0430\u0440\u043e\u043b\u044c", "paste": "Вставить", "paste.after": "Вставить после", + "paste.success": "{count} вставлено", "pixel": "Пиксель", "plugin": "Расширение", "plugins": "Плагины", "prev": "Предыдущий", "preview": "Предпросмотр", + + "publish": "Опубликовать", + "published": "Опубликовано", + "remove": "Удалить", "rename": "Переименовать", - "replace": "Заменить", - "retry": "Повторить", - "revert": "Сброс", + "renew": "Обновить", + "replace": "\u0417\u0430\u043c\u0435\u043d\u0438\u0442\u044c", + "replace.with": "Заменить на", + "retry": "\u041f\u043e\u0432\u0442\u043e\u0440\u0438\u0442\u044c", + "revert": "\u0421\u0431\u0440\u043e\u0441", "revert.confirm": "Вы действительно хотите удалить все несохраненные изменения?", - "role": "Роль", + "role": "\u0420\u043e\u043b\u044c", "role.admin.description": "Администратор имеет все права", "role.admin.title": "Администратор", "role.all": "Все", @@ -481,12 +643,15 @@ "role.nobody.description": "Эта роль применяется если у пользователя нет никаких прав", "role.nobody.title": "Никто", - "save": "Сохранить", + "save": "\u0421\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c", + "saved": "Сохранено", "search": "Поиск", + "searching": "Поиск", "search.min": "Введите хотя бы {min} символов для поиска", - "search.all": "Показать все", + "search.all": "Показать все результаты ({count})", "search.results.none": "Нет результатов", + "section.invalid": "Неверная секция", "section.required": "Секция обязательна", "security": "Безопасность", @@ -498,16 +663,25 @@ "size": "Размер", "slug": "URL", "sort": "Сортировать", + "sort.drag": "Потяните для сортировки…", + "split": "Разделить", "stats.empty": "Статистики нет", + "status": "Статус", + + "system.info.copy": "Copy info", + "system.info.copied": "System info copied", "system.issues.content": "Похоже, к папке content есть несанкционированный доступ", "system.issues.eol.kirby": "Срок службы установленной вами версии Kirby истек, и она больше не будет получать обновления для системы безопасности", "system.issues.eol.plugin": "Срок службы установленной вами версии плагина { plugin } истек, и он не будет получать дальнейших обновлений для системы безопасности", + "system.issues.eol.php": "Ваша версия PHP { release } устарела и не будет получать дальнейших обновлений для системы безопасности", "system.issues.debug": "Включен режим отладки (debugging). Используйте его только при разработке.", "system.issues.git": "Похоже, к папке .git есть несанкционированный доступ", "system.issues.https": "Рекомендуется использовать HTTPS на всех сайтах", "system.issues.kirby": "Похоже, к папке kirby есть несанкционированный доступ", + "system.issues.local": "Сайт работает локально, с ослабленными проверками безопасности", "system.issues.site": "Похоже, к папке site есть несанкционированный доступ", + "system.issues.vue.compiler": "Включен компилятор шаблонов Vue", "system.issues.vulnerability.kirby": "Обнаружена уязвимость уровня \"{ severity }\": { description }", "system.issues.vulnerability.plugin": "В плагине { plugin } обнаружена уязвимость уровня \"{ severity }\": { description }", "system.updateStatus": "Обновить статус", @@ -520,12 +694,21 @@ "system.updateStatus.update": "Доступно бесплатное обновление { version }", "system.updateStatus.upgrade": "Доступно обновление { version }", + "tel": "Телефон", + "tel.placeholder": "+79123456789", + "template": "\u0428\u0430\u0431\u043b\u043e\u043d", + + "theme": "Тема", + "theme.light": "Светлая тема", + "theme.dark": "Темная тема", + "theme.automatic": "Как в системе", + "title": "Название", - "template": "Шаблон", "today": "Сегодня", + "toolbar.button.clear": "Очистить форматирование", "toolbar.button.code": "Код", - "toolbar.button.bold": "Жирный шрифт", + "toolbar.button.bold": "\u0416\u0438\u0440\u043d\u044b\u0439 \u0448\u0440\u0438\u0444\u0442", "toolbar.button.email": "Email", "toolbar.button.headings": "Заголовки", "toolbar.button.heading.1": "Заголовок 1", @@ -538,9 +721,11 @@ "toolbar.button.file": "Файл", "toolbar.button.file.select": "Выбрать файл", "toolbar.button.file.upload": "Загрузить файл", - "toolbar.button.link": "Ссылка", + "toolbar.button.link": "\u0421\u0441\u044b\u043b\u043a\u0430", "toolbar.button.paragraph": "Параграф", "toolbar.button.strike": "Зачёркнутый", + "toolbar.button.sub": "Нижний индекс", + "toolbar.button.sup": "Верхний индекс", "toolbar.button.ol": "Нумерованный список", "toolbar.button.underline": "Подчёркнутый", "toolbar.button.ul": "Маркированный список", @@ -550,6 +735,8 @@ "translation.name": "Русский (Russian)", "translation.locale": "ru_RU", + "type": "Введите", + "upload": "Загрузить", "upload.error.cantMove": "Не удалось переместить загруженный файл", "upload.error.cantWrite": "Не получилось записать файл на диск", @@ -574,28 +761,32 @@ "user.changeLanguage": "Изменить язык", "user.changeName": "Переименовать пользователя", "user.changePassword": "Изменить пароль", + "user.changePassword.current": "Ваш текущий пароль", "user.changePassword.new": "Новый пароль", "user.changePassword.new.confirm": "Подтвердить новый пароль…", "user.changeRole": "Изменить роль", "user.changeRole.select": "Выбрать новую роль", - "user.create": "Добавить пользователя", + "user.create": "Добавить нового пользователя", "user.delete": "Удалить этого пользователя", "user.delete.confirm": "Вы действительно хотите аккаунт
{email}?", "users": "Пользователи", "version": "Версия", + "version.changes": "Измененная версия", + "version.compare": "Сравнить версии", "version.current": "Текущая версия", "version.latest": "Последняя версия", "versionInformation": "Информация о версии", + "view": "Посмотреть", "view.account": "Ваш аккаунт", - "view.installation": "Установка", + "view.installation": "\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430", "view.languages": "Языки", "view.resetPassword": "Сбросить пароль", "view.site": "Сайт", "view.system": "Система", - "view.users": "Пользователи", + "view.users": "\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438", "welcome": "Добро пожаловать", "year": "Год", diff --git a/kirby/i18n/translations/sk.json b/kirby/i18n/translations/sk.json index d3b3145..5b341aa 100644 --- a/kirby/i18n/translations/sk.json +++ b/kirby/i18n/translations/sk.json @@ -3,57 +3,75 @@ "account.delete": "Zmazať váš účet", "account.delete.confirm": "Do you really want to delete your account? You will be logged out immediately. Your account cannot be recovered.", + "activate": "Activate", "add": "Pridať", + "alpha": "Alpha", "author": "Autor", "avatar": "Profilový obrázok", - "back": "Back", - "cancel": "Cancel", + "back": "Späť", + "cancel": "Zrušiť", "change": "Zmeniť", "close": "Zavrieť", + "changes": "Zmeny", "confirm": "Ok", "collapse": "Zabaliť", "collapse.all": "Zabaliť všetky", + "color": "Farba", + "coordinates": "Koordináty", "copy": "Kopírovať", "copy.all": "Copy all", + "copy.success": "{count} copied!", + "copy.success.multiple": "{count} copied!", + "copy.url": "Copy URL", "create": "Vytvoriť", + "custom": "Custom", "date": "Dátum", "date.select": "Zvoliť dátum", "day": "Deň", "days.fri": "Pia", - "days.mon": "Mon", + "days.mon": "Pon", "days.sat": "Sob", "days.sun": "Ned", "days.thu": "Štv", "days.tue": "Uto", - "days.wed": "Wed", + "days.wed": "Str", "debugging": "Debugging", "delete": "Zmazať", "delete.all": "Zmazať všetky", + "dialog.fields.empty": "This dialog has no fields", "dialog.files.empty": "No files to select", "dialog.pages.empty": "No pages to select", + "dialog.text.empty": "This dialog does not define any text", "dialog.users.empty": "Zvolení neboli žiadni uživátelia", "dimensions": "Rozmery", + "disable": "Disable", "disabled": "Disabled", - "discard": "Discard", + "discard": "Zahodiť", + + "drawer.fields.empty": "This drawer has no fields", + + "domain": "Domain", "download": "Stiahnuť", "duplicate": "Duplikovať", - "edit": "Edit", + "edit": "Upraviť", "email": "E-mail", "email.placeholder": "mail@example.com", + "enter": "Enter", "entries": "Entries", "entry": "Entry", "environment": "Environment", + "error": "Chyba", "error.access.code": "Neplatný kód", "error.access.login": "Neplatné prihlásenie", "error.access.panel": "Nemáte povolenie na prístup do Panel-u", @@ -61,7 +79,7 @@ "error.avatar.create.fail": "Profilový obrázok sa nepodarilo nahrať", "error.avatar.delete.fail": "Profilový obrázok sa nepodarilo zmazať", - "error.avatar.dimensions.invalid": "Prosím, dodržte, aby šírka a výška profilového obrázka bola menšia ako 3000 pixelov.", + "error.avatar.dimensions.invalid": "Prosím, dodržte, aby šírka a výška profilového obrázka bola menšia ako 3000 pixelov", "error.avatar.mime.forbidden": "Profilový obrázok musí byť súbor JPEG alebo PNG.", "error.blueprint.notFound": "Blueprint \"{name}\" sa nepodarilo načítať", @@ -74,13 +92,31 @@ "error.cache.type.invalid": "Invalid cache type \"{type}\"", + "error.content.lock.delete": "The version is locked and cannot be deleted", + "error.content.lock.move": "The source version is locked and cannot be moved", + "error.content.lock.publish": "This version is already published", + "error.content.lock.replace": "The version is locked and cannot be replaced", + "error.content.lock.update": "The version is locked and cannot be updated", + + "error.entries.max.plural": "You must not add more than {max} entries", + "error.entries.max.singular": "You must not add more than one entry", + "error.entries.min.plural": "You must add at least {min} entries", + "error.entries.min.singular": "You must add at least one entry", + "error.entries.supports": "\"{type}\" field type is not supported for the entries field", + "error.entries.validation": "There's an error on the \"{field}\" field in row {index}", + "error.email.preset.notFound": "E-mailovú predvoľbu \"{name}\" nie je možné nájsť", "error.field.converter.invalid": "Neplatný converter \"{converter}\"", + "error.field.link.options": "Invalid options: {options}", "error.field.type.missing": "Field \"{ name }\": The field type \"{ type }\" does not exist", "error.file.changeName.empty": "Meno nesmie byť prázdne", "error.file.changeName.permission": "Nemáte povolenie na zmenu názvu pre \"{filename}\"", + "error.file.changeTemplate.invalid": "The template for the file \"{id}\" cannot be changed to \"{template}\" (valid: \"{blueprints}\")", + "error.file.changeTemplate.permission": "You are not allowed to change the template for the file \"{id}\"", + + "error.file.delete.multiple": "Not all files could be deleted. Try each remaining file individually to see the specific error that prevents deletion.", "error.file.duplicate": "Súbor s názvom \"{filename}\" už existuje", "error.file.extension.forbidden": "Prípona \"{extension}\" nie je povolená", "error.file.extension.invalid": "Neplatná prípona: \"{extension}\"", @@ -95,9 +131,11 @@ "error.file.minheight": "Výška obrázku musí byť aspoň \"{height}\" pixelov", "error.file.minsize": "Súbor je príliš malý", "error.file.minwidth": "Šírka obrázku musí byť aspoň \"{width}\" pixelov", + "error.file.name.unique": "The filename must be unique", "error.file.name.missing": "Názov súboru nemôže byť prázdny", "error.file.notFound": "Súbor \"{filename}\" sa nepodarilo nájsť", "error.file.orientation": "The orientation of the image must be \"{orientation}\"", + "error.file.sort.permission": "You are not allowed to change the sorting of \"{filename}\"", "error.file.type.forbidden": "Nemáte povolenie na nahrávanie súborov s typom {type}", "error.file.type.invalid": "Neplatný typ súboru: \"{type}\"", "error.file.undefined": "Súbor nie je možné nájsť", @@ -106,22 +144,30 @@ "error.form.notSaved": "Formulár sa nepodarilo uložiť", "error.language.code": "Please enter a valid code for the language", + "error.language.create.permission": "You are not allowed to create a language", + "error.language.delete.permission": "You are not allowed to delete the language", "error.language.duplicate": "The language already exists", "error.language.name": "Please enter a valid name for the language", "error.language.notFound": "The language could not be found", + "error.language.update.permission": "You are not allowed to update the language", "error.layout.validation.block": "There's an error on the \"{field}\" field in block {blockIndex} using the \"{fieldset}\" block type in layout {layoutIndex}", "error.layout.validation.settings": "There's an error in layout {index} settings", - "error.license.format": "Please enter a valid license key", + "error.license.domain": "The domain for the license is missing", "error.license.email": "Prosím, zadajte platnú e-mailovú adresu", + "error.license.format": "Please enter a valid license code", "error.license.verification": "The license could not be verified", + "error.login.totp.confirm.invalid": "Neplatný kód", + "error.login.totp.confirm.missing": "Please enter the current code", + "error.object.validation": "There’s an error in the \"{label}\" field:\n{message}", "error.offline": "The Panel is currently offline", "error.page.changeSlug.permission": "Nemáte povolenie na zmenu URL príponu pre \"{slug}\"", + "error.page.changeSlug.reserved": "The path of top-level pages must not start with \"{path}\"", "error.page.changeStatus.incomplete": "Stránka obsahuje chyby a nemôže byť zverejnená", "error.page.changeStatus.permission": "Status tejto stránky nemôže byť zmenený", "error.page.changeStatus.toDraft.invalid": "Stránka \"{slug}\" nemôže byť zmenená na koncept.", @@ -133,10 +179,18 @@ "error.page.delete": "Stránku \"{slug}\" nie je možné vymazať", "error.page.delete.confirm": "Prosím, zadajte titulok stránky pre potvrdenie", "error.page.delete.hasChildren": "Táto stránka obsahuje podstránky a nemôže byť zmazaná", + "error.page.delete.multiple": "Not all pages could be deleted. Try each remaining page individually to see the specific error that prevents deletion.", "error.page.delete.permission": "Nemáte povolenie na zmazanie stránky \"{slug}\"", "error.page.draft.duplicate": "Koncept stránky s URL appendix-om \"{slug}\" už existuje", "error.page.duplicate": "Stránka s URL appendix-om \"{slug}\" už existuje", "error.page.duplicate.permission": "You are not allowed to duplicate \"{slug}\"", + "error.page.move.ancestor": "The page cannot be moved into itself", + "error.page.move.directory": "The page directory cannot be moved", + "error.page.move.duplicate": "A sub page with the URL appendix \"{slug}\" already exists", + "error.page.move.noSections": "The page \"{parent}\" cannot be a parent of any page because it lacks any pages sections in its blueprint", + "error.page.move.notFound": "The moved page could not be found", + "error.page.move.permission": "You are not allowed to move \"{slug}\"", + "error.page.move.template": "The \"{template}\" template is not accepted as a subpage of \"{parent}\"", "error.page.notFound": "Stránku \"{slug}\" nie je možné nájsť", "error.page.num.invalid": "Prosím, zadajte platné číslo pre radenie. Čísla nemôžu byť záporné.", "error.page.slug.invalid": "Please enter a valid URL appendix", @@ -163,6 +217,8 @@ "error.site.changeTitle.permission": "Nemáte povolenie na zmenu titulku pre portál", "error.site.update.permission": "Nemáte povolenie na aktualizovanie portálu", + "error.structure.validation": "There's an error on the \"{field}\" field in row {index}", + "error.template.default.notFound": "Predvolená šablóna neexistuje", "error.unexpected": "An unexpected error occurred! Enable debug mode for more info: https://getkirby.com/docs/reference/system/options/debug", @@ -195,8 +251,10 @@ "error.validation.accepted": "Prosím, potvrďte", "error.validation.alpha": "Prosím, zadajte len znaky z hlások a-z", "error.validation.alphanum": "Prosím, zadajte len znaky z hlások a-z a čísloviek 0-9", + "error.validation.anchor": "Please enter a correct link anchor", "error.validation.between": "Prosím, zadajte hodnotu od \"{min}\" do \"{max}\"", "error.validation.boolean": "Prosím, potvrďte alebo odmietnite", + "error.validation.color": "Please enter a valid color in the {format} format", "error.validation.contains": "Prosím, zadajte hodnotu, ktorá obsahuje \"{needle}\"", "error.validation.date": "Prosím, zadajte platný dátum", "error.validation.date.after": "Please enter a date after {date}", @@ -211,6 +269,7 @@ "error.validation.integer": "Prosím, zadajte platné celé číslo", "error.validation.ip": "Prosím, zadajte platnú e-mailovú adresu", "error.validation.less": "Prosím, zadajte hodnotu menšiu ako {max}", + "error.validation.linkType": "The link type is not allowed", "error.validation.match": "Hodnota nezodpovedá očakávanému vzoru", "error.validation.max": "Prosím, zadajte hodnotu rovnú alebo menšiu ako {max}", "error.validation.maxlength": "Prosím, zadajte kratšiu hodnotu. (max. {max} charaktery/ov)", @@ -227,15 +286,18 @@ "error.validation.same": "Prosím, zadajte \"{other}\"", "error.validation.size": "Veľkosť hodnoty musí byť \"{size}\"", "error.validation.startswith": "Hodnota musí začínať s \"{start}\"", + "error.validation.tel": "Please enter an unformatted phone number", "error.validation.time": "Prosím, zadajte platný čas", "error.validation.time.after": "Please enter a time after {time}", "error.validation.time.before": "Please enter a time before {time}", "error.validation.time.between": "Please enter a time between {min} and {max}", + "error.validation.uuid": "Please enter a valid UUID", "error.validation.url": "Prosím, zadajte platnú URL", "expand": "Rozbaliť", "expand.all": "Rozbaliť všetky", + "field.invalid": "The field is invalid", "field.required": "The field is required", "field.blocks.changeType": "Change type", "field.blocks.code.name": "Kód", @@ -245,8 +307,9 @@ "field.blocks.delete.confirm.all": "Naozaj chcete zmazať všetky bloky?", "field.blocks.delete.confirm.selected": "Naozaj chcete zmazať vybrané bloky?", "field.blocks.empty": "No blocks yet", + "field.blocks.fieldsets.empty": "No fieldsets yet", "field.blocks.fieldsets.label": "Please select a block type …", - "field.blocks.fieldsets.paste": "Press {{ shortcut }} to paste/import blocks from your clipboard", + "field.blocks.fieldsets.paste": "Press {{ shortcut }} to import layouts/blocks from your clipboard Only those allowed in the current field will get inserted.", "field.blocks.gallery.name": "Galéria", "field.blocks.gallery.images.empty": "No images yet", "field.blocks.gallery.images.label": "Obrázky", @@ -254,11 +317,16 @@ "field.blocks.heading.name": "Nadpis", "field.blocks.heading.text": "Text", "field.blocks.heading.placeholder": "Nadpis ...", + "field.blocks.figure.back.plain": "Plain", + "field.blocks.figure.back.pattern.light": "Pattern (light)", + "field.blocks.figure.back.pattern.dark": "Pattern (dark)", "field.blocks.image.alt": "Alternative text", "field.blocks.image.caption": "Popis", "field.blocks.image.crop": "Orezanie", "field.blocks.image.link": "Odkaz", "field.blocks.image.location": "Poloha", + "field.blocks.image.location.internal": "This website", + "field.blocks.image.location.external": "External source", "field.blocks.image.name": "Obrázok", "field.blocks.image.placeholder": "Select an image", "field.blocks.image.ratio": "Ratio", @@ -275,38 +343,72 @@ "field.blocks.quote.citation.placeholder": "by …", "field.blocks.text.name": "Text", "field.blocks.text.placeholder": "Text …", + "field.blocks.video.autoplay": "Autoplay", "field.blocks.video.caption": "Popis", + "field.blocks.video.controls": "Controls", + "field.blocks.video.location": "Poloha", + "field.blocks.video.loop": "Loop", + "field.blocks.video.muted": "Muted", "field.blocks.video.name": "Video", "field.blocks.video.placeholder": "Enter a video URL", + "field.blocks.video.poster": "Poster", + "field.blocks.video.preload": "Preload", "field.blocks.video.url.label": "Video-URL", "field.blocks.video.url.placeholder": "https://youtube.com/?v=", - "field.files.empty": "Žiadne súbory zatiaľ neboli zvolené", + "field.entries.delete.confirm.all": "Do you really want to delete all entries?", + "field.entries.empty": "Zatiaľ žiadne údaje", + "field.files.empty": "Žiadne súbory zatiaľ neboli zvolené", + "field.files.empty.single": "No file selected yet", + + "field.layout.change": "Change layout", "field.layout.delete": "Delete layout", "field.layout.delete.confirm": "Do you really want to delete this layout?", + "field.layout.delete.confirm.all": "Do you really want to delete all layouts?", "field.layout.empty": "No rows yet", "field.layout.select": "Select a layout", "field.object.empty": "No information yet", "field.pages.empty": "Žiadne stránky zatiaľ neboli zvolené", + "field.pages.empty.single": "No page selected yet", "field.structure.delete.confirm": "Ste si istý, že chcete zmazať tento riadok?", "field.structure.delete.confirm.all": "Do you really want to delete all entries?", "field.structure.empty": "Zatiaľ žiadne údaje", "field.users.empty": "Žiadni užívatelia zatiaľ neboli zvolení", + "field.users.empty.single": "No user selected yet", + "fields.empty": "No fields yet", + + "file": "Súbor", "file.blueprint": "This file has no blueprint yet. You can define the setup in /site/blueprints/files/{blueprint}.yml", + "file.changeTemplate": "Zmeniť šablónu", + "file.changeTemplate.notice": "Changing the file's template will remove content for fields that don't match in type. If the new template defines certain rules, e.g. image dimensions, those will also be applied irreversibly. Use with caution.", "file.delete.confirm": "Ste si istý, že chcete zmazať
{filename}?", + "file.focus.placeholder": "Set focal point", + "file.focus.reset": "Remove focal point", + "file.focus.title": "Focus", "file.sort": "Change position", - "files": "Files", + "files": "Súbory", + "files.delete.confirm.selected": "Do you really want to delete the selected files? This action cannot be undone.", "files.empty": "Zatiaľ žiadne súbory", + "filter": "Filter", + + "form.discard": "Discard changes", + "form.discard.confirm": "Do you really want to discard all your changes?", + "form.locked": "This content is disabled for you as it is currently edited by another user", + "form.unsaved": "The current changes have not yet been saved", + "form.preview": "Preview changes", + "form.preview.draft": "Preview draft", + "hide": "Hide", "hour": "Hodina", + "hue": "Hue", "import": "Import", "info": "Info", "insert": "Vložiť", @@ -324,7 +426,6 @@ "installation.issues.mbstring": "MB String rozšírenie je povinné", "installation.issues.media": "Priečinok /media neexistuje alebo nie je nastavený ako zapisovateľný", "installation.issues.php": "Uistite sa, že používate PHP 8+", - "installation.issues.server": "Kirby vyžaduje Apache, Nginx alebo Caddy", "installation.issues.sessions": "Priečinok /site/sessions neexistuje alebo nie je nastavený ako zapisovateľný", "language": "Jazyk", @@ -332,6 +433,7 @@ "language.convert": "Nastaviť ako predvolené", "language.convert.confirm": "

Ste si istý, že chcete nastaviť {name} ako predvolený jazyk? Túto akciu nie je možné zvrátiť.

Ak {name} obsahuje nepreložený obsah, tak pre tento obsah nebude fungovať platné volanie a niektoré časti vašich stránok zostanú prázdne.

", "language.create": "Pridať nový jazyk", + "language.default": "Predvolený jazyk", "language.delete.confirm": "Ste si istý, že chcete zmazať jazyk {name} vrátane všetkých prekladov? Túto akciu nie je možné zvrátiť.", "language.deleted": "Jazyk bol zmazaný", "language.direction": "Smer čítania", @@ -340,7 +442,16 @@ "language.locale": "PHP locale string", "language.locale.warning": "You are using a custom locale set up. Please modify it in the language file in /site/languages", "language.name": "Názov", + "language.secondary": "Secondary language", + "language.settings": "Language settings", "language.updated": "Jazyk bol aktualizovaný", + "language.variables": "Language variables", + "language.variables.empty": "No translations yet", + + "language.variable.delete.confirm": "Do you really want to delete the variable for {key}?", + "language.variable.key": "Key", + "language.variable.notFound": "The variable could not be found", + "language.variable.value": "Value", "languages": "Jazyky", "languages.default": "Predvolený jazyk", @@ -349,15 +460,32 @@ "languages.secondary.empty": "Zatiaľ žiadne sekundárne jazyky", "license": "Licencia", + "license.activate": "Activate it now", + "license.activate.label": "Please activate your license", + "license.activate.domain": "Your license will be activated for {host}.", + "license.activate.local": "You are about to activate your Kirby license for your local domain {host}. If this site will be deployed to a public domain, please activate it there instead. If {host} is the domain you want to use your license for, please continue.", + "license.activated": "Activated", "license.buy": "Zakúpiť licenciu", - "license.register": "Registrovať", + "license.code": "Kód", + "license.code.help": "You received your license code after the purchase via email. Please copy and paste it here.", + "license.code.label": "Prosím, zadajte váš licenčný kód", + "license.status.active.info": "Includes new major versions until {date}", + "license.status.active.label": "Valid license", + "license.status.demo.info": "This is a demo installation", + "license.status.demo.label": "Demo", + "license.status.inactive.info": "Renew license to update to new major versions", + "license.status.inactive.label": "No new major versions", + "license.status.legacy.bubble": "Ready to renew your license?", + "license.status.legacy.info": "Your license does not cover this version", + "license.status.legacy.label": "Please renew your license", + "license.status.missing.bubble": "Ready to launch your site?", + "license.status.missing.info": "No valid license", + "license.status.missing.label": "Please activate your license", + "license.status.unknown.info": "The license status is unknown", + "license.status.unknown.label": "Unknown", "license.manage": "Manage your licenses", - "license.register.help": "Licenčný kód vám bol doručený e-mailom po úspešnom nákupe. Prosím, skopírujte a prilepte ho na uskutočnenie registrácie.", - "license.register.label": "Prosím, zadajte váš licenčný kód", - "license.register.domain": "Your license will be registered to {host}.", - "license.register.local": "You are about to register your license for your local domain {host}. If this site will be deployed to a public domain, please register it there instead. If {host} is the domain you want to license Kirby to, please continue.", - "license.register.success": "Ďakujeme za vašu podporu Kirby", - "license.unregistered": "Toto je neregistrované demo Kirby", + "license.purchased": "Purchased", + "license.success": "Ďakujeme za vašu podporu Kirby", "license.unregistered.label": "Unregistered", "link": "Odkaz", @@ -367,17 +495,21 @@ "lock.unsaved": "Unsaved changes", "lock.unsaved.empty": "There are no more unsaved changes", - "lock.isLocked": "Unsaved changes by {email}", - "lock.file.isLocked": "The file is currently being edited by {email} and cannot be changed.", - "lock.page.isLocked": "The page is currently being edited by {email} and cannot be changed.", + "lock.unsaved.files": "Unsaved files", + "lock.unsaved.pages": "Unsaved pages", + "lock.unsaved.users": "Unsaved accounts", + "lock.isLocked": "Unsaved changes by {email}", "lock.unlock": "Unlock", - "lock.isUnlocked": "Your unsaved changes have been overwritten by another user. You can download your changes to merge them manually.", + "lock.unlock.submit": "Unlock and overwrite unsaved changes by {email}", + "lock.isUnlocked": "Was unlocked by another user", - "login": "Log in", + "login": "Prihlásenie", "login.code.label.login": "Login code", "login.code.label.password-reset": "Password reset code", "login.code.placeholder.email": "000 000", + "login.code.placeholder.totp": "000000", "login.code.text.email": "If your email address is registered, the requested code was sent via email.", + "login.code.text.totp": "Please enter the one‑time code from your authenticator app.", "login.email.login.body": "Hi {user.nameOrEmail},\n\nYou recently requested a login code for the Panel of {site}.\nThe following login code will be valid for {timeout} minutes:\n\n{code}\n\nIf you did not request a login code, please ignore this email or contact your administrator if you have questions.\nFor security, please DO NOT forward this email.", "login.email.login.subject": "Your login code", "login.email.password-reset.body": "Hi {user.nameOrEmail},\n\nYou recently requested a password reset code for the Panel of {site}.\nThe following password reset code will be valid for {timeout} minutes:\n\n{code}\n\nIf you did not request a password reset code, please ignore this email or contact your administrator if you have questions.\nFor security, please DO NOT forward this email.", @@ -388,9 +520,24 @@ "login.toggleText.code.email-password": "Login with password", "login.toggleText.password-reset.email": "Forgot your password?", "login.toggleText.password-reset.email-password": "← Back to login", + "login.totp.enable.option": "Set up one‑time codes", + "login.totp.enable.intro": "Authenticator apps can generate one‑time codes that are used as a second factor when signing into your account.", + "login.totp.enable.qr.label": "1. Scan this QR code", + "login.totp.enable.qr.help": "Unable to scan? Add the setup key {secret} manually to your authenticator app.", + "login.totp.enable.confirm.headline": "2. Confirm with generated code", + "login.totp.enable.confirm.text": "Your app generates a new one‑time code every 30 seconds. Enter the current code to complete the setup:", + "login.totp.enable.confirm.label": "Current code", + "login.totp.enable.confirm.help": "After this setup, we will ask you for a one‑time code every time you log in.", + "login.totp.enable.success": "One‑time codes enabled", + "login.totp.disable.option": "Disable one‑time codes", + "login.totp.disable.label": "Enter your password to disable one‑time codes", + "login.totp.disable.help": "In the future, a different second factor like a login code sent via email will be requested when you log in. You can always set up one‑time codes again later.", + "login.totp.disable.admin": "

This will disable one‑time codes for {user}.

In the future, a different second factor like a login code sent via email will be requested when they log in. {user} can set up one‑time codes again after their next login.

", + "login.totp.disable.success": "One‑time codes disabled", - "logout": "Log out", + "logout": "Odhlásenie", + "merge": "Merge", "menu": "Menu", "meridiem": "AM/PM", "mime": "Typ média", @@ -400,32 +547,37 @@ "months.april": "Apríl", "months.august": "August", "months.december": "December", - "months.february": "February", + "months.february": "Február", "months.january": "Január", - "months.july": "July", - "months.june": "June", - "months.march": "March", - "months.may": "May", + "months.july": "Júl", + "months.june": "Jún", + "months.march": "Marec", + "months.may": "Máj", "months.november": "November", - "months.october": "October", + "months.october": "Október", "months.september": "September", "more": "Viac", - "name": "Názov", + "move": "Move", + "name": "Meno", "next": "Ďalej", + "night": "Night", "no": "no", "off": "off", "on": "on", "open": "Otvoriť", "open.newWindow": "Open in new window", + "option": "Option", "options": "Nastavenia", "options.none": "No options", + "options.all": "Show all {count} options", "orientation": "Orientácia", "orientation.landscape": "Širokouhlá", "orientation.portrait": "Portrét", "orientation.square": "Štvorec", + "page": "Stránka", "page.blueprint": "This page has no blueprint yet. You can define the setup in /site/blueprints/pages/{blueprint}.yml", "page.changeSlug": "Zmeniť URL", "page.changeSlug.fromTitle": "Vytvoriť z titulku", @@ -433,13 +585,15 @@ "page.changeStatus.position": "Prosím, zmeňte pozíciu", "page.changeStatus.select": "Zvoľte nový status", "page.changeTemplate": "Zmeniť šablónu", + "page.changeTemplate.notice": "Changing the page's template will remove content for fields that don't match in type. Use with caution.", + "page.create": "Create as {status}", "page.delete.confirm": "Ste si istý, že chcete zmazať {title}?", "page.delete.confirm.subpages": "Táto stránka obsahuje podstránky.
Všetky podstránky budú taktiež zmazané.", "page.delete.confirm.title": "Pre potvrdenie zadajte titulok stránky", - "page.draft.create": "Vytvoriť koncept", "page.duplicate.appendix": "Kopírovať", "page.duplicate.files": "Copy files", "page.duplicate.pages": "Copy pages", + "page.move": "Move page", "page.sort": "Change position", "page.status": "Status", "page.status.draft": "Koncept", @@ -449,7 +603,8 @@ "page.status.unlisted": "Skryté", "page.status.unlisted.description": "Stránka je prístupná len prostredníctvom priamej URL", - "pages": "Pages", + "pages": "Stránky", + "pages.delete.confirm.selected": "Do you really want to delete the selected pages? This action cannot be undone.", "pages.empty": "Zatiaľ žiadne stránky", "pages.status.draft": "Koncepty", "pages.status.listed": "Zverejnené", @@ -457,17 +612,24 @@ "pagination.page": "Stránka", - "password": "Password", + "password": "Heslo", "paste": "Paste", "paste.after": "Paste after", + "paste.success": "{count} pasted!", "pixel": "Pixel", "plugin": "Plugin", "plugins": "Plugins", "prev": "Predchádzajúci", "preview": "Preview", + + "publish": "Publish", + "published": "Zverejnené", + "remove": "Odstrániť", "rename": "Premenovať", - "replace": "Replace", + "renew": "Renew", + "replace": "Nahradiť", + "replace.with": "Replace with", "retry": "Skúsiť ešte raz", "revert": "Vrátiť späť", "revert.confirm": "Do you really want to delete all unsaved changes?", @@ -482,11 +644,14 @@ "role.nobody.title": "Nobody", "save": "Uložiť", + "saved": "Saved", "search": "Hľadať", + "searching": "Searching", "search.min": "Enter {min} characters to search", - "search.all": "Show all", + "search.all": "Show all {count} results", "search.results.none": "No results", + "section.invalid": "The section is invalid", "section.required": "The section is required", "security": "Security", @@ -498,16 +663,25 @@ "size": "Veľkosť", "slug": "URL appendix", "sort": "Zoradiť", + "sort.drag": "Drag to sort …", + "split": "Split", "stats.empty": "No reports", + "status": "Status", + + "system.info.copy": "Copy info", + "system.info.copied": "System info copied", "system.issues.content": "The content folder seems to be exposed", "system.issues.eol.kirby": "Your installed Kirby version has reached end-of-life and will not receive further security updates", "system.issues.eol.plugin": "Your installed version of the { plugin } plugin is has reached end-of-life and will not receive further security updates", + "system.issues.eol.php": "Your installed PHP release { release } has reached end-of-life and will not receive further security updates", "system.issues.debug": "Debugging must be turned off in production", "system.issues.git": "The .git folder seems to be exposed", "system.issues.https": "We recommend HTTPS for all your sites", "system.issues.kirby": "The kirby folder seems to be exposed", + "system.issues.local": "The site is running locally with relaxed security checks", "system.issues.site": "The site folder seems to be exposed", + "system.issues.vue.compiler": "The Vue template compiler is enabled", "system.issues.vulnerability.kirby": "Your installation might be affected by the following vulnerability ({ severity } severity): { description }", "system.issues.vulnerability.plugin": "Your installation might be affected by the following vulnerability in the { plugin } plugin ({ severity } severity): { description }", "system.updateStatus": "Update status", @@ -520,10 +694,19 @@ "system.updateStatus.update": "Free update { version } available", "system.updateStatus.upgrade": "Upgrade { version } available", - "title": "Titulok", + "tel": "Phone", + "tel.placeholder": "+49123456789", "template": "Šablóna", + + "theme": "Theme", + "theme.light": "Lights on", + "theme.dark": "Lights off", + "theme.automatic": "Match system default", + + "title": "Titulok", "today": "Dnes", + "toolbar.button.clear": "Clear formatting", "toolbar.button.code": "Kód", "toolbar.button.bold": "Tučný", "toolbar.button.email": "E-mail", @@ -541,6 +724,8 @@ "toolbar.button.link": "Odkaz", "toolbar.button.paragraph": "Paragraph", "toolbar.button.strike": "Strike-through", + "toolbar.button.sub": "Subscript", + "toolbar.button.sup": "Superscript", "toolbar.button.ol": "Číslovaný zoznam", "toolbar.button.underline": "Underline", "toolbar.button.ul": "Odrážkový zoznam", @@ -550,6 +735,8 @@ "translation.name": "Slovensky", "translation.locale": "sk_SK", + "type": "Type", + "upload": "Nahrať", "upload.error.cantMove": "The uploaded file could not be moved", "upload.error.cantWrite": "Failed to write file to disk", @@ -574,6 +761,7 @@ "user.changeLanguage": "Zmeniť jazyk", "user.changeName": "Premenovať tohto užívateľa", "user.changePassword": "Zmeniť heslo", + "user.changePassword.current": "Your current password", "user.changePassword.new": "Nové heslo", "user.changePassword.new.confirm": "Potvrdiť nové heslo...", "user.changeRole": "Zmeniť rolu", @@ -585,10 +773,13 @@ "users": "Užívatelia", "version": "Verzia", + "version.changes": "Changed version", + "version.compare": "Compare versions", "version.current": "Current version", "version.latest": "Latest version", "versionInformation": "Version information", + "view": "View", "view.account": "Váš účet", "view.installation": "Inštalácia", "view.languages": "Jazyky", diff --git a/kirby/i18n/translations/sv_SE.json b/kirby/i18n/translations/sv_SE.json index be0fcd7..91b0971 100644 --- a/kirby/i18n/translations/sv_SE.json +++ b/kirby/i18n/translations/sv_SE.json @@ -3,57 +3,75 @@ "account.delete": "Radera ditt konto", "account.delete.confirm": "Vill du verkligen radera ditt konto? Du kommer att loggas ut omedelbart. Ditt konto kan inte återställas.", - "add": "Lägg till", + "activate": "Aktivera", + "add": "L\u00e4gg till", + "alpha": "Alpha", "author": "Författare", "avatar": "Profilbild", "back": "Tillbaka", "cancel": "Avbryt", - "change": "Ändra", - "close": "Stäng", - "confirm": "Ok", + "change": "\u00c4ndra", + "close": "St\u00e4ng", + "changes": "Ändringar", + "confirm": "Spara", "collapse": "Kollapsa", "collapse.all": "Kollapsa alla", + "color": "Färg", + "coordinates": "Koordinater", "copy": "Kopiera", "copy.all": "Kopiera alla", + "copy.success": "{count} kopierad!", + "copy.success.multiple": "{count} kopierad!", + "copy.url": "Kopiera URL", "create": "Skapa", + "custom": "Anpassad", "date": "Datum", "date.select": "Välj ett datum", "day": "Dag", - "days.fri": "fre", - "days.mon": "mån", - "days.sat": "lör", - "days.sun": "sön", - "days.thu": "tors", - "days.tue": "tis", - "days.wed": "ons", + "days.fri": "Fre", + "days.mon": "M\u00e5n", + "days.sat": "L\u00f6r", + "days.sun": "S\u00f6n", + "days.thu": "Tor", + "days.tue": "Tis", + "days.wed": "Ons", "debugging": "Felsökning", "delete": "Radera", "delete.all": "Radera allt", + "dialog.fields.empty": "Den här dialogrutan har inga fält", "dialog.files.empty": "Inga filer att välja", "dialog.pages.empty": "Inga sidor att välja", + "dialog.text.empty": "Den här dialogrutan definierar ingen text", "dialog.users.empty": "Inga användare att välja", "dimensions": "Dimensioner", + "disable": "Inaktivera", "disabled": "Inaktiverad", "discard": "Kassera", + + "drawer.fields.empty": "Denna vy har inga fält", + + "domain": "Domän", "download": "Ladda ner", "duplicate": "Duplicera", "edit": "Redigera", "email": "E-postadress", - "email.placeholder": "mail@example.com", + "email.placeholder": "namn@exempel.se", + "enter": "Enter", "entries": "Poster", "entry": "Post", "environment": "Miljö", + "error": "Fel", "error.access.code": "Ogiltig kod", "error.access.login": "Ogiltig inloggning", "error.access.panel": "Du saknar behörighet att nå panelen", @@ -74,13 +92,31 @@ "error.cache.type.invalid": "Ogiltig cachetyp \"{type}\"", + "error.content.lock.delete": "Versionen är låst och kan inte raderas", + "error.content.lock.move": "Källversionen är låst och kan inte flyttas", + "error.content.lock.publish": "Denna version är redan publicerad", + "error.content.lock.replace": "Versionen är låst och kan inte bytas ut", + "error.content.lock.update": "Versionen är låst och kan inte uppdateras", + + "error.entries.max.plural": "You must not add more than {max} entries", + "error.entries.max.singular": "You must not add more than one entry", + "error.entries.min.plural": "You must add at least {min} entries", + "error.entries.min.singular": "You must add at least one entry", + "error.entries.supports": "\"{type}\" field type is not supported for the entries field", + "error.entries.validation": "Det finns ett fel i fältet \"{field}\" i rad {index}", + "error.email.preset.notFound": "E-postförinställningen \"{name}\" kan inte hittas", "error.field.converter.invalid": "Ogiltig omvandlare \"{converter}\"", + "error.field.link.options": "Invalid options: {options}", "error.field.type.missing": "Fältet \"{ name }\": Fälttypen \"{ type }\" finns inte", "error.file.changeName.empty": "Namnet får inte vara tomt", "error.file.changeName.permission": "Du har inte behörighet att ändra namnet på \"{filename}\"", + "error.file.changeTemplate.invalid": "Mallen för filen \"{id}\" kan inte ändras till \"{template}\" (giltiga mallar: \"{blueprints}\")", + "error.file.changeTemplate.permission": "Du saknar behörighet för att ändra mallen för filen \"{id}\"", + + "error.file.delete.multiple": "Not all files could be deleted. Try each remaining file individually to see the specific error that prevents deletion.", "error.file.duplicate": "En fil med namnet \"{filename}\" existerar redan", "error.file.extension.forbidden": "Filändelsen \"{extension}\" är inte tillåten", "error.file.extension.invalid": "Ogiltig filändelse: {extension}", @@ -95,9 +131,11 @@ "error.file.minheight": "Bildens höjd måste vara minst {height} pixlar", "error.file.minsize": "Filen är för liten", "error.file.minwidth": "Bildens bredd måste vara minst {width} pixlar", + "error.file.name.unique": "Filnamnet måste vara unikt", "error.file.name.missing": "Filnamnet får inte vara tomt", "error.file.notFound": "Filen \"{filename}\" kan ej hittas", "error.file.orientation": "Bildens orientering måste vara \"{orientation}\"", + "error.file.sort.permission": "Du har inte behörighet att ändra sorteringen av \"{filename}\"", "error.file.type.forbidden": "Du har inte behörighet att ladda upp filer av typen {type}", "error.file.type.invalid": "Ogiltig filtyp: {type}", "error.file.undefined": "Filen kan inte hittas", @@ -106,22 +144,30 @@ "error.form.notSaved": "Formuläret kunde inte sparas", "error.language.code": "Ange en giltig kod för språket", + "error.language.create.permission": "Du saknar behörighet att skapa ett nytt språk", + "error.language.delete.permission": "Du saknar behörighet att radera språket", "error.language.duplicate": "Språket finns redan", "error.language.name": "Ange ett giltigt namn för språket", "error.language.notFound": "Språket hittades inte", + "error.language.update.permission": "Du saknar behörighet att redigera språket", "error.layout.validation.block": "Det finns ett fel i fältet \"{field}\" i blocket {blockIndex} med blocktypen \"{fieldset}\" i layouten {layoutIndex}", "error.layout.validation.settings": "Det finns ett fel i inställningarna för layout {index}", - "error.license.format": "Ange en giltig licensnyckel", + "error.license.domain": "Domänen för licensen saknas", "error.license.email": "Ange en giltig e-postadress", + "error.license.format": "Ange en giltig licenskod", "error.license.verification": "Licensen kunde inte verifieras", + "error.login.totp.confirm.invalid": "Ogiltig kod", + "error.login.totp.confirm.missing": "Vänligen ange den aktuella koden", + "error.object.validation": "Det finns ett fel i fältet \"{label}\":\n{message}", "error.offline": "Panelen är för närvarande offline", "error.page.changeSlug.permission": "Du har inte behörighet att ändra URL-appendixen för \"{slug}\"", + "error.page.changeSlug.reserved": "Sökvägen till sidor på toppnivå får inte börja med \"{path}\"", "error.page.changeStatus.incomplete": "Sidan innehåller fel och kan inte publiceras", "error.page.changeStatus.permission": "Statusen för denna sida kan inte ändras", "error.page.changeStatus.toDraft.invalid": "Statusen för sidan \"{slug}\" kan inte ändras till utkast", @@ -133,10 +179,18 @@ "error.page.delete": "Sidan \"{slug}\" kan inte raderas", "error.page.delete.confirm": "Fyll i sidans titel för att bekräfta", "error.page.delete.hasChildren": "Sidan har undersidor och kan inte raderas", + "error.page.delete.multiple": "Not all pages could be deleted. Try each remaining page individually to see the specific error that prevents deletion.", "error.page.delete.permission": "Du har inte behörighet att radera \"{slug}\"", "error.page.draft.duplicate": "Ett utkast med URL-appendixen \"{slug}\" existerar redan", "error.page.duplicate": "En sida med URL-appendixen \"{slug}\" existerar redan", "error.page.duplicate.permission": "Du har inte behörighet att duplicera \"{slug}\"", + "error.page.move.ancestor": "Sidan kan inte flyttas in i sig själv", + "error.page.move.directory": "Sidans mapp kan inte flyttas", + "error.page.move.duplicate": "En undersida med URL-appendixen \"{slug}\" existerar redan", + "error.page.move.noSections": "Sidan \"{parent}\" kan inte vara en förälder till någon sida eftersom den saknar sidsektioner i dess blueprint", + "error.page.move.notFound": "Den flyttade sidan kunde inte hittas", + "error.page.move.permission": "Du saknar behörighet för att flytta \"{slug}\"", + "error.page.move.template": "Mallen \"{template}\" accepteras inte som en undersida till \"{parent}\"", "error.page.notFound": "Sidan \"{slug}\" kan inte hittas", "error.page.num.invalid": "Ange ett giltigt nummer för sortering. Numret får inte vara negativt.", "error.page.slug.invalid": "Ange en giltig URL-appendix", @@ -163,6 +217,8 @@ "error.site.changeTitle.permission": "Du har inte behörighet att ändra titeln på webbplatsen", "error.site.update.permission": "Du har inte behörighet att uppdatera webbplatsen", + "error.structure.validation": "Det finns ett fel i fältet \"{field}\" i rad {index}", + "error.template.default.notFound": "Standardmallen finns inte", "error.unexpected": "Ett oväntat fel uppstod! Aktivera felsökningsläge för mer information: https://getkirby.com/docs/reference/system/options/debug", @@ -195,8 +251,10 @@ "error.validation.accepted": "Vänligen bekräfta", "error.validation.alpha": "Ange endast tecken mellan a-z", "error.validation.alphanum": "Ange endast tecken mellan a-z eller siffror 0-9", + "error.validation.anchor": "Vänligen ange en korrekt länk", "error.validation.between": "Ange ett värde mellan \"{min}\" och \"{max}\"", "error.validation.boolean": "Bekräfta eller neka", + "error.validation.color": "Ange en giltig färg i formatet {format}", "error.validation.contains": "Ange ett värde som innehåller \"{needle}\"", "error.validation.date": "Ange ett giltigt datum", "error.validation.date.after": "Ange ett datum efter {date}", @@ -211,6 +269,7 @@ "error.validation.integer": "Ange en giltig heltalssiffra", "error.validation.ip": "Ange en giltig IP-adress", "error.validation.less": "Ange ett värde lägre än {max}", + "error.validation.linkType": "Länktypen är inte tillåten", "error.validation.match": "Värdet matchar inte det förväntade mönstret", "error.validation.max": "Ange ett värde som är lika med eller lägre än {max}", "error.validation.maxlength": "Ange ett kortare värde. (max {max} tecken)", @@ -227,15 +286,18 @@ "error.validation.same": "Ange \"{other}\"", "error.validation.size": "Storleken av värdet måste vara \"{size}\"", "error.validation.startswith": "Värdet måste börja med \"{start}\"", + "error.validation.tel": "Ange ett oformaterat telefonnummer", "error.validation.time": "Ange en giltig tid", "error.validation.time.after": "Ange en tid efter {time}", "error.validation.time.before": "Ange en tid före {time}", "error.validation.time.between": "Ange en tid mellan {min} och {max}", + "error.validation.uuid": "Ange ett giltigt UUID", "error.validation.url": "Ange en giltig URL", "expand": "Expandera", "expand.all": "Expandera alla", + "field.invalid": "Fältet är ogiltigt", "field.required": "Fältet krävs", "field.blocks.changeType": "Ändra typ", "field.blocks.code.name": "Kod", @@ -245,8 +307,9 @@ "field.blocks.delete.confirm.all": "Vill du verkligen radera alla block?", "field.blocks.delete.confirm.selected": "Vill du verkligen radera de valda blocken?", "field.blocks.empty": "Inga block än", + "field.blocks.fieldsets.empty": "Inga fältuppsättningar ännu", "field.blocks.fieldsets.label": "Välj en typ av block …", - "field.blocks.fieldsets.paste": "Tryck på {{ shortcut }} för att klistra in/importera block från ditt urklipp", + "field.blocks.fieldsets.paste": "Tryck på {{ shortcut }} för att importera layouter/block från urklipp Endast de som är tillåtna i det aktuella fältet kommer att infogas.", "field.blocks.gallery.name": "Galleri", "field.blocks.gallery.images.empty": "Inga bilder än", "field.blocks.gallery.images.label": "Bilder", @@ -254,11 +317,16 @@ "field.blocks.heading.name": "Rubrik", "field.blocks.heading.text": "Text", "field.blocks.heading.placeholder": "Rubrik …", + "field.blocks.figure.back.plain": "Vanlig", + "field.blocks.figure.back.pattern.light": "Mönster (ljust)", + "field.blocks.figure.back.pattern.dark": "Mönster (mörkt)", "field.blocks.image.alt": "Alternativ text", "field.blocks.image.caption": "Rubrik", "field.blocks.image.crop": "Beskär", "field.blocks.image.link": "Länk", "field.blocks.image.location": "Plats", + "field.blocks.image.location.internal": "Denna webbplats", + "field.blocks.image.location.external": "Extern källa", "field.blocks.image.name": "Bild", "field.blocks.image.placeholder": "Välj en bild", "field.blocks.image.ratio": "Bildförhållande", @@ -275,38 +343,72 @@ "field.blocks.quote.citation.placeholder": "av …", "field.blocks.text.name": "Text", "field.blocks.text.placeholder": "Text …", + "field.blocks.video.autoplay": "Autospela", "field.blocks.video.caption": "Rubrik", + "field.blocks.video.controls": "Kontroller", + "field.blocks.video.location": "Plats", + "field.blocks.video.loop": "Loopa", + "field.blocks.video.muted": "Ljud av", "field.blocks.video.name": "Video", "field.blocks.video.placeholder": "Ange en URL till en video", + "field.blocks.video.poster": "Stillbild", + "field.blocks.video.preload": "Förladda", "field.blocks.video.url.label": "Video-URL", "field.blocks.video.url.placeholder": "https://youtube.com/?v=", - "field.files.empty": "Inga filer valda än", + "field.entries.delete.confirm.all": "Vill du verkligen radera alla poster?", + "field.entries.empty": "Inga poster än", + "field.files.empty": "Inga filer valda än", + "field.files.empty.single": "Ingen fil har valts än", + + "field.layout.change": "Ändra layout", "field.layout.delete": "Radera layout", "field.layout.delete.confirm": "Vill du verkligen radera denna layout?", + "field.layout.delete.confirm.all": "Vill du verkligen ta bort alla layouter?", "field.layout.empty": "Inga rader än", "field.layout.select": "Välj en layout", "field.object.empty": "Ingen information ännu", "field.pages.empty": "Inga sidor valda än", + "field.pages.empty.single": "Ingen sida har valts än", "field.structure.delete.confirm": "Vill du verkligen radera denna rad?", "field.structure.delete.confirm.all": "Vill du verkligen radera alla poster?", "field.structure.empty": "Inga poster än", "field.users.empty": "Inga användare valda än", + "field.users.empty.single": "Ingen användare har valts än", + "fields.empty": "Inga fält ännu", + + "file": "Fil", "file.blueprint": "Denna fil har ingen blueprint än. Du kan skapa en i /site/blueprints/files/{blueprint}.yml", + "file.changeTemplate": "Ändra mall", + "file.changeTemplate.notice": "Att ändra filens mall kommer att ta bort innehåll för fält som inte matchar fältets typ. Om den nya mallen definierar vissa regler, t.ex. bilddimensioner, kommer de också att tillämpas oåterkalleligt. Använd med försiktighet.", "file.delete.confirm": "Vill du verkligen radera
{filename}?", + "file.focus.placeholder": "Ange fokuspunkt", + "file.focus.reset": "Ta bort fokuspunkt", + "file.focus.title": "Fokus", "file.sort": "Ändra position", "files": "Filer", + "files.delete.confirm.selected": "Do you really want to delete the selected files? This action cannot be undone.", "files.empty": "Inga filer än", + "filter": "Filter", + + "form.discard": "Kassera ändringar", + "form.discard.confirm": "Vill du verkligen kassera dina ändringar?", + "form.locked": "Detta innehåll är inaktiverat för dig eftersom det för närvarande redigeras av en annan användare", + "form.unsaved": "De aktuella ändringarna har inte sparats än", + "form.preview": "Förhandsgranska ändringar", + "form.preview.draft": "Förhandsgranska utkast", + "hide": "Göm", "hour": "Timme", + "hue": "Nyans", "import": "Importera", "info": "Info", "insert": "Infoga", @@ -324,14 +426,14 @@ "installation.issues.mbstring": "Tillägget MB String krävs", "installation.issues.media": "Mappen /media finns inte eller är inte skrivbar", "installation.issues.php": "Se till att du använder PHP 8+", - "installation.issues.server": "Kirby kräver Apache, Nginx eller Caddy", "installation.issues.sessions": "Mappen /site/sessions finns inte eller är inte skrivbar", - "language": "Språk", + "language": "Spr\u00e5k", "language.code": "Kod", "language.convert": "Ange som standard", "language.convert.confirm": "

Vill du verkligen göra {name} till standardspråket? Detta kan inte ångras.

Om {name} har oöversatt innehåll, kommer det inte längre finnas en alternativ översättning och delar av sajten kommer kanske att vara tom.

", "language.create": "Lägg till ett nytt språk", + "language.default": "Standardspråk", "language.delete.confirm": "Vill du verkligen radera språket {name} inklusive alla översättningar? Detta kan inte ångras!", "language.deleted": "Språket har raderats", "language.direction": "Läsriktning", @@ -340,7 +442,16 @@ "language.locale": "PHP locale string", "language.locale.warning": "Du använder en anpassad språkinställning. Ändra den i språkfilen i mappen /site/languages", "language.name": "Namn", + "language.secondary": "Sekundärt språk", + "language.settings": "Språkinställningar", "language.updated": "Språket har uppdaterats", + "language.variables": "Språkvariabler", + "language.variables.empty": "Inga översättningar ännu", + + "language.variable.delete.confirm": "Vill du verkligen ta bort variabeln för {key}?", + "language.variable.key": "Nyckel", + "language.variable.notFound": "Variabeln kunde inte hittas", + "language.variable.value": "Värde", "languages": "Språk", "languages.default": "Standardspråk", @@ -349,35 +460,56 @@ "languages.secondary.empty": "Det finns inga sekundära språk ännu", "license": "Licens", + "license.activate": "Aktivera nu", + "license.activate.label": "Vänligen aktivera din licens", + "license.activate.domain": "Din licens kommer att vara aktiverad för {host}.", + "license.activate.local": "Du är på väg att aktivera din Kirby-licens för din lokala domän {host}. Om den här webbplatsen kommer att publiceras på en offentlig domän, aktivera den där istället. Om {host} är domänen du vill använda din licens för, fortsätt.", + "license.activated": "Aktiverad", "license.buy": "Köp en licens", - "license.register": "Registrera", + "license.code": "Kod", + "license.code.help": "Du fick din licenskod efter köpet via e-post. Kopiera och klistra in det här.", + "license.code.label": "Ange din licenskod", + "license.status.active.info": "Inkluderar nya större versioner fram till {date}", + "license.status.active.label": "Giltig licens", + "license.status.demo.info": "Detta är en demoinstallation", + "license.status.demo.label": "Demo", + "license.status.inactive.info": "Förnya licensen för att uppdatera till nyare större versioner", + "license.status.inactive.label": "Inga nya större versioner", + "license.status.legacy.bubble": "Är du redo att förnya din licens?", + "license.status.legacy.info": "Din licens täcker inte denna version", + "license.status.legacy.label": "Vänligen förnya din licens", + "license.status.missing.bubble": "Är du redo att lansera din webbplats?", + "license.status.missing.info": "Ingen giltig licens", + "license.status.missing.label": "Vänligen aktivera din licens", + "license.status.unknown.info": "Licensstatusen är okänd", + "license.status.unknown.label": "Unknown", "license.manage": "Hantera dina licenser", - "license.register.help": "Du fick din licenskod via e-post efter inköpet. Kopiera och klistra in den för att registrera licensen.", - "license.register.label": "Ange din licenskod", - "license.register.domain": "Din licens kommer att bli registrerad för {host}.", - "license.register.local": "Du är på väg att registrera din licens för din lokala domän {host}. Om den här webbplatsen kommer att distribueras till en offentlig domän, registrera den där istället. Om {host} är domänen du vill licensiera Kirby till, fortsätt.", - "license.register.success": "Tack för att du stödjer Kirby", - "license.unregistered": "Detta är en oregistrerad demo av Kirby", + "license.purchased": "Köpt", + "license.success": "Tack för att du stödjer Kirby", "license.unregistered.label": "Oregistrerad", - "link": "Länk", - "link.text": "Länktext", + "link": "L\u00e4nk", + "link.text": "L\u00e4nktext", "loading": "Laddar", "lock.unsaved": "Osparade ändringar", "lock.unsaved.empty": "Det finns inga fler osparade ändringar", - "lock.isLocked": "Osparade ändringar av {email}", - "lock.file.isLocked": "Filen redigeras just nu av {email} och kan inte redigeras.", - "lock.page.isLocked": "Sidan redigeras just nu av {email} och kan inte redigeras.", + "lock.unsaved.files": "Osparade filer", + "lock.unsaved.pages": "Osparade sidor", + "lock.unsaved.users": "Osparade konton", + "lock.isLocked": "Osparade ändringar av {email}", "lock.unlock": "Lås upp", - "lock.isUnlocked": "Dina osparade ändringar har skrivits över av en annan användare. Du kan ladda ner dina ändringar för att slå ihop dem manuellt.", + "lock.unlock.submit": "Lås upp och skriv över osparade ändringar av {email}", + "lock.isUnlocked": "Låstes upp av en annan användare", "login": "Logga in", "login.code.label.login": "Inloggningskod", "login.code.label.password-reset": "Kod för återställning av lösenord", "login.code.placeholder.email": "000 000", + "login.code.placeholder.totp": "000000", "login.code.text.email": "Om din e-postadress är registrerad skickades den begärda koden via e-post.", + "login.code.text.totp": "Ange engångskoden från din autentiseringsapp.", "login.email.login.body": "Hej {user.nameOrEmail}.\n\nDu begärde nyligen en inloggningskod till panelen för {site}.\nFöljande kod är giltig i {timeout} minuter:\n\n{code}\n\nOm du inte har begärt någon inloggningskod, ignorera detta e-postmeddelande eller kontakta din administratör om du har frågor.\nAv säkerhetsskäl, vidarebefordra INTE detta e-postmeddelande.", "login.email.login.subject": "Din inloggningskod", "login.email.password-reset.body": "Hej {user.nameOrEmail}.\n\nDu begärde nyligen en kod för återställning av ditt lösenord till panelen för {site}.\nFöljande kod är giltig i {timeout} minuter:\n\n{code}\n\nOm du inte har begärt en återställning av ditt lösenord, ignorera detta e-postmeddelande eller kontakta din administratör om du har frågor.\nAv säkerhetsskäl, vidarebefordra INTE detta e-postmeddelande.", @@ -388,58 +520,80 @@ "login.toggleText.code.email-password": "Logga in med lösenord", "login.toggleText.password-reset.email": "Glömt ditt lösenord?", "login.toggleText.password-reset.email-password": "← Tillbaka till inloggning", + "login.totp.enable.option": "Ställ in engångskoder", + "login.totp.enable.intro": "Autentiseringsappar kan generera engångskoder som används som en andra faktor när du loggar in på ditt konto.", + "login.totp.enable.qr.label": "1. Skanna den här QR-koden", + "login.totp.enable.qr.help": "Kan du inte skanna? Lägg till inställningsnyckeln {secret} manuellt i din autentiseringsapp.", + "login.totp.enable.confirm.headline": "2. Bekräfta med genererad kod", + "login.totp.enable.confirm.text": "Din app genererar en ny engångskod var 30:e sekund. Ange den aktuella koden för att slutföra installationen:", + "login.totp.enable.confirm.label": "Nuvarande kod", + "login.totp.enable.confirm.help": "Efter denna installation kommer vi att be dig om en engångskod varje gång du loggar in.", + "login.totp.enable.success": "Engångskoder aktiverade", + "login.totp.disable.option": "Inaktivera engångskoder", + "login.totp.disable.label": "Ange ditt lösenord för att inaktivera engångskoder", + "login.totp.disable.help": "I fortsättningen kommer en annan andra faktor som en inloggningskod som skickas via e-post att begäras när du loggar in. Du kan alltid ställa in engångskoder igen senare.", + "login.totp.disable.admin": "

Detta kommer att inaktivera engångskoder för {user}.

I fortsättningen kommer en annan andra faktor som en inloggningskod som skickas via e-post att begäras när de loggar in. {user} kan ställa in engångskoder igen efter nästa inloggning.

", + "login.totp.disable.success": "Engångskoder inaktiverade", "logout": "Logga ut", + "merge": "Slå ihop", "menu": "Meny", "meridiem": "a.m./p.m.", "mime": "Mediatyp", "minutes": "Minuter", "month": "Månad", - "months.april": "april", - "months.august": "augusti", - "months.december": "december", + "months.april": "April", + "months.august": "Augusti", + "months.december": "December", "months.february": "Februari", - "months.january": "januari", - "months.july": "juli", - "months.june": "juni", - "months.march": "mars", - "months.may": "maj", - "months.november": "november", - "months.october": "october", - "months.september": "september", + "months.january": "Januari", + "months.july": "Juli", + "months.june": "Juni", + "months.march": "Mars", + "months.may": "Maj", + "months.november": "November", + "months.october": "Oktober", + "months.september": "September", "more": "Mer", + "move": "Flytta", "name": "Namn", "next": "Nästa", + "night": "Natt", "no": "nej", "off": "av", "on": "på", "open": "Öppna", "open.newWindow": "Öppna i nytt fönster", + "option": "Alternativ", "options": "Alternativ", "options.none": "Inga alternativ", + "options.all": "Visa alla {count} alternativ", "orientation": "Orientering", "orientation.landscape": "Liggande", "orientation.portrait": "Stående", "orientation.square": "Kvadrat", + "page": "Sida", "page.blueprint": "Denna sida har ingen blueprint än. Du kan skapa en i /site/blueprints/pages/{blueprint}.yml", "page.changeSlug": "Ändra URL", - "page.changeSlug.fromTitle": "Skapa utfifrån titel", + "page.changeSlug.fromTitle": "Skapa utifr\u00e5n titel", "page.changeStatus": "Ändra status", "page.changeStatus.position": "Välj en ny position", "page.changeStatus.select": "Välj en ny status", "page.changeTemplate": "Ändra mall", + "page.changeTemplate.notice": "Att ändra filens mall kommer att ta bort innehåll för fält som inte matchar fältets typ. Använd med försiktighet.", + "page.create": "Skapa som {status}", "page.delete.confirm": "Vill du verkligen radera {title}?", "page.delete.confirm.subpages": "Denna sida har undersidor.
Alla undersidor kommer också att raderas.", "page.delete.confirm.title": "Fyll i sidans titel för att bekräfta", - "page.draft.create": "Skapa utkast", "page.duplicate.appendix": "Kopiera", "page.duplicate.files": "Kopiera filer", "page.duplicate.pages": "Kopiera sidor", + "page.move": "Flytta sidan", "page.sort": "Ändra position", "page.status": "Status", "page.status.draft": "Utkast", @@ -450,25 +604,33 @@ "page.status.unlisted.description": "Sidan är endast åtkomlig via URL", "pages": "Sidor", + "pages.delete.confirm.selected": "Do you really want to delete the selected pages? This action cannot be undone.", "pages.empty": "Inga sidor än", "pages.status.draft": "Utkast", "pages.status.listed": "Publicerade", - "pages.status.unlisted": "Olistad", + "pages.status.unlisted": "Olistade", "pagination.page": "Sida", - "password": "Lösenord", + "password": "L\u00f6senord", "paste": "Klistra in", "paste.after": "Klistra in efter", + "paste.success": "{count} har klistrats in!", "pixel": "Pixel", "plugin": "Tillägg", "plugins": "Tillägg", "prev": "Föregående", "preview": "Förhandsgranska", + + "publish": "Publicera", + "published": "Publicerade", + "remove": "Ta bort", "rename": "Byt namn", + "renew": "Förnya", "replace": "Ersätt", - "retry": "Försök igen", + "replace.with": "Ersätt med", + "retry": "F\u00f6rs\u00f6k igen", "revert": "Återgå", "revert.confirm": "Vill du verkligen radera alla osparade ändringar?", @@ -482,11 +644,14 @@ "role.nobody.title": "Ingen", "save": "Spara", + "saved": "Sparad", "search": "Sök", + "searching": "Söker", "search.min": "Ange {min} tecken för att söka", - "search.all": "Visa alla", + "search.all": "Visa alla {count} resultat", "search.results.none": "Inga träffar", + "section.invalid": "Sektionen är ogiltig", "section.required": "Sektionen krävs", "security": "Säkerhet", @@ -498,16 +663,25 @@ "size": "Storlek", "slug": "URL-appendix", "sort": "Sortera", + "sort.drag": "Dra för att sortera …", + "split": "Dela", "stats.empty": "Inga rapporter", + "status": "Status", + + "system.info.copy": "Kopiera info", + "system.info.copied": "Systeminformation kopierad", "system.issues.content": "Mappen content verkar vara exponerad", "system.issues.eol.kirby": "Din installerade Kirby-version har nått slutet av sin livscykel och kommer inte att få fler säkerhetsuppdateringar", "system.issues.eol.plugin": "Den installerade versionen av tillägget { plugin } har nått slutet på sin livscykel och kommer inte att få fler säkerhetsuppdateringar.", + "system.issues.eol.php": "Din installerade PHP-version { release } har nått slutet av sin livslängd och kommer inte att få fler säkerhetsuppdateringar", "system.issues.debug": "Felsökningsläget måste vara avstängt i produktion", "system.issues.git": "Mappen .git verkar vara exponerad", "system.issues.https": "Vi rekommenderar HTTPS för alla dina webbplatser", "system.issues.kirby": "Mappen kirby verkar vara exponerad", + "system.issues.local": "The site is running locally with relaxed security checks", "system.issues.site": "Mappen site verkar vara exponerad", + "system.issues.vue.compiler": "The Vue template compiler is enabled", "system.issues.vulnerability.kirby": "Din installation kan vara påverkad av följande sårbarhet ({ severity } allvarlighetsgrad): { description }", "system.issues.vulnerability.plugin": "Din installation kan vara påverkad av följande sårbarhet i tillägget { plugin } ({ severity } allvarlighetsgrad): { description }", "system.updateStatus": "Uppdateringsstatus", @@ -520,13 +694,22 @@ "system.updateStatus.update": "Gratis uppdatering { version } tillgänglig", "system.updateStatus.upgrade": "Uppgradering { version } tillgänglig", - "title": "Titel", + "tel": "Telefon", + "tel.placeholder": "+46701234567", "template": "Mall", + + "theme": "Tema", + "theme.light": "Ljus på", + "theme.dark": "Ljus av", + "theme.automatic": "Matcha systemstandard", + + "title": "Titel", "today": "Idag", + "toolbar.button.clear": "Rensa formatering", "toolbar.button.code": "Kod", "toolbar.button.bold": "Fet", - "toolbar.button.email": "E-postadress", + "toolbar.button.email": "E-post", "toolbar.button.headings": "Rubriker", "toolbar.button.heading.1": "Rubrik 1", "toolbar.button.heading.2": "Rubrik 2", @@ -538,9 +721,11 @@ "toolbar.button.file": "Fil", "toolbar.button.file.select": "Välj en fil", "toolbar.button.file.upload": "Ladda upp en fil", - "toolbar.button.link": "Länk", + "toolbar.button.link": "L\u00e4nk", "toolbar.button.paragraph": "Stycke", "toolbar.button.strike": "Genomstruken", + "toolbar.button.sub": "Nedsänkt", + "toolbar.button.sup": "Upphöjd", "toolbar.button.ol": "Sorterad lista", "toolbar.button.underline": "Understruken", "toolbar.button.ul": "Punktlista", @@ -550,6 +735,8 @@ "translation.name": "Svenska", "translation.locale": "sv_SE", + "type": "Typ", + "upload": "Ladda upp", "upload.error.cantMove": "Den överförda filen kunde inte flyttas", "upload.error.cantWrite": "Det gick inte att skriva filen till hårddisken", @@ -574,6 +761,7 @@ "user.changeLanguage": "Ändra språk", "user.changeName": "Byt namn på denna användare", "user.changePassword": "Ändra lösenord", + "user.changePassword.current": "Your current password", "user.changePassword.new": "Nytt lösenord", "user.changePassword.new.confirm": "Bekräfta det nya lösenordet...", "user.changeRole": "Ändra roll", @@ -585,17 +773,20 @@ "users": "Användare", "version": "Version", + "version.changes": "Ändrad version", + "version.compare": "Jämför versioner", "version.current": "Aktuell version", "version.latest": "Senaste version", "versionInformation": "Versionsinformation", + "view": "Visa", "view.account": "Ditt konto", "view.installation": "Installation", "view.languages": "Språk", "view.resetPassword": "Återställ lösenord", "view.site": "Webbplats", "view.system": "System", - "view.users": "Användare", + "view.users": "Anv\u00e4ndare", "welcome": "Välkommen", "year": "År", diff --git a/kirby/i18n/translations/tr.json b/kirby/i18n/translations/tr.json index d4875e2..9684444 100644 --- a/kirby/i18n/translations/tr.json +++ b/kirby/i18n/translations/tr.json @@ -3,19 +3,28 @@ "account.delete": "Hesabınızı silin", "account.delete.confirm": "Hesabınızı gerçekten silmek istiyor musunuz? Oturumunuz hemen sonlandırılacaktır. Hesabınız daha sonra geri alınamaz.", + "activate": "Etkinleştir", "add": "Ekle", + "alpha": "Alfa", "author": "Yazar", "avatar": "Profil resmi", "back": "Geri", - "cancel": "İptal", - "change": "Değiştir", + "cancel": "\u0130ptal", + "change": "De\u011fi\u015ftir", "close": "Kapat", + "changes": "Değişiklikler", "confirm": "Tamam", "collapse": "Daralt", "collapse.all": "Tümünü daralt", - "copy": "Kopya", + "color": "Renk", + "coordinates": "Koordinatlar", + "copy": "Kopyala", "copy.all": "Tümünü kopyala", + "copy.success": "{count} kopyalandı!", + "copy.success.multiple": "{count} kopyalandı!", + "copy.url": "URL kopyala", "create": "Oluştur", + "custom": "Özel", "date": "Tarih", "date.select": "Bir tarih seçiniz", @@ -27,33 +36,42 @@ "days.sun": "Paz", "days.thu": "Per", "days.tue": "Sal", - "days.wed": "Çar", + "days.wed": "\u00c7ar", "debugging": "Hata ayıklama", "delete": "Sil", "delete.all": "Tümünü sil", + "dialog.fields.empty": "Bu iletişim kutusunda alan yok", "dialog.files.empty": "Seçilecek dosya yok", "dialog.pages.empty": "Seçilecek sayfa yok", + "dialog.text.empty": "Bu iletişim kutusu herhangi bir metin tanımlamaz", "dialog.users.empty": "Seçilecek kullanıcı yok", "dimensions": "Boyutlar", + "disable": "Devre dışı bırak", "disabled": "Devredışı", - "discard": "Vazgeç", + "discard": "Vazge\u00e7", + + "drawer.fields.empty": "Bu çekmecede alan yok", + + "domain": "Alan adı", "download": "İndir", "duplicate": "Kopyala", - "edit": "Düzenle", + "edit": "D\u00fczenle", - "email": "E-posta", - "email.placeholder": "mail@example.com", + "email": "E-Posta", + "email.placeholder": "eposta@ornek.com", + "enter": "Giriş", "entries": "Girdiler", "entry": "Girdi", "environment": "Ortam", + "error": "Hata", "error.access.code": "Geçersiz kod", "error.access.login": "Geçersiz giriş", "error.access.panel": "Panel'e erişim izniniz yok", @@ -74,13 +92,31 @@ "error.cache.type.invalid": "Geçersiz önbellek türü \"{type}\"", + "error.content.lock.delete": "Bu sürüm kilitlidir ve silinemez", + "error.content.lock.move": "Bu kaynak sürümü kilitlidir ve taşınamaz", + "error.content.lock.publish": "Bu sürüm zaten yayınlandı", + "error.content.lock.replace": "Bu sürüm kilitlidir ve değiştirilemez", + "error.content.lock.update": "Bu sürüm kilitlidir ve güncellenemez", + + "error.entries.max.plural": "{max} girdiden fazlasını eklememelisiniz", + "error.entries.max.singular": "Birden fazla girdi eklememelisiniz", + "error.entries.min.plural": "En az {min} girdi eklemelisiniz", + "error.entries.min.singular": "En az bir girdi eklemelisiniz", + "error.entries.supports": "\"{type}\" alan türü girdiler alanı için desteklenmiyor", + "error.entries.validation": "{index} satırındaki \"{field}\" alanında bir hata var", + "error.email.preset.notFound": "\"{name}\" e-posta adresi bulunamadı", "error.field.converter.invalid": "Geçersiz dönüştürücü \"{converter}\"", + "error.field.link.options": "Geçersiz seçenekler: {options}", "error.field.type.missing": "\"{ name }\" alanı: \"{ type }\" alan türü mevcut değil", "error.file.changeName.empty": "İsim boş olmamalıdır", "error.file.changeName.permission": "\"{filename}\" adını değiştiremezsiniz", + "error.file.changeTemplate.invalid": "\"{id}\" dosyası için şablon \"{template}\" olarak değiştirilemez (geçerli: \"{blueprints}\")", + "error.file.changeTemplate.permission": "\"{id}\" dosyası için şablonu değiştirmenize izin verilmiyor", + + "error.file.delete.multiple": "Tüm dosyalar silinemedi. Silinmeyi engelleyen belirli hatayı görmek için kalan her dosyayı ayrı ayrı deneyin.", "error.file.duplicate": "\"{filename}\" isimli bir dosya zaten var", "error.file.extension.forbidden": "\"{extension}\" dosya uzantısına izin verilmiyor", "error.file.extension.invalid": "Geçersiz uzantı: {extension}", @@ -95,33 +131,43 @@ "error.file.minheight": "Resmin yüksekliği en az {height} piksel olmalıdır", "error.file.minsize": "Dosya çok küçük", "error.file.minwidth": "Resmin genişliği en az {width} piksel olmalıdır", + "error.file.name.unique": "Dosya adı benzersiz olmalıdır", "error.file.name.missing": "Dosya adı boş bırakılamaz", "error.file.notFound": "\"{filename}\" dosyası bulunamadı", "error.file.orientation": "Resmin oryantasyonu \"{orientation}\" olmalıdır", + "error.file.sort.permission": "\"{filename}\" dosyasının sıralamasını değiştiremezsiniz", "error.file.type.forbidden": "{type} dosya yükleme izni yok", "error.file.type.invalid": "Geçersiz dosya türü: {type}", - "error.file.undefined": "Dosya bulunamadı", + "error.file.undefined": "Dosya bulunamad\u0131", "error.form.incomplete": "Lütfen tüm form hatalarını düzeltin...", "error.form.notSaved": "Form kaydedilemedi", "error.language.code": "Lütfen dil için geçerli bir kod girin", + "error.language.create.permission": "Bir dil oluşturmanıza izin verilmiyor", + "error.language.delete.permission": "Dili silmenize izin verilmiyor", "error.language.duplicate": "Bu dil zaten var", "error.language.name": "Lütfen dil için geçerli bir isim girin", "error.language.notFound": "Dil bulunamadı", + "error.language.update.permission": "Dili güncellemenize izin verilmiyor", "error.layout.validation.block": "{layoutIndex}. sıradaki düzende \"{fieldset}\" blok türünü kullanan {blockIndex}. bloktaki \"{field}\" alanında bir hata var", "error.layout.validation.settings": "{index}. düzen ayarlarında bir hata var", - "error.license.format": "Lütfen geçerli bir lisans anahtarı girin", + "error.license.domain": "Lisans için alan adı eksik", "error.license.email": "Lütfen geçerli bir e-posta adresi girin", + "error.license.format": "Lütfen geçerli bir lisans anahtarı girin", "error.license.verification": "Lisans doğrulanamadı", + "error.login.totp.confirm.invalid": "Geçersiz kod", + "error.login.totp.confirm.missing": "Lütfen geçerli kodu girin", + "error.object.validation": "\"{label}\" alanında bir hata var:\n{message}", "error.offline": "Panel şu anda çevrimdışı", "error.page.changeSlug.permission": "\"{slug}\" uzantısına sahip bu sayfanın adresini değiştirilemez", + "error.page.changeSlug.reserved": "Üst düzey sayfaların yolu \"{path}\" ile başlamamalıdır", "error.page.changeStatus.incomplete": "Sayfada hatalar var ve yayınlanamadı", "error.page.changeStatus.permission": "Bu sayfanın durumu değiştirilemez", "error.page.changeStatus.toDraft.invalid": "\"{slug}\" sayfası bir taslak haline dönüştürülemiyor", @@ -133,17 +179,25 @@ "error.page.delete": "\"{slug}\" sayfası silinemedi", "error.page.delete.confirm": "Onaylamak için sayfa başlığını girin", "error.page.delete.hasChildren": "Sayfada alt sayfalar var ve silinemiyor", + "error.page.delete.multiple": "Tüm sayfalar silinemedi. Silinmeyi engelleyen belirli hatayı görmek için kalan her sayfayı ayrı ayrı deneyin.", "error.page.delete.permission": "\"{slug}\" öğesini silmenize izin verilmiyor", "error.page.draft.duplicate": "\"{slug}\" adres eki olan bir sayfa taslağı zaten mevcut", "error.page.duplicate": "\"{slug}\" adres eki içeren bir sayfa zaten mevcut", "error.page.duplicate.permission": "\"{slug}\" öğesini çoğaltmanıza izin verilmiyor", + "error.page.move.ancestor": "Sayfa kendi içine taşınamaz", + "error.page.move.directory": "Sayfa dizini taşınamaz", + "error.page.move.duplicate": "\"{slug}\" URL ekine sahip bir alt sayfa zaten mevcut", + "error.page.move.noSections": "\"{parent}\" sayfası, planında herhangi bir sayfa bölümü bulunmadığı için herhangi bir sayfanın üst öğesi olamaz", + "error.page.move.notFound": "Taşınan sayfa bulunamadı", + "error.page.move.permission": "\"{slug}\" öğesini taşımanıza izin verilmiyor", + "error.page.move.template": "\"{template}\" şablonu \"{parent}\" alt sayfası olarak kabul edilmiyor", "error.page.notFound": "\"{slug}\" uzantısındaki sayfa bulunamadı", "error.page.num.invalid": "Lütfen geçerli bir sıralama numarası girin. Sayılar negatif olmamalıdır.", "error.page.slug.invalid": "Lütfen geçerli bir URL eki girin", "error.page.slug.maxlength": "Adres uzantısı \"{length}\" karakterden az olmalıdır", "error.page.sort.permission": "\"{slug}\" sayfası sıralanamıyor", "error.page.status.invalid": "Lütfen geçerli bir sayfa durumu ayarlayın", - "error.page.undefined": "Sayfa bulunamadı", + "error.page.undefined": "Sayfa bulunamad\u0131", "error.page.update.permission": "\"{slug}\" güncellemesine izin verilmiyor", "error.section.files.max.plural": "\"{section}\" bölümüne {max} dosyadan daha fazlasını eklememelisiniz", @@ -163,6 +217,8 @@ "error.site.changeTitle.permission": "Sitenin başlığını değiştiremezsin", "error.site.update.permission": "Siteyi güncellemenize izin verilmiyor", + "error.structure.validation": "{index} satırındaki \"{field}\" alanında bir hata var", + "error.template.default.notFound": "Varsayılan şablon yok", "error.unexpected": "Beklenmeyen bir hata oluştu! Daha fazla bilgi için hata ayıklama modunu etkinleştirin: https://getkirby.com/docs/reference/system/options/debug", @@ -176,7 +232,7 @@ "error.user.changeRole.toAdmin": "Birini yönetici rolüne tanıtmanıza izin verilmiyor", "error.user.create.permission": "Bu kullanıcıyı oluşturmanıza izin verilmiyor", "error.user.delete": "\"{name}\" kullanıcısı silinemedi", - "error.user.delete.lastAdmin": "Son yönetici kullanıcıyı silemezsiniz", + "error.user.delete.lastAdmin": "Son y\u00f6netici kullan\u0131c\u0131y\u0131 silemezsiniz", "error.user.delete.lastUser": "Son kullanıcı silinemez", "error.user.delete.permission": "\"{name}\" kullanıcısını silme yetkiniz yok", "error.user.duplicate": "\"{email}\" e-posta adresine sahip bir kullanıcı zaten var", @@ -185,7 +241,7 @@ "error.user.notFound": "\"{name}\" kullanıcısı bulunamadı", "error.user.password.excessive": "Lütfen geçerli bir şifre girin. Şifreler 1000 karakterden uzun olmamalıdır.", "error.user.password.invalid": "Lütfen geçerli bir şifre giriniz. Şifreler en az 8 karakter uzunluğunda olmalıdır.", - "error.user.password.notSame": "Lütfen şifreyi doğrulayın", + "error.user.password.notSame": "L\u00fctfen \u015fifreyi do\u011frulay\u0131n", "error.user.password.undefined": "Bu kullanıcının şifresi yok", "error.user.password.wrong": "Yanlış şifre", "error.user.role.invalid": "Lütfen geçerli bir rol girin", @@ -195,8 +251,10 @@ "error.validation.accepted": "Lütfen onaylayın", "error.validation.alpha": "Lütfen sadece a-z arasındaki karakterleri girin", "error.validation.alphanum": "Lütfen sadece a-z veya 0-9 arasındaki rakamları girin", + "error.validation.anchor": "Lütfen doğru bir bağlantı çapası girin", "error.validation.between": "Lütfen \"{min}\" ile \"{max}\" arasında bir değer girin", "error.validation.boolean": "Lütfen onaylayın veya reddedin", + "error.validation.color": "Lütfen {format} biçiminde geçerli bir renk girin", "error.validation.contains": "Lütfen \"{needle}\" içeren bir değer girin", "error.validation.date": "Lütfen geçerli bir tarih girin", "error.validation.date.after": "Lütfen {date} tarihinden sonra bir tarih girin", @@ -211,6 +269,7 @@ "error.validation.integer": "Lütfen geçerli bir tamsayı girin", "error.validation.ip": "Lütfen geçerli bir ip adresi girin", "error.validation.less": "Lütfen {max} 'dan daha düşük bir değer girin", + "error.validation.linkType": "Bağlantı türüne izin verilmiyor", "error.validation.match": "Değer beklenen modelle eşleşmiyor", "error.validation.max": "Lütfen {max} 'a eşit veya daha küçük bir değer girin", "error.validation.maxlength": "Lütfen daha kısa bir değer girin. (maks. {max} karakter)", @@ -227,15 +286,18 @@ "error.validation.same": "Lütfen \"{other}\" yazınız", "error.validation.size": "Değerin boyutu \"{size}\" olmalıdır", "error.validation.startswith": "Değer \"{start}\" ile başlamalıdır", + "error.validation.tel": "Lütfen biçimlendirilmemiş bir telefon numarası girin", "error.validation.time": "Lütfen geçerli bir zaman girin", "error.validation.time.after": "Lütfen {time} sonrası bir tarih girin", "error.validation.time.before": "Lütfen {time} öncesi bir tarih girin", "error.validation.time.between": "Lütfen {min} ile {max} arasında bir tarih girin", + "error.validation.uuid": "Lütfen geçerli bir UUID girin", "error.validation.url": "Lütfen geçerli bir adres girin", "expand": "Genişlet", "expand.all": "Tümünü genişlet", + "field.invalid": "Bu alan geçersizdir", "field.required": "Alan gereklidir", "field.blocks.changeType": "Türü değiştir", "field.blocks.code.name": "Kod", @@ -245,8 +307,9 @@ "field.blocks.delete.confirm.all": "Tüm blokları gerçekten silmek istiyor musunuz?", "field.blocks.delete.confirm.selected": "Seçilen blokları gerçekten silmek istiyor musunuz?", "field.blocks.empty": "Henüz blok yok", + "field.blocks.fieldsets.empty": "Henüz alan kümesi yok", "field.blocks.fieldsets.label": "Lütfen bir blok türü seçiniz …", - "field.blocks.fieldsets.paste": "Panonuzdan blokları yapıştırmak veya içe aktarmak için {{ shortcut }}'e basın", + "field.blocks.fieldsets.paste": "Panonuzdan düzenleri/blokları içe aktarmak için {{ shortcut }} tuşuna basın Yalnızca geçerli alanda izin verilenler eklenecektir.", "field.blocks.gallery.name": "Galeri", "field.blocks.gallery.images.empty": "Henüz görsel yok", "field.blocks.gallery.images.label": "Görseller", @@ -254,11 +317,16 @@ "field.blocks.heading.name": "Başlık", "field.blocks.heading.text": "Metin", "field.blocks.heading.placeholder": "Başlık …", + "field.blocks.figure.back.plain": "Düz", + "field.blocks.figure.back.pattern.light": "Desen (açık)", + "field.blocks.figure.back.pattern.dark": "Desen (koyu)", "field.blocks.image.alt": "Alternatif metin", "field.blocks.image.caption": "Altyazı", "field.blocks.image.crop": "Kırp", "field.blocks.image.link": "Bağlantı", "field.blocks.image.location": "Lokasyon", + "field.blocks.image.location.internal": "Bu website", + "field.blocks.image.location.external": "Dış kaynak", "field.blocks.image.name": "Görsel", "field.blocks.image.placeholder": "Bir görsel seçin", "field.blocks.image.ratio": "Oran", @@ -275,38 +343,72 @@ "field.blocks.quote.citation.placeholder": "yazar …", "field.blocks.text.name": "Metin", "field.blocks.text.placeholder": "Metin …", + "field.blocks.video.autoplay": "Otomatik oynatma", "field.blocks.video.caption": "Altyazı", + "field.blocks.video.controls": "Kontroller", + "field.blocks.video.location": "Lokasyon", + "field.blocks.video.loop": "Döngü", + "field.blocks.video.muted": "Sessiz", "field.blocks.video.name": "Video", "field.blocks.video.placeholder": "Bir video URL'si girin", + "field.blocks.video.poster": "Kapak", + "field.blocks.video.preload": "Önyükleme", "field.blocks.video.url.label": "Video-URL", "field.blocks.video.url.placeholder": "https://youtube.com/?v=", - "field.files.empty": "Henüz dosya seçilmedi", + "field.entries.delete.confirm.all": "Tüm girdileri gerçekten silmek istiyor musunuz?", + "field.entries.empty": "Henüz bir girdi yok", + "field.files.empty": "Henüz dosya seçilmedi", + "field.files.empty.single": "Henüz dosya seçilmedi", + + "field.layout.change": "Düzeni değiştir", "field.layout.delete": "Düzeni sil", "field.layout.delete.confirm": "Bu düzeni gerçekten silmek istiyor musunuz?", + "field.layout.delete.confirm.all": "Gerçekten tüm düzenleri silmek istiyor musunuz?", "field.layout.empty": "Henüz satır yok", "field.layout.select": "Bir düzen seçin", "field.object.empty": "Henüz bilgi yok", "field.pages.empty": "Henüz sayfa seçilmedi", + "field.pages.empty.single": "Henüz sayfa seçilmedi", - "field.structure.delete.confirm": "Bu girdiyi silmek istediğinizden emin misiniz?", + "field.structure.delete.confirm": "Bu girdiyi silmek istedi\u011finizden emin misiniz?", "field.structure.delete.confirm.all": "Tüm girdileri gerçekten silmek istiyor musunuz?", - "field.structure.empty": "Henüz bir girdi yok", + "field.structure.empty": "Hen\u00fcz bir girdi yok", "field.users.empty": "Henüz kullanıcı seçilmedi", + "field.users.empty.single": "Henüz kullanıcı seçilmedi", + "fields.empty": "Henüz alan yok", + + "file": "Dosya", "file.blueprint": "Bu dosyanın henüz bir planı yok. Kurulumu /site/blueprints/files/{blueprint}.yml dosyasında tanımlayabilirsiniz.", + "file.changeTemplate": "Şablonu değiştir", + "file.changeTemplate.notice": "Dosyanın şablonunun değiştirilmesi, tür olarak eşleşmeyen alanların içeriğini kaldıracaktır. Yeni şablon, görüntü boyutları gibi belirli kuralları tanımlıyorsa, bunlar da geri döndürülemez şekilde uygulanacaktır. Dikkatli kullanın.", "file.delete.confirm": "{filename} dosyasını silmek istediğinizden emin misiniz?", + "file.focus.placeholder": "Odak noktasını belirleyin", + "file.focus.reset": "Odak noktasını kaldırın", + "file.focus.title": "Odak", "file.sort": "Pozisyon değiştir", "files": "Dosyalar", + "files.delete.confirm.selected": "Seçili dosyaları gerçekten silmek istiyor musunuz? Bu eylem geri alınamaz.", "files.empty": "Henüz dosya yok", + "filter": "Filtre", + + "form.discard": "Değişiklikleri iptal et", + "form.discard.confirm": "Gerçekten tüm değişikliklerinizi silmek istiyor musunuz?", + "form.locked": "Bu içerik şu anda başka bir kullanıcı tarafından düzenlendiği için sizin için devre dışı bırakıldı", + "form.unsaved": "Mevcut değişiklikler henüz kaydedilmedi", + "form.preview": "Değişiklikleri önizle", + "form.preview.draft": "Taslağı önizle", + "hide": "Gizle", "hour": "Saat", + "hue": "Renk tonu", "import": "İçe aktar", "info": "Bilgi", "insert": "Ekle", @@ -324,7 +426,6 @@ "installation.issues.mbstring": "MB String eklentisi gerekli", "installation.issues.media": "/media klasörü yok yada yazılamaz", "installation.issues.php": "PHP 8+ kullandığınızdan emin olun. ", - "installation.issues.server": "Kirby Apache, Nginx veya Caddy gerektirir", "installation.issues.sessions": "/site/sessions klasörü mevcut değil veya yazılabilir değil", "language": "Dil", @@ -332,6 +433,7 @@ "language.convert": "Varsayılan yap", "language.convert.confirm": "

{name}'i varsayılan dile dönüştürmek istiyor musunuz? Bu geri alınamaz.

{name} çevrilmemiş içeriğe sahipse, artık geçerli bir geri dönüş olmaz ve sitenizin bazı bölümleri boş olabilir.

", "language.create": "Yeni bir dil ekle", + "language.default": "Varsayılan dil", "language.delete.confirm": "Tüm çevirileri içeren {name} dilini gerçekten silmek istiyor musunuz? Bu geri alınamaz!", "language.deleted": "Dil silindi", "language.direction": "Okuma yönü", @@ -340,7 +442,16 @@ "language.locale": "PHP yerel dizesi", "language.locale.warning": "Özel bir yerel ayar kullanıyorsunuz. Lütfen /site/languages konumundaki dil dosyasından değiştirin.", "language.name": "İsim", + "language.secondary": "İkincil dil", + "language.settings": "Dil ayarları", "language.updated": "Dil güncellendi", + "language.variables": "Dil değişkenleri", + "language.variables.empty": "Henüz çeviri yok", + + "language.variable.delete.confirm": "Gerçekten {key} değişkenini silmek istiyor musunuz?", + "language.variable.key": "Anahtar", + "language.variable.notFound": "Değişken bulunamadı", + "language.variable.value": "Değer", "languages": "Diller", "languages.default": "Varsayılan dil", @@ -349,35 +460,56 @@ "languages.secondary.empty": "Henüz ikincil bir dil yok", "license": "Lisans", + "license.activate": "Şimdi etkinleştirin", + "license.activate.label": "Lütfen lisansınızı etkinleştirin", + "license.activate.domain": "Lisansınız {host} için etkinleştirilecektir.", + "license.activate.local": "Yerel etki alanınız {host} için Kirby lisansınızı etkinleştirmek üzeresiniz. Bu site genel bir etki alanına kurulacaksa, lütfen bunun yerine orada etkinleştirin. Eğer lisansınızı kullanmak istediğiniz alan adı {host} ise lütfen devam edin.", + "license.activated": "Etkinleştirildi", "license.buy": "Bir lisans satın al", - "license.register": "Kayıt Ol", + "license.code": "Kod", + "license.code.help": "Lisans kodunuzu satın alma işleminden sonra e-posta yoluyla aldınız. Lütfen kopyalayıp buraya yapıştırın.", + "license.code.label": "Lütfen lisans kodunu giriniz", + "license.status.active.info": "{date} tarihine kadar yeni ana sürümleri içerir", + "license.status.active.label": "Geçerli lisans", + "license.status.demo.info": "Bu bir demo kurulumudur", + "license.status.demo.label": "Demo", + "license.status.inactive.info": "Yeni ana sürümlere güncellemek için lisansı yenileyin", + "license.status.inactive.label": "Yeni ana sürüm yok", + "license.status.legacy.bubble": "Lisansınızı yenilemeye hazır mısınız?", + "license.status.legacy.info": "Lisansınız bu sürümü kapsamıyor", + "license.status.legacy.label": "Lütfen lisansınızı yenileyin", + "license.status.missing.bubble": "Sitenizi yayına almaya hazır mısınız?", + "license.status.missing.info": "Geçerli lisans yok", + "license.status.missing.label": "Lütfen lisansınızı etkinleştirin", + "license.status.unknown.info": "Lisans durumu bilinmiyor", + "license.status.unknown.label": "Bilinmiyor", "license.manage": "Lisanslarınızı yönetin", - "license.register.help": "Satın alma işleminden sonra e-posta yoluyla lisans kodunuzu aldınız. Lütfen kayıt olmak için kodu kopyalayıp yapıştırın.", - "license.register.label": "Lütfen lisans kodunu giriniz", - "license.register.domain": "Lisansınız {host} için kaydedilecek.", - "license.register.local": "Lisansınızı yerel alan adınız {host} için kaydetmek üzeresiniz. Bu site genel bir etki alanına dağıtılacaksa, lütfen bunun yerine lisansı orada kaydedin. Kirby'yi lisanslamak istediğiniz alan adı {host} ise lütfen devam edin.", - "license.register.success": "Kirby'yi desteklediğiniz için teşekkürler", - "license.unregistered": "Bu Kirby'nin kayıtsız bir demosu", + "license.purchased": "Satın alındı", + "license.success": "Kirby'yi desteklediğiniz için teşekkürler", "license.unregistered.label": "Kayıtsız", - "link": "Bağlantı", - "link.text": "Bağlantı metni", + "link": "Ba\u011flant\u0131", + "link.text": "Ba\u011flant\u0131 yaz\u0131s\u0131", "loading": "Yükleniyor", "lock.unsaved": "Kaydedilmemiş değişiklikler", "lock.unsaved.empty": "Daha fazla kaydedilmemiş değişiklik yok", - "lock.isLocked": "{email} tarafından kaydedilmemiş değişiklikler", - "lock.file.isLocked": "Dosya şu anda {email} tarafından düzenlenmektedir ve değiştirilemez.", - "lock.page.isLocked": "Sayfa şu anda {email} tarafından düzenlenmektedir ve değiştirilemez.", + "lock.unsaved.files": "Kaydedilmemiş dosyalar", + "lock.unsaved.pages": "Kaydedilmemiş sayfalar", + "lock.unsaved.users": "Kaydedilmemiş hesaplar", + "lock.isLocked": "{email} tarafından yapılan kaydedilmemiş değişiklikler", "lock.unlock": "Kilidi Aç", - "lock.isUnlocked": "Kaydedilmemiş değişikliklerin üzerine başka bir kullanıcı yazmış. Değişikliklerinizi el ile birleştirmek için değişikliklerinizi indirebilirsiniz.", + "lock.unlock.submit": "Kaydedilmemiş değişikliklerin kilidini {email} ile açın ve üzerine yazın", + "lock.isUnlocked": "Başka bir kullanıcı tarafından kilidi açıldı", "login": "Giriş", "login.code.label.login": "Giriş kodu", "login.code.label.password-reset": "Şifre sıfırlama kodu", "login.code.placeholder.email": "000 000", + "login.code.placeholder.totp": "000000", "login.code.text.email": "E-posta adresiniz kayıtlıysa, istenen kod e-posta yoluyla gönderilmiştir.", + "login.code.text.totp": "Lütfen kimlik doğrulayıcı uygulamanızdaki tek seferlik kodu girin.", "login.email.login.body": "Merhaba {user.nameOrEmail},\n\nKısa süre önce {site} Panel'i için bir giriş kodu istediniz.\nAşağıdaki giriş kodu {timeout} dakika boyunca geçerli olacaktır:\n\n{code}\n\nBir giriş kodu istemediyseniz, lütfen bu e-postayı dikkate almayın veya sorularınız varsa yöneticinize başvurun.\nGüvenliğiniz için lütfen bu e-postayı İLETMEYİN.", "login.email.login.subject": "Giriş kodunuz", "login.email.password-reset.body": "Merhaba {user.nameOrEmail},\n\nKısa süre önce {site} Panel'i için bir şifre sıfırlama kodu istediniz.\nAşağıdaki şifre sıfırlama kodu {timeout} dakika boyunca geçerli olacaktır:\n\n{code}\n\nŞifre sıfırlama kodu istemediyseniz, lütfen bu e-postayı dikkate almayın veya sorularınız varsa yöneticinizle iletişime geçin.\nGüvenliğiniz için lütfen bu e-postayı İLETMEYİN.", @@ -388,9 +520,24 @@ "login.toggleText.code.email-password": "Şifre ile giriş yapın", "login.toggleText.password-reset.email": "Şifrenizi mi unuttunuz?", "login.toggleText.password-reset.email-password": "← Girişe geri dön", + "login.totp.enable.option": "Tek seferlik kodlar ayarlama", + "login.totp.enable.intro": "Kimlik doğrulayıcı uygulamalar, hesabınızda oturum açarken ikinci bir faktör olarak kullanılan tek seferlik kodlar oluşturabilir.", + "login.totp.enable.qr.label": "1. Bu QR kodunu tarayın", + "login.totp.enable.qr.help": "Tarama yapılamıyor mu? Kurulum anahtarını {secret} kimlik doğrulayıcı uygulamanıza elle ekleyin.", + "login.totp.enable.confirm.headline": "2. Oluşturulan kod ile onaylayın", + "login.totp.enable.confirm.text": "Uygulamanız her 30 saniyede bir yeni bir kerelik kod oluşturur. Kurulumu tamamlamak için geçerli kodu girin:", + "login.totp.enable.confirm.label": "Geçerli kod", + "login.totp.enable.confirm.help": "Bu kurulumdan sonra, her oturum açtığınızda sizden tek seferlik bir kod isteyeceğiz.", + "login.totp.enable.success": "Tek seferlik kodlar etkinleştirildi", + "login.totp.disable.option": "Tek seferlik kodları devre dışı bırakma", + "login.totp.disable.label": "Tek seferlik kodları devre dışı bırakmak için şifrenizi girin", + "login.totp.disable.help": "Gelecekte, oturum açtığınızda e-posta yoluyla gönderilen bir oturum açma kodu gibi farklı bir ikinci faktör istenecektir. Tek seferlik kodları daha sonra her zaman yeniden ayarlayabilirsiniz.", + "login.totp.disable.admin": "

Bu {user} için tek seferlik kodları devre dışı bırakacaktır.

Gelecekte, oturum açtıklarında e-posta yoluyla gönderilen bir oturum açma kodu gibi farklı bir ikinci faktör istenecektir. {user} bir sonraki girişinden sonra tek seferlik kodları tekrar ayarlayabilir.

", + "login.totp.disable.success": "Tek seferlik kodlar devre dışı", "logout": "Oturumu kapat", + "merge": "Birleştir", "menu": "Menü", "meridiem": "AM/PM", "mime": "Medya Türü", @@ -398,48 +545,55 @@ "month": "Ay", "months.april": "Nisan", - "months.august": "Ağustos", - "months.december": "Aralık", + "months.august": "A\u011fustos", + "months.december": "Aral\u0131k", "months.february": "Şubat", "months.january": "Ocak", "months.july": "Temmuz", "months.june": "Haziran", "months.march": "Mart", - "months.may": "Mayıs", - "months.november": "Kasım", + "months.may": "May\u0131s", + "months.november": "Kas\u0131m", "months.october": "Ekim", - "months.september": "Eylül", + "months.september": "Eyl\u00fcl", "more": "Daha Fazla", + "move": "Taşı", "name": "İsim", "next": "Sonraki", + "night": "Gece", "no": "hayır", "off": "kapalı", "on": "açık", "open": "Önizleme", "open.newWindow": "Yeni pencerede aç", + "option": "Seçenek", "options": "Seçenekler", "options.none": "Seçenek yok", + "options.all": "Tüm {count} seçeneklerini göster", "orientation": "Oryantasyon", "orientation.landscape": "Yatay", "orientation.portrait": "Dikey", "orientation.square": "Kare", + "page": "Sayfa", "page.blueprint": "Bu dosyanın henüz bir planı yok. Kurulumu /site/blueprints/pages/{blueprint}.yml dosyasında tanımlayabilirsiniz.", "page.changeSlug": "Web Adresini Değiştir", - "page.changeSlug.fromTitle": "Başlıktan oluştur", + "page.changeSlug.fromTitle": "Ba\u015fl\u0131ktan olu\u015ftur", "page.changeStatus": "Durumu değiştir", "page.changeStatus.position": "Lütfen bir pozisyon seçin", "page.changeStatus.select": "Yeni bir durum seçin", "page.changeTemplate": "Şablonu değiştir", + "page.changeTemplate.notice": "Sayfanın şablonunu değiştirmek, tür olarak eşleşmeyen alanların içeriğini kaldıracaktır. Dikkatli kullanın.", + "page.create": "{status} olarak oluştur", "page.delete.confirm": "{title} sayfasını silmek istediğinizden emin misiniz?", "page.delete.confirm.subpages": "Bu sayfada alt sayfalar var.
Tüm alt sayfalar da silinecek.", "page.delete.confirm.title": "Onaylamak için sayfa başlığını girin", - "page.draft.create": "Taslak oluştur", "page.duplicate.appendix": "Kopya", "page.duplicate.files": "Dosyaları kopyala", "page.duplicate.pages": "Sayfaları kopyala", + "page.move": "Sayfayı taşı", "page.sort": "Pozisyon değiştir", "page.status": "Durum", "page.status.draft": "Taslak", @@ -450,6 +604,7 @@ "page.status.unlisted.description": "Bu sayfa sadece bağlantı adresi ile erişilebilir", "pages": "Sayfalar", + "pages.delete.confirm.selected": "Seçili sayfaları gerçekten silmek istiyor musunuz? Bu işlem geri alınamaz.", "pages.empty": "Henüz sayfa yok", "pages.status.draft": "Taslaklar", "pages.status.listed": "Yayınlandı", @@ -457,19 +612,26 @@ "pagination.page": "Sayfa", - "password": "Şifre", + "password": "\u015eifre", "paste": "Yapıştır", "paste.after": "Sonrasına yapıştır", + "paste.success": "{count} yapıştırıldı!", "pixel": "Piksel", "plugin": "Eklenti", "plugins": "Eklentiler", "prev": "Önceki", "preview": "Önizle", + + "publish": "Yayınla", + "published": "Yayınlandı", + "remove": "Kaldır", "rename": "Yeniden Adlandır", - "replace": "Değiştir", + "renew": "Yenileme", + "replace": "De\u011fi\u015ftir", + "replace.with": "Değiştir", "retry": "Tekrar Dene", - "revert": "Vazgeç", + "revert": "Vazge\u00e7", "revert.confirm": "Gerçekten kaydedilmemiş tüm değişiklikleri silmek istiyor musunuz?", "role": "Rol", @@ -482,11 +644,14 @@ "role.nobody.title": "Hiçkimse", "save": "Kaydet", + "saved": "Kaydedildi", "search": "Arama", + "searching": "Arama", "search.min": "Aramak için {min} karakter girin", - "search.all": "Tümünü göster", + "search.all": "Tüm {count} sonuçlarını göster", "search.results.none": "Sonuç yok", + "section.invalid": "Bu bölüm geçersizdir", "section.required": "Bölüm gereklidir", "security": "Güvenlik", @@ -498,16 +663,25 @@ "size": "Boyut", "slug": "Web Adres Uzantısı", "sort": "Sırala", + "sort.drag": "Sıralamak için sürükleyin …", + "split": "Ayır", "stats.empty": "Rapor yok", + "status": "Durum", + + "system.info.copy": "Bilgileri kopyala", + "system.info.copied": "Sistem bilgisi kopyalandı", "system.issues.content": "İçerik klasörü açığa çıkmış görünüyor", "system.issues.eol.kirby": "Yüklü Kirby sürümünüz kullanım ömrünün sonuna ulaştı ve daha fazla güvenlik güncellemesi almayacak", "system.issues.eol.plugin": "{ plugin } eklentisinin yüklü sürümü kullanım ömrünün sonuna ulaştı ve daha fazla güvenlik güncellemesi almayacak", + "system.issues.eol.php": "Yüklü PHP sürümünüz { release } kullanım ömrünün sonuna ulaşmıştır ve başka güvenlik güncellemeleri almayacaktır", "system.issues.debug": "Canlı modda hata ayıklama kapatılmalıdır", "system.issues.git": ".git klasörü açığa çıkmış görünüyor", "system.issues.https": "Tüm siteleriniz için HTTPS'yi öneriyoruz", "system.issues.kirby": "Kirby klasörü açığa çıkmış görünüyor", + "system.issues.local": "Site rahat güvenlik kontrolleri ile yerel olarak çalışıyor", "system.issues.site": "Site klasörü açığa çıkmış görünüyor", + "system.issues.vue.compiler": "Vue şablon derleyicisi etkinleştirildi", "system.issues.vulnerability.kirby": "Kurulumunuz aşağıdaki güvenlik açığından ({ severity } önem derecesi) etkilenebilir: { description }", "system.issues.vulnerability.plugin": "Kurulumunuz, { plugin } eklentisindeki ({ severity } önem derecesi) aşağıdaki güvenlik açığından etkilenebilir: { description }", "system.updateStatus": "Güncelleme durumu", @@ -520,13 +694,22 @@ "system.updateStatus.update": "Ücretsiz güncelleme { version } mevcut", "system.updateStatus.upgrade": "{ version } yükseltme mevcut", + "tel": "Telefon", + "tel.placeholder": "+49123456789", + "template": "\u015eablon", + + "theme": "Tema", + "theme.light": "Açık mod", + "theme.dark": "Koyu mod", + "theme.automatic": "Sistem varsayılanı", + "title": "Başlık", - "template": "Şablon", "today": "Bugün", + "toolbar.button.clear": "Biçimlendirmeyi temizle", "toolbar.button.code": "Kod", "toolbar.button.bold": "Kalın Yazı", - "toolbar.button.email": "E-posta", + "toolbar.button.email": "E-Posta", "toolbar.button.headings": "Başlıklar", "toolbar.button.heading.1": "Başlık 1", "toolbar.button.heading.2": "Başlık 2", @@ -538,18 +721,22 @@ "toolbar.button.file": "Dosya", "toolbar.button.file.select": "Bir dosya seçin", "toolbar.button.file.upload": "Bir dosya yükleyin", - "toolbar.button.link": "Bağlantı", + "toolbar.button.link": "Ba\u011flant\u0131", "toolbar.button.paragraph": "Paragraf", "toolbar.button.strike": "Üstü çizili", + "toolbar.button.sub": "Alt simge", + "toolbar.button.sup": "Üst simge", "toolbar.button.ol": "Sıralı liste", "toolbar.button.underline": "Altı çizili", "toolbar.button.ul": "Madde listesi", "translation.author": "Kirby Takımı", "translation.direction": "ltr", - "translation.name": "Türkçe", + "translation.name": "T\u00fcrk\u00e7e", "translation.locale": "tr_TR", + "type": "Tür", + "upload": "Yükle", "upload.error.cantMove": "Yüklenen dosya taşınamadı", "upload.error.cantWrite": "Dosya diske yazılamadı", @@ -574,6 +761,7 @@ "user.changeLanguage": "Dili değiştir", "user.changeName": "Kullanıcıyı yeniden adlandır", "user.changePassword": "Şifre değiştir", + "user.changePassword.current": "Mevcut şifreniz", "user.changePassword.new": "Yeni Şifre", "user.changePassword.new.confirm": "Şifreyi onaylayın...", "user.changeRole": "Rolü değiştir", @@ -585,17 +773,20 @@ "users": "Kullanıcılar", "version": "Versiyon", + "version.changes": "Değiştirilen sürüm", + "version.compare": "Sürümleri karşılaştırın", "version.current": "Mevcut sürüm", "version.latest": "En son sürüm", "versionInformation": "Sürüm bilgisi", - "view.account": "Hesabın", + "view": "Görüntüle", + "view.account": "Hesap Bilgilerin", "view.installation": "Kurulum", "view.languages": "Diller", "view.resetPassword": "Şifreyi sıfırla", "view.site": "Site", "view.system": "Sistem", - "view.users": "Kullanıcılar", + "view.users": "Kullan\u0131c\u0131lar", "welcome": "Hoşgeldiniz", "year": "Yıl", diff --git a/kirby/panel/dist/apple-touch-icon-dark.png b/kirby/panel/dist/apple-touch-icon-dark.png new file mode 100644 index 0000000..321598b Binary files /dev/null and b/kirby/panel/dist/apple-touch-icon-dark.png differ diff --git a/kirby/panel/dist/css/style.css b/kirby/panel/dist/css/style.css deleted file mode 100644 index f47b6c2..0000000 --- a/kirby/panel/dist/css/style.css +++ /dev/null @@ -1 +0,0 @@ -.k-dialog{position:relative;background:var(--color-background);width:100%;box-shadow:var(--shadow-lg);border-radius:var(--rounded-md);line-height:1;max-height:calc(100vh - 3rem);margin:1.5rem;display:flex;flex-direction:column}@media screen and (min-width:20rem){.k-dialog[data-size=small]{width:20rem}}@media screen and (min-width:22rem){.k-dialog[data-size=default]{width:22rem}}@media screen and (min-width:30rem){.k-dialog[data-size=medium]{width:30rem}}@media screen and (min-width:40rem){.k-dialog[data-size=large]{width:40rem}}[dir=ltr] .k-dialog-notification,[dir=rtl] .k-dialog-notification{border-top-right-radius:var(--rounded);border-top-left-radius:var(--rounded)}.k-dialog-notification{padding:.75rem 1.5rem;background:var(--color-gray-900);width:100%;margin-top:-1px;line-height:1.25rem;color:var(--color-white);display:flex;flex-shrink:0;align-items:center}.k-dialog-notification[data-theme]{background:var(--theme-light);color:var(--color-black)}.k-dialog-notification p{flex-grow:1;word-wrap:break-word;overflow:hidden}[dir=ltr] .k-dialog-notification .k-button{margin-left:1rem}[dir=rtl] .k-dialog-notification .k-button{margin-right:1rem}.k-dialog-notification .k-button{display:flex}.k-dialog-body{padding:1.5rem}.k-dialog-body .k-fieldset{padding-bottom:.5rem}.k-dialog-footer{padding:0;border-top:1px solid var(--color-gray-300);line-height:1;flex-shrink:0}.k-dialog-footer .k-button-group{display:flex;margin:0;justify-content:space-between}.k-dialog-footer .k-button-group .k-button{padding:.75rem 1rem;line-height:1.25rem}[dir=ltr] .k-dialog-footer .k-button-group .k-button:first-child{text-align:left}[dir=rtl] .k-dialog-footer .k-button-group .k-button:first-child{text-align:right}[dir=ltr] .k-dialog-footer .k-button-group .k-button:first-child{padding-left:1.5rem}[dir=rtl] .k-dialog-footer .k-button-group .k-button:first-child{padding-right:1.5rem}[dir=ltr] .k-dialog-footer .k-button-group .k-button:last-child{text-align:right}[dir=rtl] .k-dialog-footer .k-button-group .k-button:last-child{text-align:left}[dir=ltr] .k-dialog-footer .k-button-group .k-button:last-child{padding-right:1.5rem}[dir=rtl] .k-dialog-footer .k-button-group .k-button:last-child{padding-left:1.5rem}.k-dialog .k-pagination{margin-bottom:-1.5rem;display:flex;justify-content:center;align-items:center}.k-dialog-search{margin-bottom:.75rem}.k-dialog-search.k-input{background:rgba(0,0,0,.075);padding:0 1rem;height:36px;border-radius:var(--rounded)}.k-error-details{background:var(--color-white);display:block;overflow:auto;padding:1rem;font-size:var(--text-sm);line-height:1.25em;margin-top:.75rem}.k-error-details dt{color:var(--color-negative-light);margin-bottom:.25rem}.k-error-details dd{overflow:hidden;overflow-wrap:break-word;text-overflow:ellipsis}.k-error-details dd:not(:last-of-type){margin-bottom:1.5em}.k-error-details li{white-space:pre-line}.k-error-details li:not(:last-child){border-bottom:1px solid var(--color-background);padding-bottom:.25rem;margin-bottom:.25rem}.k-files-dialog .k-list-item{cursor:pointer}[dir=ltr] .k-pages-dialog-navbar{padding-right:38px}[dir=rtl] .k-pages-dialog-navbar{padding-left:38px}.k-pages-dialog-navbar{display:flex;align-items:center;justify-content:center;margin-bottom:.5rem}.k-pages-dialog-navbar .k-button{width:38px}.k-pages-dialog-navbar .k-button[disabled]{opacity:0}.k-pages-dialog-navbar .k-headline{flex-grow:1;text-align:center}.k-pages-dialog .k-list-item{cursor:pointer}.k-pages-dialog .k-list-item .k-button[data-theme=disabled],.k-pages-dialog .k-list-item .k-button[disabled]{opacity:.25}.k-pages-dialog .k-list-item .k-button[data-theme=disabled]:hover{opacity:1}.k-users-dialog .k-list-item{cursor:pointer}.k-drawer{--drawer-header-height:2.5rem;--drawer-header-padding:1.5rem;position:fixed;top:0;right:0;bottom:0;left:0;z-index:var(--z-toolbar);display:flex;align-items:stretch;justify-content:flex-end;background:rgba(0,0,0,.2)}.k-drawer-box{position:relative;flex-basis:50rem;display:flex;flex-direction:column;background:var(--color-background);box-shadow:var(--shadow-xl)}[dir=ltr] .k-drawer-header{padding-left:var(--drawer-header-padding)}[dir=rtl] .k-drawer-header{padding-right:var(--drawer-header-padding)}.k-drawer-header{flex-shrink:0;height:var(--drawer-header-height);display:flex;align-items:center;line-height:1;justify-content:space-between;background:var(--color-white);font-size:var(--text-sm)}.k-drawer-title{padding:0 .75rem}[dir=ltr] .k-drawer-breadcrumb,[dir=ltr] .k-drawer-title{margin-left:-.75rem}[dir=rtl] .k-drawer-breadcrumb,[dir=rtl] .k-drawer-title{margin-right:-.75rem}.k-drawer-breadcrumb,.k-drawer-title{display:flex;flex-grow:1;align-items:center;min-width:0;font-size:var(--text-sm);font-weight:var(--font-normal)}[dir=ltr] .k-drawer-breadcrumb li:not(:last-child) .k-button:after{right:-.75rem}[dir=rtl] .k-drawer-breadcrumb li:not(:last-child) .k-button:after{left:-.75rem}.k-drawer-breadcrumb li:not(:last-child) .k-button:after{position:absolute;width:1.5rem;display:inline-flex;justify-content:center;align-items:center;content:"›";color:var(--color-gray-500);height:var(--drawer-header-height)}[dir=ltr] .k-drawer-breadcrumb .k-icon,[dir=ltr] .k-drawer-title .k-icon{margin-right:.5rem}[dir=rtl] .k-drawer-breadcrumb .k-icon,[dir=rtl] .k-drawer-title .k-icon{margin-left:.5rem}.k-drawer-breadcrumb .k-icon,.k-drawer-title .k-icon{width:1rem;color:var(--color-gray-500)}.k-drawer-breadcrumb .k-button{display:inline-flex;align-items:center;height:var(--drawer-header-height);padding-left:.75rem;padding-right:.75rem}.k-drawer-breadcrumb .k-button-text{opacity:1}[dir=ltr] .k-drawer-breadcrumb .k-button .k-button-icon~.k-button-text{padding-left:0}[dir=rtl] .k-drawer-breadcrumb .k-button .k-button-icon~.k-button-text{padding-right:0}[dir=ltr] .k-drawer-tabs{margin-right:.75rem}[dir=rtl] .k-drawer-tabs{margin-left:.75rem}.k-drawer-tabs{display:flex;align-items:center;line-height:1}.k-drawer-tab.k-button{height:var(--drawer-header-height);padding-left:.75rem;padding-right:.75rem;display:flex;align-items:center;font-size:var(--text-xs)}.k-drawer-tab.k-button[aria-current]:after{position:absolute;bottom:-1px;left:.75rem;right:.75rem;content:"";background:var(--color-black);height:2px}[dir=ltr] .k-drawer-options{padding-right:.75rem}[dir=rtl] .k-drawer-options{padding-left:.75rem}.k-drawer-option.k-button{width:var(--drawer-header-height);height:var(--drawer-header-height);color:var(--color-gray-500);line-height:1}.k-drawer-option.k-button:focus,.k-drawer-option.k-button:hover{color:var(--color-black)}.k-drawer-body{padding:1.5rem;flex-grow:1;background:var(--color-background)}.k-drawer[data-nested=true]{background:0 0}.k-drawer-body .k-table th,.k-drawer-body .k-textarea-input:focus-within .k-toolbar{top:-1.5rem}.k-calendar-input{--cell-padding:.25rem .5rem;padding:.5rem;color:var(--color-light);border-radius:var(--rounded-xs)}.k-calendar-table{table-layout:fixed;width:100%;min-width:15rem;padding-top:.5rem}.k-calendar-input>nav{display:flex;direction:ltr}.k-calendar-input>nav .k-button{padding:.5rem}.k-calendar-selects{flex-grow:1;display:flex;align-items:center;justify-content:center}[dir=ltr] .k-calendar-selects{direction:ltr}[dir=rtl] .k-calendar-selects{direction:rtl}.k-calendar-selects .k-select-input{padding:0 .5rem;font-weight:var(--font-normal);font-size:var(--text-sm)}.k-calendar-selects .k-select-input:focus-within{color:var(--color-focus-light)!important}.k-calendar-input th{padding:.5rem 0;color:var(--color-gray-500);font-size:var(--text-xs);font-weight:400;text-align:center}.k-calendar-day .k-button{width:2rem;height:2rem;margin-left:auto;margin-right:auto;color:var(--color-white);line-height:1.75rem;display:flex;justify-content:center;border-radius:50%;border:2px solid transparent}.k-calendar-day .k-button .k-button-text{opacity:1}.k-calendar-table .k-button:hover{color:var(--color-white)}.k-calendar-day:hover .k-button:not([data-disabled=true]){border-color:#ffffff40}.k-calendar-day[aria-current=date] .k-button{text-decoration:underline}.k-calendar-day[aria-selected=date] .k-button{border-color:currentColor;font-weight:600;color:var(--color-focus-light)}.k-calendar-today{text-align:center;padding-top:.5rem}.k-calendar-today .k-button{font-size:var(--text-xs);padding:1rem;text-decoration:underline}.k-calendar-today .k-button-text{opacity:1;vertical-align:baseline}.k-counter{font-size:var(--text-xs);color:var(--color-gray-900);font-weight:var(--font-bold)}.k-counter[data-invalid=true]{box-shadow:none;border:0;color:var(--color-negative)}[dir=ltr] .k-counter-rules{padding-left:.5rem}[dir=rtl] .k-counter-rules{padding-right:.5rem}.k-counter-rules{color:var(--color-gray-600);font-weight:var(--font-normal)}.k-form-submitter{display:none}.k-form-buttons[data-theme]{background:var(--theme-light)}.k-form-buttons .k-view{display:flex;justify-content:space-between;align-items:center}.k-form-button.k-button{font-weight:500;white-space:nowrap;line-height:1;height:2.5rem;display:flex;padding:0 1rem;align-items:center}[dir=ltr] .k-form-button:first-child{margin-left:-1rem}[dir=rtl] .k-form-button:first-child{margin-right:-1rem}[dir=ltr] .k-form-button:last-child{margin-right:-1rem}[dir=rtl] .k-form-button:last-child{margin-left:-1rem}[dir=ltr] .k-form-lock-info{margin-right:3rem}[dir=rtl] .k-form-lock-info{margin-left:3rem}.k-form-lock-info{display:flex;font-size:var(--text-sm);align-items:center;line-height:1.5em;padding:.625rem 0}[dir=ltr] .k-form-lock-info>.k-icon{margin-right:.5rem}[dir=rtl] .k-form-lock-info>.k-icon{margin-left:.5rem}.k-form-lock-buttons{display:flex;flex-shrink:0}.k-form-lock-loader{animation:Spin 4s linear infinite}.k-form-lock-loader .k-icon-loader{display:flex}.k-form-indicator-toggle{color:var(--color-notice-light)}.k-form-indicator-info{font-size:var(--text-sm);font-weight:var(--font-bold);padding:.75rem 1rem .25rem;line-height:1.25em;width:15rem}.k-field-label{font-weight:var(--font-bold);display:block;padding:0 0 .75rem;flex-grow:1;line-height:1.25rem}[dir=ltr] .k-field-label abbr{padding-left:.25rem}[dir=rtl] .k-field-label abbr{padding-right:.25rem}.k-field-label abbr{text-decoration:none;color:var(--color-gray-500)}.k-field-header{position:relative;display:flex;align-items:baseline}[dir=ltr] .k-field-options{right:0}[dir=rtl] .k-field-options{left:0}.k-field-options{position:absolute;top:calc(-.5rem - 1px)}.k-field-options.k-button-group .k-dropdown{height:auto}.k-field-options.k-button-group .k-field-options-button.k-button{padding:.75rem;display:flex}.k-field[data-disabled=true]{cursor:not-allowed}.k-field[data-disabled=true] *{pointer-events:none}.k-field[data-disabled=true] .k-text[data-theme=help] *{pointer-events:initial}.k-field:focus-within>.k-field-header>.k-field-counter{display:block}.k-field-help{padding-top:.5rem}.k-field-add-item-button{display:flex;align-items:center;width:100%;color:var(--color-gray-500);justify-content:center;padding:.75rem 0}.k-field-add-item-button:hover{color:var(--color-black)}.k-fieldset{border:0}.k-fieldset .k-grid{grid-row-gap:2.25rem}@media screen and (min-width:30em){.k-fieldset .k-grid{grid-column-gap:1.5rem}}.k-sections>.k-column[data-width="1/3"] .k-fieldset .k-grid,.k-sections>.k-column[data-width="1/4"] .k-fieldset .k-grid{grid-template-columns:repeat(1,1fr)}.k-sections>.k-column[data-width="1/3"] .k-fieldset .k-grid .k-column,.k-sections>.k-column[data-width="1/4"] .k-fieldset .k-grid .k-column{grid-column-start:initial}.k-input{display:flex;align-items:center;line-height:1;border:0;outline:0;background:0 0}.k-input-element{flex-grow:1}.k-input-icon{display:flex;justify-content:center;align-items:center;line-height:0}.k-input[data-disabled=true]{pointer-events:none}[data-disabled=true] .k-input-icon{color:var(--color-gray-600)}.k-input[data-theme=field]{line-height:1;border:var(--field-input-border);background:var(--field-input-background);border-radius:var(--rounded)}.k-input[data-theme=field]:focus-within{border:var(--field-input-focus-border);box-shadow:var(--color-focus-outline) 0 0 0 2px}.k-input[data-theme=field][data-disabled=true]{background:var(--color-background)}.k-input[data-theme=field] .k-input-icon{width:var(--field-input-height);align-self:stretch;display:flex;align-items:center;flex-shrink:0}.k-input[data-theme=field] .k-input-after,.k-input[data-theme=field] .k-input-before{align-self:stretch;display:flex;align-items:center;flex-shrink:0;padding:0 var(--field-input-padding)}[dir=ltr] .k-input[data-theme=field] .k-input-before{padding-right:0}[dir=ltr] .k-input[data-theme=field] .k-input-after,[dir=rtl] .k-input[data-theme=field] .k-input-before{padding-left:0}.k-input[data-theme=field] .k-input-before{color:var(--field-input-color-before)}[dir=rtl] .k-input[data-theme=field] .k-input-after{padding-right:0}.k-input[data-theme=field] .k-input-after{color:var(--field-input-color-after)}.k-input[data-theme=field] .k-input-icon>.k-dropdown{width:100%;height:100%}.k-input[data-theme=field] .k-input-icon-button{width:100%;height:100%;display:flex;align-items:center;justify-content:center;flex-shrink:0}.k-input[data-theme=field] .k-number-input,.k-input[data-theme=field] .k-select-input,.k-input[data-theme=field] .k-text-input{padding:var(--field-input-padding);line-height:var(--field-input-line-height);border-radius:var(--rounded)}.k-input[data-theme=field] .k-date-input .k-select-input,.k-input[data-theme=field] .k-time-input .k-select-input{padding-left:0;padding-right:0}[dir=ltr] .k-input[data-theme=field] .k-date-input .k-select-input:first-child,[dir=ltr] .k-input[data-theme=field] .k-time-input .k-select-input:first-child{padding-left:var(--field-input-padding)}[dir=rtl] .k-input[data-theme=field] .k-date-input .k-select-input:first-child,[dir=rtl] .k-input[data-theme=field] .k-time-input .k-select-input:first-child{padding-right:var(--field-input-padding)}.k-input[data-theme=field] .k-date-input .k-select-input:focus-within,.k-input[data-theme=field] .k-time-input .k-select-input:focus-within{color:var(--color-focus);font-weight:var(--font-bold)}[dir=ltr] .k-input[data-theme=field].k-time-input .k-time-input-meridiem{padding-left:var(--field-input-padding)}[dir=rtl] .k-input[data-theme=field].k-time-input .k-time-input-meridiem{padding-right:var(--field-input-padding)}.k-input[data-theme=field][data-type=checkboxes] .k-checkboxes-input li,.k-input[data-theme=field][data-type=checkboxes] .k-radio-input li,.k-input[data-theme=field][data-type=radio] .k-checkboxes-input li,.k-input[data-theme=field][data-type=radio] .k-radio-input li{min-width:0;overflow-wrap:break-word}[dir=ltr] .k-input[data-theme=field][data-type=checkboxes] .k-input-before{border-right:1px solid var(--color-background)}[dir=ltr] .k-input[data-theme=field][data-type=checkboxes] .k-input-element+.k-input-after,[dir=ltr] .k-input[data-theme=field][data-type=checkboxes] .k-input-element+.k-input-icon,[dir=rtl] .k-input[data-theme=field][data-type=checkboxes] .k-input-before{border-left:1px solid var(--color-background)}[dir=ltr] .k-input[data-theme=field][data-type=checkboxes] .k-checkboxes-input li,[dir=rtl] .k-input[data-theme=field][data-type=checkboxes] .k-input-element+.k-input-after,[dir=rtl] .k-input[data-theme=field][data-type=checkboxes] .k-input-element+.k-input-icon{border-right:1px solid var(--color-background)}.k-input[data-theme=field][data-type=checkboxes] .k-input-element{overflow:hidden}[dir=ltr] .k-input[data-theme=field][data-type=checkboxes] .k-checkboxes-input{margin-right:-1px}[dir=rtl] .k-input[data-theme=field][data-type=checkboxes] .k-checkboxes-input{margin-left:-1px}.k-input[data-theme=field][data-type=checkboxes] .k-checkboxes-input{display:grid;grid-template-columns:1fr;margin-bottom:-1px}@media screen and (min-width:65em){.k-input[data-theme=field][data-type=checkboxes] .k-checkboxes-input{grid-template-columns:repeat(var(--columns),1fr)}}[dir=rtl] .k-input[data-theme=field][data-type=checkboxes] .k-checkboxes-input li{border-left:1px solid var(--color-background)}.k-input[data-theme=field][data-type=checkboxes] .k-checkboxes-input li,.k-input[data-theme=field][data-type=radio] .k-radio-input li{border-bottom:1px solid var(--color-background)}.k-input[data-theme=field][data-type=checkboxes] .k-checkboxes-input label{display:block;line-height:var(--field-input-line-height);padding:var(--field-input-padding) var(--field-input-padding)}[dir=ltr] .k-input[data-theme=field][data-type=checkboxes] .k-checkbox-input-icon,[dir=ltr] .k-input[data-theme=field][data-type=radio] .k-radio-input label:before{left:var(--field-input-padding)}[dir=rtl] .k-input[data-theme=field][data-type=checkboxes] .k-checkbox-input-icon,[dir=rtl] .k-input[data-theme=field][data-type=radio] .k-radio-input label:before{right:var(--field-input-padding)}.k-input[data-theme=field][data-type=checkboxes] .k-checkbox-input-icon{top:calc((var(--field-input-height) - var(--field-input-font-size))/2);margin-top:0}[dir=ltr] .k-input[data-theme=field][data-type=radio] .k-input-before{border-right:1px solid var(--color-background)}[dir=ltr] .k-input[data-theme=field][data-type=radio] .k-input-element+.k-input-after,[dir=ltr] .k-input[data-theme=field][data-type=radio] .k-input-element+.k-input-icon,[dir=rtl] .k-input[data-theme=field][data-type=radio] .k-input-before{border-left:1px solid var(--color-background)}[dir=ltr] .k-input[data-theme=field][data-type=radio] .k-radio-input li,[dir=rtl] .k-input[data-theme=field][data-type=radio] .k-input-element+.k-input-after,[dir=rtl] .k-input[data-theme=field][data-type=radio] .k-input-element+.k-input-icon{border-right:1px solid var(--color-background)}.k-input[data-theme=field][data-type=radio] .k-input-element{overflow:hidden}[dir=ltr] .k-input[data-theme=field][data-type=radio] .k-radio-input{margin-right:-1px}[dir=rtl] .k-input[data-theme=field][data-type=radio] .k-radio-input{margin-left:-1px}.k-input[data-theme=field][data-type=radio] .k-radio-input{display:grid;grid-template-columns:1fr;margin-bottom:-1px}@media screen and (min-width:65em){.k-input[data-theme=field][data-type=radio] .k-radio-input{grid-template-columns:repeat(var(--columns),1fr)}}[dir=rtl] .k-input[data-theme=field][data-type=radio] .k-radio-input li{border-left:1px solid var(--color-background)}.k-input[data-theme=field][data-type=radio] .k-radio-input label{display:block;flex-grow:1;min-height:var(--field-input-height);line-height:var(--field-input-line-height);padding:calc((var(--field-input-height) - var(--field-input-line-height))/2) var(--field-input-padding)}.k-input[data-theme=field][data-type=radio] .k-radio-input label:before{top:calc((var(--field-input-height) - 1rem)/2);margin-top:-1px}.k-input[data-theme=field][data-type=radio] .k-radio-input .k-radio-input-info{display:block;font-size:var(--text-sm);color:var(--color-gray-600);line-height:var(--field-input-line-height);padding-top:calc(var(--field-input-line-height)/10)}.k-input[data-theme=field][data-type=radio] .k-radio-input .k-icon{width:var(--field-input-height);height:var(--field-input-height);display:flex;align-items:center;justify-content:center}.k-input[data-theme=field][data-type=range] .k-range-input{padding:var(--field-input-padding)}.k-input[data-theme=field][data-type=multiselect],.k-input[data-theme=field][data-type=select]{position:relative}[dir=ltr] .k-input[data-theme=field][data-type=select] .k-input-icon{right:0}[dir=rtl] .k-input[data-theme=field][data-type=select] .k-input-icon{left:0}.k-input[data-theme=field][data-type=select] .k-input-icon{position:absolute;top:0;bottom:0}.k-input[data-theme=field][data-type=tags] .k-tags-input{padding:.25rem .25rem 0}[dir=ltr] .k-input[data-theme=field][data-type=tags] .k-tag{margin-right:.25rem}[dir=rtl] .k-input[data-theme=field][data-type=tags] .k-tag{margin-left:.25rem}.k-input[data-theme=field][data-type=tags] .k-tag{margin-bottom:.25rem;height:auto;min-height:1.75rem;font-size:var(--text-sm)}.k-input[data-theme=field][data-type=tags] .k-tags-input input{font-size:var(--text-sm);padding:0 .25rem;height:1.75rem;line-height:1;margin-bottom:.25rem}.k-input[data-theme=field][data-type=tags] .k-tags-input .k-dropdown-content{top:calc(100% + .5rem + 2px)}.k-input[data-theme=field][data-type=tags] .k-tags-input .k-dropdown-content[data-dropup]{top:calc(100% + .5rem + 2px);bottom:initial;margin-bottom:initial}.k-input[data-theme=field][data-type=multiselect] .k-multiselect-input{padding:.25rem 2rem 0 .25rem;min-height:2.25rem}[dir=ltr] .k-input[data-theme=field][data-type=multiselect] .k-tag{margin-right:.25rem}[dir=rtl] .k-input[data-theme=field][data-type=multiselect] .k-tag{margin-left:.25rem}.k-input[data-theme=field][data-type=multiselect] .k-tag{margin-bottom:.25rem;height:1.75rem;font-size:var(--text-sm)}[dir=ltr] .k-input[data-theme=field][data-type=multiselect] .k-input-icon{right:0}[dir=rtl] .k-input[data-theme=field][data-type=multiselect] .k-input-icon{left:0}.k-input[data-theme=field][data-type=multiselect] .k-input-icon{position:absolute;top:0;bottom:0;pointer-events:none}.k-input[data-theme=field][data-type=textarea] .k-textarea-input-native{padding:.25rem var(--field-input-padding);line-height:1.5rem}[dir=ltr] .k-input[data-theme=field][data-type=toggle] .k-input-before{padding-right:calc(var(--field-input-padding)/2)}[dir=rtl] .k-input[data-theme=field][data-type=toggle] .k-input-before{padding-left:calc(var(--field-input-padding)/2)}[dir=ltr] .k-input[data-theme=field][data-type=toggle] .k-toggle-input{padding-left:var(--field-input-padding)}[dir=rtl] .k-input[data-theme=field][data-type=toggle] .k-toggle-input{padding-right:var(--field-input-padding)}.k-input[data-theme=field][data-type=toggle] .k-toggle-input-label{padding:0 var(--field-input-padding)0 .75rem;line-height:var(--field-input-height)}.k-login-code-form .k-user-info{height:38px;margin-bottom:2.25rem;padding:.5rem;background:var(--color-white);border-radius:var(--rounded-xs);box-shadow:var(--shadow)}.k-times{padding:var(--spacing-4) var(--spacing-6);display:grid;line-height:1;grid-template-columns:1fr 1fr;grid-gap:var(--spacing-6)}.k-times .k-icon{width:1rem;margin-bottom:var(--spacing-2)}.k-times-slot .k-button{padding:var(--spacing-1) var(--spacing-3) var(--spacing-1)0;font-variant-numeric:tabular-nums;white-space:nowrap}.k-times .k-times-slot hr{position:relative;opacity:1;margin:var(--spacing-2)0;border:0;height:1px;top:1px;background:var(--color-dark)}[dir=ltr] .k-upload input{left:-3000px}[dir=rtl] .k-upload input{right:-3000px}.k-upload input{position:absolute;top:0}.k-upload-dialog .k-headline{margin-bottom:.75rem}.k-upload-error-list,.k-upload-list{line-height:1.5em;font-size:var(--text-sm)}.k-upload-list-filename{color:var(--color-gray-600)}.k-upload-error-list li{padding:.75rem;background:var(--color-white);border-radius:var(--rounded-xs)}.k-upload-error-list li:not(:last-child){margin-bottom:2px}.k-upload-error-filename{color:var(--color-negative);font-weight:var(--font-bold)}.k-upload-error-message{color:var(--color-gray-600)}.k-writer-toolbar{position:absolute;display:flex;background:var(--color-black);height:30px;transform:translate(-50%) translateY(-.75rem);z-index:calc(var(--z-dropdown) + 1);box-shadow:var(--shadow);color:var(--color-white);border-radius:var(--rounded)}.k-writer-toolbar-button.k-button{display:flex;align-items:center;justify-content:center;height:30px;width:30px;font-size:var(--text-sm)!important;color:currentColor;line-height:1}.k-writer-toolbar-button.k-button:hover{background:rgba(255,255,255,.15)}.k-writer-toolbar-button.k-writer-toolbar-button-active{color:var(--color-blue-300)}.k-writer-toolbar-button.k-writer-toolbar-nodes{width:auto;padding:0 .75rem}[dir=ltr] .k-writer-toolbar .k-dropdown+.k-writer-toolbar-button{border-left:1px solid var(--color-gray-700)}[dir=rtl] .k-writer-toolbar .k-dropdown+.k-writer-toolbar-button{border-right:1px solid var(--color-gray-700)}[dir=ltr] .k-writer-toolbar-button.k-writer-toolbar-nodes:after{margin-left:.5rem}[dir=rtl] .k-writer-toolbar-button.k-writer-toolbar-nodes:after{margin-right:.5rem}.k-writer-toolbar-button.k-writer-toolbar-nodes:after{content:"";border-top:4px solid var(--color-white);border-left:4px solid transparent;border-right:4px solid transparent}.k-writer-toolbar .k-dropdown-content{color:var(--color-black);background:var(--color-white);margin-top:.5rem}.k-writer-toolbar .k-dropdown-content .k-dropdown-item[aria-current]{color:var(--color-focus);font-weight:500}.k-writer{position:relative;width:100%;grid-template-areas:"content";display:grid}.k-writer .ProseMirror{overflow-wrap:break-word;word-wrap:break-word;word-break:break-word;white-space:pre-wrap;font-variant-ligatures:none;line-height:inherit;grid-area:content}.k-writer .ProseMirror:focus{outline:0}.k-writer .ProseMirror *{caret-color:currentColor}.k-writer .ProseMirror a{color:var(--color-focus);text-decoration:underline}.k-writer .ProseMirror>:last-child{margin-bottom:0}.k-writer .ProseMirror h1,.k-writer .ProseMirror h2,.k-writer .ProseMirror h3,.k-writer .ProseMirror ol,.k-writer .ProseMirror p,.k-writer .ProseMirror ul{margin-bottom:.75rem}.k-writer .ProseMirror h1{font-size:var(--text-3xl);line-height:1.25em}.k-writer .ProseMirror h2{font-size:var(--text-2xl);line-height:1.25em}.k-writer .ProseMirror h3{font-size:var(--text-xl);line-height:1.25em}.k-writer .ProseMirror h1 strong,.k-writer .ProseMirror h2 strong,.k-writer .ProseMirror h3 strong{font-weight:700}.k-writer .ProseMirror strong{font-weight:600}.k-writer .ProseMirror code{position:relative;font-size:.925em;display:inline-block;line-height:1.325;padding:.05em .325em;background:var(--color-gray-300);border-radius:var(--rounded)}[dir=ltr] .k-writer .ProseMirror ol,[dir=ltr] .k-writer .ProseMirror ul{padding-left:1.75rem}[dir=rtl] .k-writer .ProseMirror ol,[dir=rtl] .k-writer .ProseMirror ul{padding-right:1.75rem}.k-writer .ProseMirror ul>li{list-style:disc}.k-writer .ProseMirror ul ul>li{list-style:circle}.k-writer .ProseMirror ul ul ul>li{list-style:square}.k-writer .ProseMirror ol>li{list-style:decimal}.k-writer .ProseMirror li>ol,.k-writer .ProseMirror li>p,.k-writer .ProseMirror li>ul{margin:0}.k-writer-code pre{-moz-tab-size:2;-o-tab-size:2;tab-size:2;font-size:var(--text-sm);line-height:2em;overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch;white-space:pre}.k-writer .ProseMirror code,.k-writer-code code{font-family:var(--font-mono)}.k-writer[data-placeholder][data-empty=true]:before{grid-area:content;content:attr(data-placeholder);line-height:inherit;color:var(--color-gray-500);pointer-events:none;white-space:pre-wrap;word-wrap:break-word}.k-login-alert{padding:.5rem .75rem;display:flex;justify-content:space-between;align-items:center;min-height:38px;margin-bottom:2rem;background:var(--color-negative);color:var(--color-white);font-size:var(--text-sm);border-radius:var(--rounded-xs);box-shadow:var(--shadow-lg);cursor:pointer}.k-structure-backdrop{position:absolute;top:0;right:0;bottom:0;left:0;z-index:2;height:100vh}.k-structure-form section{position:relative;z-index:3;border-radius:var(--rounded-xs);margin-bottom:1px;box-shadow:#1111110d 0 0 0 3px;border:1px solid var(--color-border);background:var(--color-background)}.k-structure-form-fields{padding:1.5rem 1.5rem 2rem}.k-structure-form-buttons{border-top:1px solid var(--color-border);display:flex;justify-content:space-between}.k-structure-form-buttons .k-pagination{display:none}@media screen and (min-width:65em){.k-structure-form-buttons .k-pagination{display:flex}}.k-structure-form-buttons .k-pagination>.k-button,.k-structure-form-buttons .k-pagination>span{padding:.875rem 1rem!important}.k-structure-form-cancel-button,.k-structure-form-submit-button{padding:.875rem 1.5rem;line-height:1rem;display:flex}[dir=ltr] .k-toolbar,[dir=rtl] .k-toolbar{border-top-right-radius:var(--rounded);border-top-left-radius:var(--rounded)}.k-toolbar{background:var(--color-white);border-bottom:1px solid var(--color-background);height:38px}.k-toolbar-wrapper{position:absolute;top:0;left:0;right:0;max-width:100%}.k-toolbar-buttons{display:flex}.k-toolbar-divider{width:1px;background:var(--color-background)}.k-toolbar-button{width:36px;height:36px}.k-toolbar-button:hover{background:rgba(239,239,239,.5)}.k-checkbox-input{position:relative;cursor:pointer}[dir=ltr] .k-checkbox-input-label{padding-left:1.75rem}[dir=rtl] .k-checkbox-input-label{padding-right:1.75rem}.k-checkbox-input-label{display:block}[dir=ltr] .k-checkbox-input-icon{left:0}[dir=rtl] .k-checkbox-input-icon{right:0}.k-checkbox-input-icon{position:absolute;width:1rem;height:1rem;border-radius:var(--rounded);border:2px solid var(--color-gray-500)}.k-checkbox-input-icon svg{position:absolute;width:12px;height:12px;display:none}.k-checkbox-input-icon path{stroke:var(--color-white)}.k-checkbox-input-native:checked+.k-checkbox-input-icon{border-color:var(--color-gray-900);background:var(--color-gray-900)}[data-disabled=true] .k-checkbox-input-native:checked+.k-checkbox-input-icon{border-color:var(--color-gray-600);background:var(--color-gray-600)}.k-checkbox-input-native:checked+.k-checkbox-input-icon svg{display:block}.k-checkbox-input-native:focus+.k-checkbox-input-icon{border-color:var(--color-blue-600)}.k-checkbox-input-native:focus:checked+.k-checkbox-input-icon{background:var(--color-focus)}.k-text-input{width:100%;border:0;background:0 0;font:inherit;color:inherit;font-variant-numeric:tabular-nums}.k-text-input::-moz-placeholder{color:var(--color-gray-500)}.k-text-input::placeholder{color:var(--color-gray-500)}.k-text-input:focus{outline:0}.k-text-input:invalid{box-shadow:none;outline:0}.k-list-input .ProseMirror{line-height:1.5em}.k-list-input .ProseMirror ol>li::marker{font-size:var(--text-sm);color:var(--color-gray-500)}.k-multiselect-input{display:flex;flex-wrap:wrap;position:relative;font-size:var(--text-sm);min-height:2.25rem;line-height:1}.k-multiselect-input .k-sortable-ghost{background:var(--color-focus)}.k-multiselect-input .k-tag{border-radius:var(--rounded-sm)}.k-multiselect-input .k-dropdown-content,.k-multiselect-input[data-layout=list] .k-tag{width:100%}.k-multiselect-search{margin-top:0!important;color:var(--color-white);background:var(--color-gray-900);border-bottom:1px dashed rgba(255,255,255,.2)}.k-multiselect-search>.k-button-text{flex:1;opacity:1!important}.k-multiselect-search input{width:100%;color:var(--color-white);background:0 0;border:0;outline:0;padding:.25rem 0;font:inherit}.k-multiselect-options{position:relative;max-height:275px;padding:.5rem 0}.k-multiselect-option{position:relative}.k-multiselect-option.selected{color:var(--color-positive-light)}.k-multiselect-option.disabled:not(.selected) .k-icon{opacity:0}.k-multiselect-option b{color:var(--color-focus-light);font-weight:700}[dir=ltr] .k-multiselect-input[data-layout=list] .k-tag{margin-right:0!important}[dir=rtl] .k-multiselect-input[data-layout=list] .k-tag{margin-left:0!important}.k-multiselect-more{width:100%;padding:.75rem;color:#fffc;text-align:center;border-top:1px dashed rgba(255,255,255,.2)}.k-multiselect-more:hover{color:var(--color-white)}.k-number-input{width:100%;border:0;background:0 0;font:inherit;color:inherit}.k-number-input::-moz-placeholder{color:var(--color-gray-500)}.k-number-input::placeholder{color:var(--color-gray-500)}.k-number-input:focus{outline:0}.k-number-input:invalid{box-shadow:none;outline:0}[dir=ltr] .k-radio-input li{padding-left:1.75rem}[dir=rtl] .k-radio-input li{padding-right:1.75rem}.k-radio-input li{position:relative;line-height:1.5rem}.k-radio-input input{position:absolute;width:0;height:0;-webkit-appearance:none;-moz-appearance:none;appearance:none;opacity:0}.k-radio-input label{cursor:pointer;align-items:center}[dir=ltr] .k-radio-input label:before{left:0}[dir=rtl] .k-radio-input label:before{right:0}.k-radio-input label:before{position:absolute;top:.175em;content:"";width:1rem;height:1rem;border-radius:50%;border:2px solid var(--color-gray-500);box-shadow:var(--color-white) 0 0 0 2px inset}.k-radio-input input:checked+label:before{border-color:var(--color-gray-900);background:var(--color-gray-900)}[data-disabled=true] .k-radio-input input:checked+label:before{border-color:var(--color-gray-600);background:var(--color-gray-600)}.k-radio-input input:focus+label:before{border-color:var(--color-blue-600)}.k-radio-input input:focus:checked+label:before{background:var(--color-focus)}.k-radio-input-text{display:block}.k-range-input{--range-thumb-size:16px;--range-thumb-border:4px solid var(--color-gray-900);--range-thumb-border-disabled:4px solid var(--color-gray-600);--range-thumb-background:var(--color-background);--range-thumb-focus-border:4px solid var(--color-focus);--range-thumb-focus-background:var(--color-background);--range-track-height:4px;--range-track-background:var(--color-border);--range-track-color:var(--color-gray-900);--range-track-color-disabled:var(--color-gray-600);--range-track-focus-color:var(--color-focus);display:flex;align-items:center}.k-range-input-native{--min:0;--max:100;--value:0;--range:calc(var(--max) - var(--min));--ratio:calc((var(--value) - var(--min)) / var(--range));--position:calc( .5 * var(--range-thumb-size) + var(--ratio) * calc(100% - var(--range-thumb-size)) );-webkit-appearance:none;-moz-appearance:none;appearance:none;width:100%;height:var(--range-thumb-size);background:0 0;font-size:var(--text-sm);line-height:1}.k-range-input-native::-webkit-slider-thumb{-webkit-appearance:none;appearance:none}.k-range-input-native::-webkit-slider-runnable-track{border:0;border-radius:var(--range-track-height);width:100%;height:var(--range-track-height);background:var(--range-track-background)}.k-range-input-native::-moz-range-track{border:0;border-radius:var(--range-track-height);width:100%;height:var(--range-track-height);background:var(--range-track-background)}.k-range-input-native::-ms-track{border:0;border-radius:var(--range-track-height);width:100%;height:var(--range-track-height);background:var(--range-track-background)}.k-range-input-native::-webkit-slider-runnable-track{background:linear-gradient(var(--range-track-color),var(--range-track-color))0/var(--position) 100%no-repeat var(--range-track-background)}.k-range-input-native::-moz-range-progress{height:var(--range-track-height);background:var(--range-track-color)}.k-range-input-native::-ms-fill-lower{height:var(--range-track-height);background:var(--range-track-color)}.k-range-input-native::-webkit-slider-thumb{margin-top:calc(.5*(var(--range-track-height) - var(--range-thumb-size)));box-sizing:border-box;width:var(--range-thumb-size);height:var(--range-thumb-size);background:var(--range-thumb-background);border:var(--range-thumb-border);border-radius:50%;cursor:pointer}.k-range-input-native::-moz-range-thumb{box-sizing:border-box;width:var(--range-thumb-size);height:var(--range-thumb-size);background:var(--range-thumb-background);border:var(--range-thumb-border);border-radius:50%;cursor:pointer}.k-range-input-native::-ms-thumb{box-sizing:border-box;width:var(--range-thumb-size);height:var(--range-thumb-size);background:var(--range-thumb-background);border:var(--range-thumb-border);border-radius:50%;cursor:pointer;margin-top:0}.k-range-input-native::-ms-tooltip{display:none}.k-range-input-native:focus{outline:0}.k-range-input-native:focus::-webkit-slider-runnable-track{border:0;border-radius:var(--range-track-height);width:100%;height:var(--range-track-height);background:var(--range-track-background);background:linear-gradient(var(--range-track-focus-color),var(--range-track-focus-color))0/var(--position) 100%no-repeat var(--range-track-background)}.k-range-input-native:focus::-moz-range-progress{height:var(--range-track-height);background:var(--range-track-focus-color)}.k-range-input-native:focus::-ms-fill-lower{height:var(--range-track-height);background:var(--range-track-focus-color)}.k-range-input-native:focus::-webkit-slider-thumb{background:var(--range-thumb-focus-background);border:var(--range-thumb-focus-border)}.k-range-input-native:focus::-moz-range-thumb{background:var(--range-thumb-focus-background);border:var(--range-thumb-focus-border)}.k-range-input-native:focus::-ms-thumb{background:var(--range-thumb-focus-background);border:var(--range-thumb-focus-border)}[dir=ltr] .k-range-input-tooltip{margin-left:1rem}[dir=rtl] .k-range-input-tooltip{margin-right:1rem}.k-range-input-tooltip{position:relative;max-width:20%;display:flex;align-items:center;color:var(--color-white);font-size:var(--text-xs);line-height:1;text-align:center;border-radius:var(--rounded-xs);background:var(--color-gray-900);padding:0 .25rem;white-space:nowrap}[dir=ltr] .k-range-input-tooltip:after{left:-5px}[dir=rtl] .k-range-input-tooltip:after{right:-5px}[dir=ltr] .k-range-input-tooltip:after{border-right:5px solid var(--color-gray-900)}[dir=rtl] .k-range-input-tooltip:after{border-left:5px solid var(--color-gray-900)}.k-range-input-tooltip:after{position:absolute;top:50%;width:0;height:0;transform:translateY(-50%);border-top:5px solid transparent;border-bottom:5px solid transparent;content:""}.k-range-input-tooltip>*{padding:4px}[data-disabled=true] .k-range-input-native::-webkit-slider-runnable-track{background:linear-gradient(var(--range-track-color-disabled),var(--range-track-color-disabled))0/var(--position) 100%no-repeat var(--range-track-background)}[data-disabled=true] .k-range-input-native::-moz-range-progress{height:var(--range-track-height);background:var(--range-track-color-disabled)}[data-disabled=true] .k-range-input-native::-ms-fill-lower{height:var(--range-track-height);background:var(--range-track-color-disabled)}[data-disabled=true] .k-range-input-native::-webkit-slider-thumb{border:var(--range-thumb-border-disabled)}[data-disabled=true] .k-range-input-native::-moz-range-thumb{border:var(--range-thumb-border-disabled)}[data-disabled=true] .k-range-input-native::-ms-thumb{border:var(--range-thumb-border-disabled)}[data-disabled=true] .k-range-input-tooltip{background:var(--color-gray-600)}[dir=ltr] [data-disabled=true] .k-range-input-tooltip:after{border-right:5px solid var(--color-gray-600)}[dir=rtl] [data-disabled=true] .k-range-input-tooltip:after{border-left:5px solid var(--color-gray-600)}.k-select-input{position:relative;display:block;cursor:pointer;overflow:hidden}.k-select-input-native{position:absolute;top:0;right:0;bottom:0;left:0;opacity:0;width:100%;font:inherit;z-index:1;cursor:pointer;-webkit-appearance:none;-moz-appearance:none;appearance:none;font-weight:var(--font-normal)}.k-select-input-native[disabled]{cursor:default}.k-tags-input{display:flex;flex-wrap:wrap}.k-tags-input .k-tag{border-radius:var(--rounded-sm)}.k-tags-input .k-sortable-ghost{background:var(--color-focus)}.k-tags-input-element{flex-grow:1;flex-basis:0;min-width:0}.k-tags-input:focus-within .k-tags-input-element{flex-basis:4rem}.k-tags-input-element input{font:inherit;border:0;width:100%;background:0 0}.k-tags-input-element input:focus{outline:0}[dir=ltr] .k-tags-input[data-layout=list] .k-tag{margin-right:0!important}[dir=rtl] .k-tags-input[data-layout=list] .k-tag{margin-left:0!important}.k-tags-input[data-layout=list] .k-tag{width:100%}.k-textarea-input[data-size=small]{--size:7.5rem}.k-textarea-input[data-size=medium]{--size:15rem}.k-textarea-input[data-size=large]{--size:30rem}.k-textarea-input[data-size=huge]{--size:45rem}.k-textarea-input-wrapper{position:relative}.k-textarea-input-native{resize:none;border:0;width:100%;background:0 0;font:inherit;line-height:1.5em;color:inherit;min-height:var(--size)}.k-textarea-input-native::-moz-placeholder{color:var(--color-gray-500)}.k-textarea-input-native::placeholder{color:var(--color-gray-500)}.k-textarea-input-native:focus{outline:0}.k-textarea-input-native:invalid{box-shadow:none;outline:0}.k-textarea-input-native[data-font=monospace]{font-family:var(--font-mono)}.k-toolbar{margin-bottom:.25rem;color:#aaa}.k-textarea-input:focus-within .k-toolbar{position:sticky;top:0;left:0;right:0;z-index:1;box-shadow:#0000000d 0 2px 5px;border-bottom:1px solid rgba(0,0,0,.1);color:#000}.k-toggle-input{--toggle-background:var(--color-white);--toggle-color:var(--color-gray-500);--toggle-active-color:var(--color-gray-900);--toggle-focus-color:var(--color-focus);--toggle-height:16px;display:flex;align-items:center}.k-toggle-input-native{position:relative;height:var(--toggle-height);width:calc(var(--toggle-height)*2);border-radius:var(--toggle-height);border:2px solid var(--toggle-color);box-shadow:inset 0 0 0 2px var(--toggle-background),inset calc(var(--toggle-height)*-1) 0 0 2px var(--toggle-background);background-color:var(--toggle-color);outline:0;transition:all ease-in-out .1s;-webkit-appearance:none;-moz-appearance:none;appearance:none;cursor:pointer;flex-shrink:0}.k-toggle-input-native:checked{border-color:var(--toggle-active-color);box-shadow:inset 0 0 0 2px var(--toggle-background),inset var(--toggle-height) 0 0 2px var(--toggle-background);background-color:var(--toggle-active-color)}.k-toggle-input-native[disabled]{border-color:var(--color-border);box-shadow:inset 0 0 0 2px var(--color-background),inset calc(var(--toggle-height)*-1) 0 0 2px var(--color-background);background-color:var(--color-border)}.k-toggle-input-native[disabled]:checked{box-shadow:inset 0 0 0 2px var(--color-background),inset var(--toggle-height) 0 0 2px var(--color-background)}.k-toggle-input-native:focus:checked{border:2px solid var(--color-focus);background-color:var(--toggle-focus-color)}.k-toggle-input-native::-ms-check{opacity:0}.k-toggle-input-label{cursor:pointer;flex-grow:1}.k-input[data-type=toggles]{display:inline-flex}.k-input[data-type=toggles].grow{display:flex}.k-toggles-input{display:grid;grid-template-columns:repeat(var(--options),minmax(0,1fr));gap:1px;border-radius:var(--rounded);line-height:1;background:var(--color-border);overflow:hidden}.k-toggles-input li{height:var(--field-input-height);background:var(--color-white)}.k-toggles-input label{align-items:center;background:var(--color-white);cursor:pointer;display:flex;font-size:var(--text-sm);justify-content:center;line-height:1.25;padding:0 var(--spacing-3);height:100%}[dir=ltr] .k-toggles-input .k-icon+.k-toggles-text{margin-left:var(--spacing-2)}[dir=rtl] .k-toggles-input .k-icon+.k-toggles-text{margin-right:var(--spacing-2)}.k-toggles-input input:focus:not(:checked)+label{background:var(--color-gray-200)}.k-toggles-input input:checked+label{background:var(--color-black);color:var(--color-white)}.k-blocks-field{position:relative}.k-date-field-body{display:flex;flex-wrap:wrap;line-height:1;border:var(--field-input-border);background:var(--color-gray-300);gap:1px;border-radius:var(--rounded);--multiplier:calc(25rem - 100%)}.k-date-field-body:focus-within{border:var(--field-input-focus-border);box-shadow:var(--color-focus-outline) 0 0 0 2px}.k-date-field[data-disabled] .k-date-field-body{background:0 0}.k-date-field-body>.k-input[data-theme=field]{border:0;box-shadow:none;border-radius:var(--rounded)}.k-date-field-body>.k-input[data-invalid=true],.k-date-field-body>.k-input[data-invalid=true]:focus-within{border:0!important;box-shadow:none!important}.k-date-field-body>*{flex-grow:1;flex-basis:calc(var(--multiplier)*999);max-width:100%}.k-date-field-body .k-input[data-type=date]{min-width:60%}.k-date-field-body .k-input[data-type=time]{min-width:30%}.k-files-field[data-disabled=true] .k-item *{pointer-events:all!important}body{counter-reset:headline-counter}.k-headline-field{position:relative;padding-top:1.5rem}.k-fieldset>.k-grid .k-column:first-child .k-headline-field{padding-top:0}[dir=ltr] .k-headline-field .k-headline[data-numbered]:before{padding-right:.25rem}[dir=rtl] .k-headline-field .k-headline[data-numbered]:before{padding-left:.25rem}.k-headline-field .k-headline[data-numbered]:before{counter-increment:headline-counter;content:counter(headline-counter,decimal-leading-zero);color:var(--color-focus);font-weight:400}.k-info-field .k-headline{padding-bottom:.75rem;line-height:1.25rem}.k-layout-column{position:relative;height:100%;display:flex;flex-direction:column;background:var(--color-white);min-height:6rem}.k-layout-column:focus{outline:0}.k-layout-column .k-blocks{background:0 0;box-shadow:none;padding:0;height:100%;background:var(--color-white);min-height:4rem}.k-layout-column .k-blocks[data-empty=true]{min-height:6rem}.k-layout-column .k-blocks-list{display:flex;flex-direction:column;height:100%}.k-layout-column .k-blocks .k-block-container:last-of-type{flex-grow:1}.k-layout-column .k-blocks-empty{position:absolute;top:0;right:0;bottom:0;left:0;justify-content:center;opacity:0;transition:opacity .3s;border:0}.k-layout-column .k-blocks-empty:hover{opacity:1}[dir=ltr] .k-layout-column .k-blocks-empty.k-empty .k-icon{border-right:0}[dir=rtl] .k-layout-column .k-blocks-empty.k-empty .k-icon{border-left:0}.k-layout-column .k-blocks-empty.k-empty .k-icon{width:1rem}[dir=ltr] .k-layout{padding-right:var(--layout-toolbar-width)}[dir=rtl] .k-layout{padding-left:var(--layout-toolbar-width)}.k-layout{--layout-border-color:var(--color-gray-300);--layout-toolbar-width:2rem;position:relative;background:#fff;box-shadow:var(--shadow)}[dir=ltr] [data-disabled=true] .k-layout{padding-right:0}[dir=rtl] [data-disabled=true] .k-layout{padding-left:0}.k-layout:not(:last-of-type){margin-bottom:1px}.k-layout:focus{outline:0}section.k-layout{z-index:auto;border:unset}[dir=ltr] .k-layout-toolbar{right:0}[dir=rtl] .k-layout-toolbar{left:0}[dir=ltr] .k-layout-toolbar{border-left:1px solid var(--color-light)}[dir=rtl] .k-layout-toolbar{border-right:1px solid var(--color-light)}.k-layout-toolbar{position:absolute;top:0;bottom:0;width:var(--layout-toolbar-width);display:flex;flex-direction:column;font-size:var(--text-sm);background:var(--color-gray-100);color:var(--color-gray-500)}.k-layout-toolbar:hover{color:var(--color-black)}.k-layout-toolbar-button{width:var(--layout-toolbar-width);height:var(--layout-toolbar-width)}.k-layout-toolbar .k-sort-handle{margin-top:auto;color:currentColor}.k-layout-columns.k-grid{grid-gap:1px;background:var(--layout-border-color);background:var(--color-gray-300)}.k-layout:not(:first-child) .k-layout-columns.k-grid{border-top:0}.k-layouts .k-sortable-ghost{position:relative;box-shadow:#11111140 0 5px 10px;outline:2px solid var(--color-focus);cursor:grabbing;cursor:-webkit-grabbing;z-index:1}.k-layout-selector.k-dialog{background:#313740;color:var(--color-white)}.k-layout-selector .k-headline{line-height:1;margin-top:-.25rem;margin-bottom:1.5rem}.k-layout-selector ul{display:grid;grid-template-columns:repeat(3,1fr);grid-gap:1.5rem}.k-layout-selector-option .k-grid{height:5rem;grid-gap:2px;box-shadow:var(--shadow);cursor:pointer}.k-layout-selector-option:hover{outline:2px solid var(--color-green-300);outline-offset:2px}.k-layout-selector-option:last-child{margin-bottom:0}.k-layout-selector-option .k-column{display:flex;background:rgba(255,255,255,.2);justify-content:center;font-size:var(--text-xs);align-items:center}.k-line-field{position:relative;border:0;height:3rem;width:auto}.k-line-field:after{position:absolute;content:"";top:50%;margin-top:-1px;left:0;right:0;height:1px;background:var(--color-border)}.k-list-field .k-list-input{padding:.375rem .5rem .375rem .75rem}.k-table.k-object-field-table{table-layout:auto}.k-table.k-object-field-table tbody td,.k-table.k-object-field-table tbody th,.k-table.k-object-field-table tbody th button{cursor:pointer;overflow:hidden;text-overflow:ellipsis}.k-table.k-object-field-table tbody td{max-width:0}.k-pages-field[data-disabled=true] .k-item *{pointer-events:all!important}.k-structure-field:not([data-disabled=true]) td.k-table-column{cursor:pointer}.k-field-counter{display:none}.k-text-field:focus-within .k-field-counter{display:block}.k-users-field[data-disabled=true] .k-item *{pointer-events:all!important}.k-writer-field-input{line-height:1.5em;padding:.375rem .5rem}.k-aspect-ratio{position:relative;display:block;overflow:hidden;padding-bottom:100%}.k-aspect-ratio>*{position:absolute!important;top:0;right:0;bottom:0;left:0;height:100%;width:100%;-o-object-fit:contain;object-fit:contain}.k-aspect-ratio[data-cover=true]>*{-o-object-fit:cover;object-fit:cover}.k-bar{display:flex;align-items:center;justify-content:space-between;line-height:1}.k-bar-slot{flex-grow:1}.k-bar-slot[data-position=center]{text-align:center}[dir=ltr] .k-bar-slot[data-position=right]{text-align:right}[dir=rtl] .k-bar-slot[data-position=right]{text-align:left}.k-box{word-wrap:break-word;font-size:var(--text-sm)}.k-box:not([data-theme=none]){background:var(--color-white);border-radius:var(--rounded);line-height:1.25rem;padding:.5rem .75rem}.k-box[data-theme=code]{background:var(--color-gray-900);border:1px solid var(--color-black);color:var(--color-light);font-family:Input,Menlo,monospace;font-size:var(--text-sm);line-height:1.5}.k-box[data-theme=button]{padding:0}[dir=ltr] .k-box[data-theme=button] .k-button{text-align:left}[dir=rtl] .k-box[data-theme=button] .k-button{text-align:right}.k-box[data-theme=button] .k-button{padding:0 .75rem;height:2.25rem;width:100%;display:flex;align-items:center;line-height:2rem}[dir=ltr] .k-box[data-theme=info],[dir=ltr] .k-box[data-theme=negative],[dir=ltr] .k-box[data-theme=notice],[dir=ltr] .k-box[data-theme=positive]{border-left-color:var(--theme-light)}[dir=rtl] .k-box[data-theme=info],[dir=rtl] .k-box[data-theme=negative],[dir=rtl] .k-box[data-theme=notice],[dir=rtl] .k-box[data-theme=positive]{border-right-color:var(--theme-light)}.k-box[data-theme=info],.k-box[data-theme=negative],.k-box[data-theme=notice],.k-box[data-theme=positive]{border:0;background:var(--theme-bg)}[dir=ltr] .k-box[data-theme=empty]{border-left:0}[dir=rtl] .k-box[data-theme=empty]{border-right:0}.k-box[data-theme=empty]{text-align:center;padding:3rem 1.5rem;display:flex;justify-content:center;align-items:center;flex-direction:column;background:var(--color-background);border:1px dashed var(--color-border)}.k-box[data-theme=empty] .k-icon{margin-bottom:.5rem;color:var(--color-gray-500)}.k-box[data-theme=empty],.k-box[data-theme=empty] p{color:var(--color-gray-600)}.k-bubble{display:flex;padding:0 .5rem;white-space:nowrap;align-items:center;line-height:1.5;font-size:var(--text-xs);height:1.525rem;background:var(--color-light);color:var(--color-black);border-radius:var(--rounded);overflow:hidden}[dir=ltr] .k-bubble .k-item-figure{margin-left:-.5rem}[dir=rtl] .k-bubble .k-item-figure{margin-right:-.5rem}[dir=ltr] .k-bubble .k-item-figure{margin-right:var(--spacing-2)}[dir=rtl] .k-bubble .k-item-figure{margin-left:var(--spacing-2)}.k-bubble .k-item-figure{width:1.525rem;height:1.525rem}.k-bubbles{display:flex;gap:.25rem}.k-collection-help{padding:.5rem .75rem}.k-collection-footer{display:flex;justify-content:space-between;margin-left:-.75rem;margin-right:-.75rem}.k-collection-pagination{line-height:1.25rem;flex-shrink:0;min-height:2.75rem}.k-collection-pagination .k-pagination .k-button{padding:.5rem .75rem;line-height:1.125rem}.k-column{min-width:0;grid-column-start:span 12}.k-column[data-sticky=true]>div{position:sticky;top:4vh;z-index:2}@media screen and (min-width:65em){.k-column[data-width="1/1"],.k-column[data-width="12/12"],.k-column[data-width="2/2"],.k-column[data-width="3/3"],.k-column[data-width="4/4"],.k-column[data-width="6/6"]{grid-column-start:span 12}.k-column[data-width="11/12"]{grid-column-start:span 11}.k-column[data-width="10/12"],.k-column[data-width="5/6"]{grid-column-start:span 10}.k-column[data-width="3/4"],.k-column[data-width="9/12"]{grid-column-start:span 9}.k-column[data-width="2/3"],.k-column[data-width="4/6"],.k-column[data-width="8/12"]{grid-column-start:span 8}.k-column[data-width="7/12"]{grid-column-start:span 7}.k-column[data-width="1/2"],.k-column[data-width="2/4"],.k-column[data-width="3/6"],.k-column[data-width="6/12"]{grid-column-start:span 6}.k-column[data-width="5/12"]{grid-column-start:span 5}.k-column[data-width="1/3"],.k-column[data-width="2/6"],.k-column[data-width="4/12"]{grid-column-start:span 4}.k-column[data-width="1/4"],.k-column[data-width="3/12"]{grid-column-start:span 3}.k-column[data-width="1/6"],.k-column[data-width="2/12"]{grid-column-start:span 2}.k-column[data-width="1/12"]{grid-column-start:span 1}}.k-column[data-disabled=true]{cursor:not-allowed;opacity:.4}.k-column[data-disabled=true] *{pointer-events:none}.k-column[data-disabled=true] .k-text[data-theme=help] *{pointer-events:initial}.k-dropzone{position:relative}.k-dropzone:after{content:"";position:absolute;top:0;right:0;bottom:0;left:0;display:none;pointer-events:none;z-index:1}.k-dropzone[data-over=true]:after{display:block;outline:1px solid var(--color-focus);box-shadow:var(--color-focus-outline) 0 0 0 3px}.k-empty{display:flex;align-items:stretch;border-radius:var(--rounded);color:var(--color-gray-600);border:1px dashed var(--color-border)}button.k-empty{width:100%}button.k-empty:focus{outline:0}.k-empty p{font-size:var(--text-sm);color:var(--color-gray-600)}.k-empty>.k-icon{color:var(--color-gray-500)}.k-empty[data-layout=cardlets],.k-empty[data-layout=cards]{text-align:center;padding:1.5rem;justify-content:center;flex-direction:column}.k-empty[data-layout=cardlets] .k-icon,.k-empty[data-layout=cards] .k-icon{margin-bottom:1rem}.k-empty[data-layout=cardlets] .k-icon svg,.k-empty[data-layout=cards] .k-icon svg{width:2rem;height:2rem}.k-empty[data-layout=list],.k-empty[data-layout=table]{min-height:38px}[dir=ltr] .k-empty[data-layout=list]>.k-icon,[dir=ltr] .k-empty[data-layout=table]>.k-icon{border-right:1px solid rgba(0,0,0,.05)}[dir=rtl] .k-empty[data-layout=list]>.k-icon,[dir=rtl] .k-empty[data-layout=table]>.k-icon{border-left:1px solid rgba(0,0,0,.05)}.k-empty[data-layout=list]>.k-icon,.k-empty[data-layout=table]>.k-icon{width:36px;min-height:36px}.k-empty[data-layout=list]>p,.k-empty[data-layout=table]>p{line-height:1.25rem;padding:.5rem .75rem}.k-file-preview{background:var(--color-gray-800)}.k-file-preview-layout{display:grid;grid-template-columns:50%auto}.k-file-preview-layout>*{min-width:0}.k-file-preview-image{position:relative;display:flex;align-items:center;justify-content:center;background:var(--bg-pattern)}.k-file-preview-image-link{display:block;width:100%;padding:min(4vw,3rem);outline:0}.k-file-preview-image-link[data-tabbed=true]{box-shadow:none;outline:2px solid var(--color-focus);outline-offset:-2px}.k-file-preview-details{padding:1.5rem;flex-grow:1}.k-file-preview-details ul{line-height:1.5em;max-width:50rem;display:grid;grid-gap:1.5rem 3rem;grid-template-columns:repeat(auto-fill,minmax(100px,1fr))}.k-file-preview-details h3{font-size:var(--text-sm);font-weight:500;color:var(--color-gray-500)}.k-file-preview-details a,.k-file-preview-details p{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:#ffffffbf;font-size:var(--text-sm)}@media screen and (min-width:30em){.k-file-preview-details ul{grid-template-columns:repeat(auto-fill,minmax(200px,1fr))}}@media screen and (max-width:65em){.k-file-preview-layout{padding:0!important}}@media screen and (min-width:65em){.k-file-preview-layout{grid-template-columns:33.333%auto;align-items:center}.k-file-preview-details{padding:3rem}}@media screen and (min-width:90em){.k-file-preview-layout{grid-template-columns:25%auto}}.k-grid{--columns:12;display:grid;grid-column-gap:0;grid-row-gap:0;grid-template-columns:1fr}@media screen and (min-width:30em){.k-grid[data-gutter=small]{grid-column-gap:1rem;grid-row-gap:1rem}.k-grid[data-gutter=huge],.k-grid[data-gutter=large],.k-grid[data-gutter=medium]{grid-column-gap:1.5rem;grid-row-gap:1.5rem}}@media screen and (min-width:65em){.k-grid{grid-template-columns:repeat(var(--columns),1fr)}.k-grid[data-gutter=large]{grid-column-gap:3rem}.k-grid[data-gutter=huge]{grid-column-gap:4.5rem}}@media screen and (min-width:90em){.k-grid[data-gutter=large]{grid-column-gap:4.5rem}.k-grid[data-gutter=huge]{grid-column-gap:6rem}}@media screen and (min-width:120em){.k-grid[data-gutter=large]{grid-column-gap:6rem}.k-grid[data-gutter=huge]{grid-column-gap:7.5rem}}.k-header{padding-top:4vh;margin-bottom:2rem;border-bottom:1px solid var(--color-border)}.k-header[data-tabs=true]{border-bottom:0}.k-header .k-headline{min-height:1.25em;margin-bottom:.5rem;word-wrap:break-word}.k-header .k-header-buttons{margin-top:-.5rem;height:3.25rem}.k-header .k-headline-editable{cursor:pointer}[dir=ltr] .k-header .k-headline-editable .k-icon{margin-left:.5rem}[dir=rtl] .k-header .k-headline-editable .k-icon{margin-right:.5rem}.k-header .k-headline-editable .k-icon{color:var(--color-gray-500);opacity:0;transition:opacity .3s;display:inline-block}.k-header .k-headline-editable:hover .k-icon{opacity:1}.k-panel-inside{position:absolute;top:0;right:0;bottom:0;left:0;display:flex;flex-direction:column}.k-panel-inside:focus{outline:0}.k-panel-header{z-index:var(--z-navigation);flex-shrink:0}.k-panel-view{flex-grow:1;padding-bottom:6rem}.k-item{position:relative;background:var(--color-white);border-radius:var(--rounded);box-shadow:var(--shadow);display:grid;grid-template-columns:auto;line-height:1}.k-item a:focus,.k-item:focus{outline:0}.k-item.k-sortable-ghost,.k-item:focus-within{box-shadow:var(--shadow-outline)}.k-item-sort-handle.k-sort-handle{position:absolute;opacity:0;width:1.25rem;height:1.5rem;z-index:2;border-radius:1px}.k-item:hover .k-item-sort-handle{opacity:1}.k-item-figure{grid-area:figure}.k-item-content{grid-area:content;overflow:hidden}.k-item-info,.k-item-title{font-size:var(--text-sm);font-weight:400;text-overflow:ellipsis;white-space:nowrap;line-height:1.125rem;overflow:hidden}.k-item-info{grid-area:info;color:var(--color-gray-500)}.k-item-title-link.k-link[data-=true]{box-shadow:none}.k-item-title-link:after{position:absolute;content:"";top:0;right:0;bottom:0;left:0;z-index:1}.k-item-footer{grid-area:footer;display:flex;justify-content:space-between;align-items:center;min-width:0}[dir=ltr] .k-item-label{margin-right:.5rem}[dir=rtl] .k-item-label{margin-left:.5rem}.k-item-buttons{position:relative;display:flex;justify-content:flex-end;flex-shrink:0;flex-grow:1}.k-item-buttons>.k-button,.k-item-buttons>.k-dropdown{position:relative;width:38px;height:38px;display:flex!important;align-items:center;justify-content:center;line-height:1}.k-item-buttons>.k-button{z-index:1}.k-item-buttons>.k-options-dropdown>.k-options-dropdown-toggle{z-index:var(--z-toolbar)}.k-list-item{display:flex;align-items:center;height:38px}[dir=ltr] .k-list-item .k-item-sort-handle{left:-1.5rem}[dir=rtl] .k-list-item .k-item-sort-handle{right:-1.5rem}.k-list-item .k-item-sort-handle{width:1.5rem}[dir=ltr] .k-list-item .k-item-figure{border-top-left-radius:var(--rounded)}[dir=rtl] .k-list-item .k-item-figure{border-top-right-radius:var(--rounded)}[dir=ltr] .k-list-item .k-item-figure{border-bottom-left-radius:var(--rounded)}[dir=rtl] .k-list-item .k-item-figure{border-bottom-right-radius:var(--rounded)}.k-list-item .k-item-figure{width:38px}[dir=ltr] .k-list-item .k-item-content{margin-left:.75rem}[dir=rtl] .k-list-item .k-item-content{margin-right:.75rem}.k-list-item .k-item-content{display:flex;flex-grow:1;flex-shrink:2;justify-content:space-between;align-items:center}.k-list-item .k-item-info,.k-list-item .k-item-title{flex-grow:1;line-height:1.5rem}[dir=ltr] .k-list-item .k-item-title{margin-right:.5rem}[dir=rtl] .k-list-item .k-item-title{margin-left:.5rem}.k-list-item .k-item-title{flex-shrink:1}[dir=ltr] .k-list-item .k-item-info{text-align:right}[dir=rtl] .k-list-item .k-item-info{text-align:left}[dir=ltr] .k-list-item .k-item-info{margin-right:.5rem}[dir=rtl] .k-list-item .k-item-info{margin-left:.5rem}.k-list-item .k-item-info{flex-shrink:2;justify-self:end}.k-list-item .k-item-buttons,.k-list-item .k-item-footer{flex-shrink:0}.k-item:not(.k-list-item) .k-item-sort-handle{margin:var(--spacing-2);background:var(--color-background);box-shadow:var(--shadow-lg);border-radius:var(--rounded-sm)}[dir=ltr] .k-item:not(.k-list-item) .k-item-label{margin-left:-2px}[dir=rtl] .k-item:not(.k-list-item) .k-item-label{margin-right:-2px}.k-item:not(.k-list-item) .k-item-content{padding:.625rem .75rem}.k-cardlets-item{height:6rem;grid-template-rows:auto 38px;grid-template-areas:"content""footer"}.k-cardlets-item[data-has-figure=true]{grid-template-columns:6rem auto;grid-template-areas:"figure content""figure footer"}[dir=ltr] .k-cardlets-item .k-item-figure{border-top-left-radius:var(--rounded)}[dir=rtl] .k-cardlets-item .k-item-figure{border-top-right-radius:var(--rounded)}[dir=ltr] .k-cardlets-item .k-item-figure{border-bottom-left-radius:var(--rounded)}[dir=rtl] .k-cardlets-item .k-item-figure{border-bottom-right-radius:var(--rounded)}.k-cardlets-item .k-item-footer{padding-top:.5rem;padding-bottom:.5rem}.k-cards-item{grid-template-columns:auto;grid-template-rows:auto 1fr;grid-template-areas:"figure""content";--item-content-wrapper:0}[dir=ltr] .k-cards-item .k-item-figure,[dir=rtl] .k-cards-item .k-item-figure{border-top-right-radius:var(--rounded);border-top-left-radius:var(--rounded)}.k-cards-item .k-item-content{padding:.5rem .75rem!important;overflow:hidden}.k-cards-item .k-item-info,.k-cards-item .k-item-title{line-height:1.375rem;white-space:normal}.k-cards-item .k-item-info:after,.k-cards-item .k-item-title:after{display:inline-block;content:" ";width:var(--item-content-wrapper)}.k-cards-item[data-has-flag=true],.k-cards-item[data-has-options=true]{--item-content-wrapper:38px}.k-cards-item[data-has-flag=true][data-has-options=true]{--item-content-wrapper:76px}.k-cards-item[data-has-info=true] .k-item-title:after{display:none}[dir=ltr] .k-cards-item .k-item-footer{right:0}[dir=rtl] .k-cards-item .k-item-footer{left:0}.k-cards-item .k-item-footer{position:absolute;bottom:0;width:auto}.k-item-figure{overflow:hidden;flex-shrink:0}.k-cards-items{--min:13rem;--max:1fr;--gap:1.5rem;--column-gap:var(--gap);--row-gap:var(--gap);display:grid;grid-column-gap:var(--column-gap);grid-row-gap:var(--row-gap);grid-template-columns:repeat(auto-fill,minmax(var(--min),var(--max)))}@media screen and (min-width:30em){.k-cards-items[data-size=tiny]{--min:10rem}.k-cards-items[data-size=small]{--min:16rem}.k-cards-items[data-size=medium]{--min:24rem}.k-cards-items[data-size=huge],.k-cards-items[data-size=large],.k-column[data-width="1/4"] .k-cards-items,.k-column[data-width="1/5"] .k-cards-items,.k-column[data-width="1/6"] .k-cards-items{--min:1fr}}@media screen and (min-width:65em){.k-cards-items[data-size=large]{--min:32rem}}.k-cardlets-items{display:grid;grid-template-columns:repeat(auto-fill,minmax(16rem,1fr));grid-gap:.5rem}.k-list-items .k-list-item:not(:last-child){margin-bottom:2px}.k-overlay{position:fixed;top:0;right:0;bottom:0;left:0;width:100%;height:100%;z-index:var(--z-dialog);transform:translateZ(0)}.k-overlay[data-centered=true]{display:flex;align-items:center;justify-content:center}.k-overlay[data-dimmed=true]{background:var(--color-backdrop)}.k-overlay-loader{color:var(--color-white)}.k-panel[data-loading=true]{animation:LoadingCursor .5s}.k-panel[data-dragging=true],.k-panel[data-loading=true]:after{-webkit-user-select:none;-moz-user-select:none;user-select:none}.k-stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(14rem,1fr));grid-auto-rows:1fr;grid-gap:var(--spacing-2px)}.k-stat{display:flex;flex-direction:column;background:var(--color-white);box-shadow:var(--shadow);padding:var(--spacing-3) var(--spacing-6);line-height:var(--leading-normal);border-radius:var(--rounded)}.k-stat.k-link:hover{cursor:pointer;background:var(--color-gray-100)}.k-stat dd,.k-stat dt{display:block}.k-stat-value{font-size:var(--value);margin-bottom:var(--spacing-1);order:1}.k-stat-info,.k-stat-label{font-size:var(--text-xs)}.k-stat-label{order:2}.k-stat-info{order:3;color:var(--theme, var(--color-gray-500))}.k-stats[data-size=small]{--value:var(--text-base)}.k-stats[data-size=medium]{--value:var(--text-xl)}.k-stats[data-size=large]{--value:var(--text-2xl)}.k-stats[data-size=huge]{--value:var(--text-3xl)}.k-table{--table-row-height:38px;position:relative;background:var(--color-white);font-size:var(--text-sm);box-shadow:var(--shadow);border-radius:var(--rounded)}.k-table table{width:100%;border-spacing:0;table-layout:fixed;font-variant-numeric:tabular-nums}.k-table[data-invalid]{border:0;box-shadow:var(--color-negative-outline) 0 0 0 1px,var(--color-negative-outline) 0 0 3px 2px}.k-table td,.k-table th{height:var(--table-row-height);overflow:hidden;text-overflow:ellipsis;line-height:1.25em}.k-table td{width:100%}[dir=ltr] .k-table tbody tr:first-child th,[dir=ltr] .k-table thead th:first-child{border-top-left-radius:var(--rounded)}[dir=rtl] .k-table tbody tr:first-child th,[dir=rtl] .k-table thead th:first-child{border-top-right-radius:var(--rounded)}[dir=ltr] .k-table thead th:last-child{border-top-right-radius:var(--rounded)}[dir=rtl] .k-table thead th:last-child{border-top-left-radius:var(--rounded)}[dir=ltr] .k-table td:last-child,[dir=ltr] .k-table th:last-child{border-right:0}[dir=rtl] .k-table td:last-child,[dir=rtl] .k-table th:last-child{border-left:0}.k-table td:last-child,.k-table th:last-child{height:var(--table-row-height)}.k-table th,.k-table tr:not(:last-child) td{border-bottom:1px solid var(--color-background)}.k-table td:last-child{overflow:visible}[dir=ltr] .k-table td,[dir=ltr] .k-table th{border-right:1px solid var(--color-background)}[dir=rtl] .k-table td,[dir=rtl] .k-table th{border-left:1px solid var(--color-background)}[dir=ltr] .k-table td,[dir=ltr] .k-table th,[dir=ltr] .k-table th button{text-align:left}[dir=rtl] .k-table td,[dir=rtl] .k-table th,[dir=rtl] .k-table th button{text-align:right}.k-table th{padding:0 .75rem;font-family:var(--font-mono);font-size:var(--text-xs);font-weight:400;color:var(--color-gray-600);background:var(--color-gray-100);width:100%}.k-table th button{font:inherit;display:block;padding:0 .75rem;height:100%;width:100%;border-radius:var(--rounded)}.k-table th button:focus-visible{outline:2px solid var(--color-black);outline-offset:-2px}.k-table tbody tr:hover td{background:rgba(239,239,239,.25)}.k-table thead th{position:sticky;top:0;left:0;right:0;z-index:1}.k-table tbody th{width:auto;white-space:nowrap;padding:0;overflow:visible;border-radius:0}[dir=ltr] .k-table tbody tr:last-child th{border-bottom-left-radius:var(--rounded)}[dir=rtl] .k-table tbody tr:last-child th{border-bottom-right-radius:var(--rounded)}.k-table tbody tr:last-child th{border-bottom:0}.k-table-column[data-align]{text-align:var(--align)!important}.k-table-column[data-align=right]>.k-input{flex-direction:column;align-items:flex-end}.k-table .k-sort-handle,.k-table-index{display:grid;place-items:center;width:100%;height:var(--table-row-height)}.k-table .k-sort-handle,.k-table tr:hover .k-table-index-column[data-sortable=true] .k-table-index{display:none}.k-table tr:hover .k-sort-handle{display:grid!important}.k-table-row-ghost{background:var(--color-white);box-shadow:var(--shadow-outline);outline:2px solid var(--color-black);border-radius:var(--rounded);margin-bottom:2px;cursor:grabbing;cursor:-webkit-grabbing}.k-table-row-fallback{opacity:0!important}td.k-table-index-column,td.k-table-options-column,th.k-table-index-column,th.k-table-options-column{width:var(--table-row-height);text-align:center!important}.k-table-index{font-size:var(--text-xs);color:var(--color-gray-500);line-height:1.1em}.k-table-empty{color:var(--color-gray-600);font-size:var(--text-sm)}[data-disabled=true] .k-table{background:var(--color-gray-100)}[data-disabled=true] .k-table tbody td,[data-disabled=true] .k-table th{border-color:var(--color-gray-200)}[data-disabled=true] .k-table td:last-child{overflow:hidden;text-overflow:ellipsis}@media screen and (max-width:65em){.k-table tbody td:not([data-mobile]),.k-table thead th:not([data-mobile]){display:none}}[dir=ltr] .k-table-pagination.k-pagination,[dir=rtl] .k-table-pagination.k-pagination{border-bottom-right-radius:var(--rounded);border-bottom-left-radius:var(--rounded)}.k-table-pagination.k-pagination{border-top:1px solid var(--color-gray-200);background:var(--color-gray-100);display:flex;justify-content:space-between;align-items:center;height:var(--table-row-height)}.k-table-pagination.k-pagination .k-button{padding:0 .75rem;display:flex;align-items:center;line-height:1;height:var(--table-row-height)}.k-table-update-status-cell{padding:0 .75rem;display:flex;align-items:center;height:100%}.k-table-update-status-cell-button,.k-table-update-status-cell-version{font-variant-numeric:tabular-nums}.k-table-update-status-cell-button{display:inline-flex;padding:.25rem 1.5rem .25rem .325rem;border-radius:var(--rounded);line-height:1;align-items:center;background:var(--color-gray-200)}.k-table-update-status-cell-button .k-button-text:after{position:absolute;top:50%;right:.5rem;margin-top:-2px;content:"";border-top:4px solid #000;border-left:4px solid transparent;border-right:4px solid transparent}.k-table-update-status-cell-button .k-icon{color:var(--theme)}.k-plugin-info{padding:1rem}.k-plugin-info div+div{margin-top:.5rem}.k-plugin-info dt{color:var(--color-gray-400);margin-right:.5rem}.k-plugin-info dd[data-theme]{color:var(--theme-light)}.k-plugin-info+.k-dropdown-item{padding-top:.75rem;border-top:1px solid var(--color-gray-700)}@media screen and (min-width:30em){.k-plugin-info{width:20rem}.k-plugin-info div{display:flex}}.k-tabs{position:relative;background:#e9e9e9;border:1px solid var(--color-border);border-radius:var(--rounded)}.k-tabs nav{display:flex;justify-content:center;margin-left:-1px;margin-right:-1px}[dir=ltr] .k-tab-button.k-button{border-left:1px solid transparent}[dir=rtl] .k-tab-button.k-button{border-right:1px solid transparent}[dir=ltr] .k-tab-button.k-button{border-right:1px solid var(--color-border)}[dir=rtl] .k-tab-button.k-button{border-left:1px solid var(--color-border)}.k-tab-button.k-button{position:relative;z-index:1;display:inline-flex;justify-content:center;align-items:center;padding:.625rem .75rem;font-size:var(--text-xs);text-transform:uppercase;text-align:center;font-weight:500;flex-grow:1;flex-shrink:1;flex-direction:column;max-width:15rem}@media screen and (min-width:30em){.k-tab-button.k-button{flex-direction:row}[dir=ltr] .k-tab-button.k-button .k-icon{margin-right:.5rem}[dir=rtl] .k-tab-button.k-button .k-icon{margin-left:.5rem}}[dir=ltr] .k-tab-button.k-button>.k-button-text{padding-left:0}[dir=rtl] .k-tab-button.k-button>.k-button-text{padding-right:0}.k-tab-button.k-button>.k-button-text{padding-top:.375rem;font-size:10px;overflow:hidden;max-width:10rem;text-overflow:ellipsis;opacity:1}@media screen and (min-width:30em){.k-tab-button.k-button>.k-button-text{font-size:var(--text-xs);padding-top:0}}[dir=ltr] .k-tab-button:last-child{border-right:1px solid transparent}[dir=rtl] .k-tab-button:last-child{border-left:1px solid transparent}[dir=ltr] .k-tab-button[aria-current]{border-right:1px solid var(--color-border)}[dir=rtl] .k-tab-button[aria-current]{border-left:1px solid var(--color-border)}.k-tab-button[aria-current]{position:relative;background:var(--color-background);pointer-events:none}[dir=ltr] .k-tab-button[aria-current]:first-child{border-left:1px solid var(--color-border)}[dir=rtl] .k-tab-button[aria-current]:first-child{border-right:1px solid var(--color-border)}.k-tab-button[aria-current]:after,.k-tab-button[aria-current]:before{position:absolute;content:""}.k-tab-button[aria-current]:before{left:-1px;right:-1px;top:-1px;height:2px;background:var(--color-black)}.k-tab-button[aria-current]:after{left:0;right:0;bottom:-1px;height:1px;background:var(--color-background)}[dir=ltr] .k-tabs-dropdown{right:0}[dir=rtl] .k-tabs-dropdown{left:0}.k-tabs-dropdown{top:100%}[dir=ltr] .k-tabs-badge{right:2px}[dir=rtl] .k-tabs-badge{left:2px}.k-tabs-badge{position:absolute;top:3px;font-variant-numeric:tabular-nums;line-height:1.5;padding:0 .25rem;border-radius:2px;font-size:10px;box-shadow:var(--shadow-md)}.k-tabs[data-theme=notice] .k-tabs-badge{background:var(--theme-light);color:var(--color-black)}.k-view{padding-left:1.5rem;padding-right:1.5rem;margin:0 auto;max-width:100rem}@media screen and (min-width:30em){.k-view{padding-left:3rem;padding-right:3rem}}@media screen and (min-width:90em){.k-view{padding-left:6rem;padding-right:6rem}}.k-view[data-align=center]{height:100vh;display:flex;align-items:center;justify-content:center;padding:0 3rem;overflow:auto}.k-view[data-align=center]>*{flex-basis:22.5rem}.k-fatal{position:fixed;top:0;right:0;bottom:0;left:0;background:var(--color-backdrop);display:flex;z-index:var(--z-fatal);align-items:center;justify-content:center;padding:1.5rem}.k-fatal-box{width:100%;height:100%;display:flex;flex-direction:column;color:var(--color-black);background:var(--color-red-400);box-shadow:var(--shadow-xl);border-radius:var(--rounded)}.k-fatal-box .k-headline{line-height:1;font-size:var(--text-sm);padding:.75rem}.k-fatal-box .k-button{padding:.75rem}.k-fatal-iframe{border:0;width:100%;flex-grow:1;background:var(--color-white)}.k-headline{--size:var(--text-base);font-size:var(--size);font-weight:var(--font-bold);line-height:1.5em}.k-headline[data-size=small]{--size:var(--text-sm)}.k-headline[data-size=large]{--size:var(--text-xl);font-weight:var(--font-normal)}@media screen and (min-width:65em){.k-headline[data-size=large]{--size:var(--text-2xl)}}.k-headline[data-size=huge]{--size:var(--text-2xl);line-height:1.15em}@media screen and (min-width:65em){.k-headline[data-size=huge]{--size:var(--text-3xl)}}.k-headline[data-theme]{color:var(--theme)}[dir=ltr] .k-headline abbr{padding-left:.25rem}[dir=rtl] .k-headline abbr{padding-right:.25rem}.k-headline abbr{color:var(--color-gray-500);text-decoration:none}.k-icon{--size:1rem;position:relative;line-height:0;display:flex;align-items:center;justify-content:center;flex-shrink:0;font-size:var(--size)}.k-icon[data-size=medium]{--size:2rem}.k-icon[data-size=large]{--size:3rem}.k-icon svg{width:var(--size);height:var(--size);-moz-transform:scale(1)}.k-icon svg *{fill:currentColor}.k-icon[data-back=black]{color:var(--color-white)}.k-icon[data-back=white]{color:var(--color-gray-900)}.k-icon[data-back=pattern]{color:var(--color-white)}[data-disabled=true] .k-icon[data-back=pattern] svg{opacity:1}.k-icon-emoji{display:block;line-height:1;font-style:normal;font-size:var(--size)}@media not all,not all,not all,only screen and (min-resolution:192dpi),only screen and (min-resolution:2dppx){.k-icon-emoji{font-size:1.25em}}.k-icons{position:absolute;width:0;height:0}.k-image span{position:relative;display:block;line-height:0;padding-bottom:100%}.k-image img{position:absolute;top:0;right:0;bottom:0;left:0;width:100%;height:100%;-o-object-fit:contain;object-fit:contain}[dir=ltr] .k-image-error{left:50%}[dir=rtl] .k-image-error{right:50%}.k-image-error{position:absolute;top:50%;transform:translate(-50%,-50%);color:var(--color-white);font-size:.9em}.k-image-error svg *{fill:#ffffff4d}.k-image[data-cover=true] img{-o-object-fit:cover;object-fit:cover}.k-image[data-back=black] span{background:var(--color-gray-900)}.k-image[data-back=white] span{background:var(--color-white);color:var(--color-gray-900)}.k-image[data-back=white] .k-image-error{background:var(--color-gray-900);color:var(--color-white)}.k-image[data-back=pattern] span{background:var(--color-gray-800) var(--bg-pattern)}.k-loader{z-index:1}.k-loader-icon{animation:Spin .9s linear infinite}.k-offline-warning{position:fixed;top:0;right:0;bottom:0;left:0;z-index:var(--z-offline);background:var(--color-backdrop);display:flex;align-items:center;justify-content:center;line-height:1}.k-offline-warning p{display:flex;align-items:center;gap:.5rem;background:var(--color-white);box-shadow:var(--shadow);padding:.75rem;border-radius:var(--rounded)}.k-offline-warning p .k-icon{color:var(--color-red-400)}.k-progress{-webkit-appearance:none;width:100%;height:.5rem;border-radius:5rem;background:var(--color-border);overflow:hidden;border:0}.k-progress::-webkit-progress-bar{border:0;background:var(--color-border);height:.5rem;border-radius:20px}.k-progress::-webkit-progress-value{border-radius:inherit;background:var(--color-focus);-webkit-transition:width .3s;transition:width .3s}.k-progress::-moz-progress-bar{border-radius:inherit;background:var(--color-focus);-moz-transition:width .3s;transition:width .3s}[dir=ltr] .k-registration,[dir=ltr] .k-registration p{margin-right:1rem}[dir=rtl] .k-registration,[dir=rtl] .k-registration p{margin-left:1rem}.k-registration{display:flex;align-items:center}.k-registration p{color:var(--color-negative-light);font-size:var(--text-sm);font-weight:600}@media screen and (max-width:90em){.k-registration p{display:none}}.k-registration .k-button{color:var(--color-white)}.k-sort-handle{cursor:move;cursor:grab;cursor:-webkit-grab;color:var(--color-gray-900);justify-content:center;align-items:center;line-height:0;width:2rem;height:2rem;display:flex;will-change:opacity,color;transition:opacity .3s;z-index:1}.k-sort-handle svg{width:1rem;height:1rem}.k-sort-handle:active{cursor:grabbing;cursor:-webkit-grabbing}.k-status-icon svg{width:14px;height:14px}.k-status-icon .k-icon{color:var(--theme-light)}.k-status-icon .k-button-text{color:var(--color-black)}.k-status-icon[data-disabled=true]{opacity:1!important}.k-status-icon[data-disabled=true] .k-icon{color:var(--color-gray-400);opacity:.5}.k-text{line-height:1.5em}[dir=ltr] .k-text ol,[dir=ltr] .k-text ul{margin-left:1rem}[dir=rtl] .k-text ol,[dir=rtl] .k-text ul{margin-right:1rem}.k-text li{list-style:inherit}.k-text p,.k-text>ol,.k-text>ul{margin-bottom:1.5em}.k-text a{text-decoration:underline}.k-text>:last-child{margin-bottom:0}.k-text[data-size=tiny]{font-size:var(--text-xs)}.k-text[data-size=small]{font-size:var(--text-sm)}.k-text[data-size=medium]{font-size:var(--text-base)}.k-text[data-size=large]{font-size:var(--text-xl)}.k-text[data-align]{text-align:var(--align)}.k-text[data-theme=help]{font-size:var(--text-sm);color:var(--color-gray-600);line-height:1.25rem}.k-dialog-body .k-text{word-wrap:break-word}.k-user-info{display:flex;align-items:center;line-height:1;font-size:var(--text-sm)}[dir=ltr] .k-user-info .k-image{margin-right:.75rem}[dir=rtl] .k-user-info .k-image{margin-left:.75rem}.k-user-info .k-image{width:1.5rem}[dir=ltr] .k-user-info .k-icon{margin-right:.75rem}[dir=rtl] .k-user-info .k-icon{margin-left:.75rem}.k-user-info .k-icon{width:1.5rem;height:1.5rem;background:var(--color-black);color:var(--color-white)}.k-breadcrumb{padding-left:.5rem;padding-right:.5rem}.k-breadcrumb-dropdown{height:2.5rem;width:2.5rem;display:flex;align-items:center;justify-content:center}.k-breadcrumb ol{display:none;align-items:center}@media screen and (min-width:30em){.k-breadcrumb ol{display:flex}.k-breadcrumb-dropdown{display:none}}.k-breadcrumb li,.k-breadcrumb-link{display:flex;align-items:center;min-width:0}.k-breadcrumb-link{font-size:var(--text-sm);align-self:stretch;padding-top:.625rem;padding-bottom:.625rem;line-height:1.25rem}.k-breadcrumb li{flex-shrink:3}.k-breadcrumb li:last-child{flex-shrink:1}.k-breadcrumb li:not(:last-child):after{content:"/";padding-left:.5rem;padding-right:.5rem;opacity:.5;flex-shrink:0}.k-breadcrumb li:not(:first-child):not(:last-child){max-width:15vw}[dir=ltr] .k-breadcrumb-icon{margin-right:.5rem}[dir=rtl] .k-breadcrumb-icon{margin-left:.5rem}.k-breadcrumb-icon.k-loader{opacity:.5}.k-breadcrumb-link-text{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}button{line-height:inherit;border:0;font-family:var(--font-sans);font-size:1rem;color:currentColor;background:0 0;cursor:pointer}button::-moz-focus-inner{padding:0;border:0}.k-button{display:inline-block;position:relative;font-size:var(--text-sm);transition:color .3s;outline:0}.k-button:focus,.k-button:hover{outline:0}.k-button *{vertical-align:middle}.k-button[data-responsive=true] .k-button-text{display:none}@media screen and (min-width:30em){.k-button[data-responsive=true] .k-button-text{display:inline}}.k-button[data-theme]{color:var(--theme)}.k-button-icon{display:inline-flex;align-items:center;line-height:0}[dir=ltr] .k-button-icon~.k-button-text{padding-left:.5rem}[dir=rtl] .k-button-icon~.k-button-text{padding-right:.5rem}.k-button-text{opacity:.75}.k-button:focus .k-button-text,.k-button:hover .k-button-text{opacity:1}.k-button-text b,.k-button-text span{vertical-align:baseline}.k-button[data-disabled=true]{opacity:.5;pointer-events:none;cursor:default}.k-card-options>.k-button[data-disabled=true]{display:inline-flex}.k-button-group{--padding-x:.75rem;--padding-y:1rem;--line-height:1rem;font-size:0;margin:0 calc(var(--padding-x)*-1)}.k-button-group>.k-dropdown{height:calc(var(--line-height) + (var(--padding-y)*2));display:inline-block}.k-button-group>.k-button,.k-button-group>.k-dropdown>.k-button{padding:var(--padding-y) var(--padding-x);line-height:var(--line-height)}.k-button-group .k-dropdown-content{top:calc(100% + 1px);margin:0 var(--padding-x)}.k-dropdown{position:relative}[dir=ltr] .k-dropdown-content{text-align:left}[dir=rtl] .k-dropdown-content{text-align:right}.k-dropdown-content{position:absolute;top:100%;background:var(--color-black);color:var(--color-white);z-index:var(--z-dropdown);box-shadow:var(--shadow-lg);border-radius:var(--rounded);margin-bottom:6rem}[dir=ltr] .k-dropdown-content[data-align=left]{left:0}[dir=ltr] .k-dropdown-content[data-align=right],[dir=rtl] .k-dropdown-content[data-align=left]{right:0}[dir=rtl] .k-dropdown-content[data-align=right]{left:0}.k-dropdown-content>.k-dropdown-item:first-child{margin-top:.5rem}.k-dropdown-content>.k-dropdown-item:last-child{margin-bottom:.5rem}.k-dropdown-content[data-dropup=true]{top:auto;bottom:100%;margin-bottom:.5rem}.k-dropdown-content hr{border-color:currentColor;opacity:.2;margin:.5rem 1rem}.k-dropdown-content[data-theme=light]{background:var(--color-white);color:var(--color-black)}.k-dropdown-item{white-space:nowrap;line-height:1;display:flex;width:100%;align-items:center;font-size:var(--text-sm);padding:6px 16px}.k-dropdown-item:focus{outline:0;box-shadow:var(--shadow-outline)}[dir=ltr] .k-dropdown-item .k-button-figure{padding-right:.5rem}[dir=rtl] .k-dropdown-item .k-button-figure{padding-left:.5rem}.k-dropdown-item .k-button-figure{text-align:center}.k-link{outline:0}.k-options-dropdown,.k-options-dropdown-toggle{display:flex;justify-content:center;align-items:center;height:38px}.k-options-dropdown-toggle{min-width:38px;padding:0 .75rem}.k-pagination{-webkit-user-select:none;-moz-user-select:none;user-select:none;direction:ltr}.k-pagination .k-button{padding:1rem}.k-pagination-details{white-space:nowrap}.k-pagination>span{font-size:var(--text-sm)}.k-pagination[data-align]{text-align:var(--align)}[dir=ltr] .k-dropdown-content.k-pagination-selector{left:50%}[dir=rtl] .k-dropdown-content.k-pagination-selector{right:50%}.k-dropdown-content.k-pagination-selector{position:absolute;top:100%;transform:translate(-50%);background:var(--color-black)}[dir=ltr] .k-dropdown-content.k-pagination-selector{direction:ltr}[dir=rtl] .k-dropdown-content.k-pagination-selector{direction:rtl}.k-pagination-settings{display:flex;align-items:center;justify-content:space-between}.k-pagination-settings .k-button{line-height:1}[dir=ltr] .k-pagination-settings label{border-right:1px solid rgba(255,255,255,.35)}[dir=rtl] .k-pagination-settings label{border-left:1px solid rgba(255,255,255,.35)}.k-pagination-settings label{display:flex;align-items:center;padding:.625rem 1rem;font-size:var(--text-xs)}[dir=ltr] .k-pagination-settings label span{margin-right:.5rem}[dir=rtl] .k-pagination-settings label span{margin-left:.5rem}.k-prev-next{direction:ltr}.k-search{max-width:30rem;margin:2.5rem auto;box-shadow:var(--shadow-lg);background:var(--color-light);border-radius:var(--rounded)}.k-search-input{display:flex}.k-search-types{flex-shrink:0;display:flex}[dir=ltr] .k-search-types>.k-button{padding-left:1rem}[dir=rtl] .k-search-types>.k-button{padding-right:1rem}.k-search-types>.k-button{font-size:var(--text-base);line-height:1;height:2.5rem}.k-search-types>.k-button .k-icon{height:2.5rem}.k-search-types>.k-button .k-button-text{opacity:1;font-weight:500}.k-search-input input{background:0 0;flex-grow:1;font:inherit;padding:.75rem;border:0;height:2.5rem}.k-search-close{width:3rem;line-height:1}.k-search-close .k-icon-loader{animation:Spin 2s linear infinite}.k-search input:focus{outline:0}.k-search-results{padding:.5rem 1rem 1rem}.k-search .k-item:not(:last-child){margin-bottom:.25rem}.k-search .k-item[data-selected=true]{outline:2px solid var(--color-focus)}.k-search .k-item-info,.k-search-empty{font-size:var(--text-xs)}.k-search-empty{text-align:center;color:var(--color-gray-600)}.k-tag{position:relative;font-size:var(--text-sm);line-height:1;cursor:pointer;background-color:var(--color-gray-900);color:var(--color-light);border-radius:var(--rounded);display:flex;align-items:center;justify-content:space-between;-webkit-user-select:none;-moz-user-select:none;user-select:none}.k-tag:focus{outline:0;background-color:var(--color-focus);color:#fff}.k-tag-text{padding:.3rem .75rem .375rem;line-height:var(--leading-tight)}[dir=ltr] .k-tag-toggle{padding-right:1px}[dir=rtl] .k-tag-toggle{padding-left:1px}[dir=ltr] .k-tag-toggle{border-left:1px solid rgba(255,255,255,.15)}[dir=rtl] .k-tag-toggle{border-right:1px solid rgba(255,255,255,.15)}.k-tag-toggle{color:#ffffffb3;width:1.75rem;height:100%}.k-tag-toggle:hover{background:rgba(255,255,255,.2);color:#fff}[data-disabled=true] .k-tag{background-color:var(--color-gray-600)}[data-disabled=true] .k-tag .k-tag-toggle{display:none}.k-topbar{--bg:var(--color-gray-900);position:relative;color:var(--color-white);flex-shrink:0;height:2.5rem;line-height:1;background:var(--bg)}.k-topbar-wrapper{position:relative;display:flex;align-items:center;margin-left:-.75rem;margin-right:-.75rem}[dir=ltr] .k-topbar-wrapper:after{left:100%}[dir=rtl] .k-topbar-wrapper:after{right:100%}.k-topbar-wrapper:after{position:absolute;content:"";height:2.5rem;background:var(--bg);width:3rem}.k-topbar-menu{flex-shrink:0}.k-topbar-menu ul{padding:.5rem 0}.k-topbar .k-button[data-theme]{color:var(--theme-light)}.k-topbar .k-button-text{opacity:1}.k-topbar-menu-button{display:flex;align-items:center}.k-topbar-menu .k-link[aria-current]{color:var(--color-focus);font-weight:500}.k-topbar-button{padding:.75rem;line-height:1;font-size:var(--text-sm)}.k-topbar-button .k-button-text{display:flex}[dir=ltr] .k-topbar-view-button{padding-right:0}[dir=rtl] .k-topbar-view-button{padding-left:0}.k-topbar-view-button{flex-shrink:0;display:flex;align-items:center}[dir=ltr] .k-topbar-view-button .k-icon{margin-right:.5rem}[dir=rtl] .k-topbar-view-button .k-icon{margin-left:.5rem}[dir=ltr] .k-topbar-signals{right:0}[dir=rtl] .k-topbar-signals{left:0}.k-topbar-signals{position:absolute;top:0;background:var(--bg);height:2.5rem;display:flex;align-items:center}.k-topbar-signals:before{position:absolute;content:"";top:-.5rem;bottom:0;width:.5rem;background:-webkit-linear-gradient(inline-start,rgba(17,17,17,0),#111)}.k-topbar-signals .k-button{line-height:1}.k-topbar-notification{font-weight:var(--font-bold);line-height:1;display:flex}@media screen and (max-width:30em){.k-topbar .k-button[data-theme=negative] .k-button-text{display:none}}.k-section,.k-sections{padding-bottom:3rem}.k-section-header{position:relative;display:flex;align-items:baseline;z-index:1}[dir=ltr] .k-section-header .k-headline{padding-right:var(--spacing-3)}[dir=rtl] .k-section-header .k-headline{padding-left:var(--spacing-3)}.k-section-header .k-headline{line-height:1.25rem;min-height:2rem;flex-grow:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}[dir=ltr] .k-section-header .k-button-group{right:0}[dir=rtl] .k-section-header .k-button-group{left:0}.k-section-header .k-button-group{position:absolute;top:calc(-.5rem - 1px)}.k-section-header .k-button-group>.k-button{padding:.75rem;display:inline-flex}.k-fields-issue-headline{margin-bottom:.5rem}.k-fields-section input[type=submit]{display:none}[data-locked=true] .k-fields-section{opacity:.2;pointer-events:none}.k-models-section[data-processing=true]{pointer-events:none}.k-models-section-search.k-input{margin-bottom:var(--spacing-3);background:var(--color-gray-300);padding:var(--spacing-2) var(--spacing-3);height:var(--field-input-height);border-radius:var(--rounded);font-size:var(--text-sm)}.k-info-section-label{margin-bottom:.5rem}.k-user-profile{background:var(--color-white)}.k-user-profile>.k-view{padding-top:3rem;padding-bottom:3rem;display:flex;align-items:center;line-height:0}[dir=ltr] .k-user-profile .k-button-group{margin-left:.75rem}[dir=rtl] .k-user-profile .k-button-group{margin-right:.75rem}.k-user-profile .k-button-group{overflow:hidden}.k-user-profile .k-button-group .k-button{display:block;padding-top:.25rem;padding-bottom:.25rem;overflow:hidden;white-space:nowrap}.k-user-view-image .k-icon,.k-user-view-image .k-image{width:5rem;height:5rem;border-radius:var(--rounded);line-height:0;overflow:hidden}.k-user-view-image[data-disabled=true]{opacity:1}.k-user-view-image .k-image{display:block}.k-user-view-image .k-button-text{opacity:1}.k-user-name-placeholder{color:var(--color-gray-500);transition:color .3s}.k-header[data-editable=true] .k-user-name-placeholder:hover{color:var(--color-gray-900)}.k-error-view{position:absolute;top:0;right:0;bottom:0;left:0;display:flex;align-items:center;justify-content:center}.k-error-view-content{line-height:1.5em;max-width:25rem;text-align:center}.k-error-view-icon{color:var(--color-negative);display:inline-block}.k-error-view-content p:not(:last-child){margin-bottom:.75rem}.k-installation-view .k-button{display:block;margin-top:1.5rem}.k-installation-view .k-headline{margin-bottom:.75rem}.k-installation-issues{line-height:1.5em;font-size:var(--text-sm)}[dir=ltr] .k-installation-issues li{padding-left:3.5rem}[dir=rtl] .k-installation-issues li{padding-right:3.5rem}.k-installation-issues li{position:relative;padding:1.5rem;background:var(--color-white)}[dir=ltr] .k-installation-issues .k-icon{left:1.5rem}[dir=rtl] .k-installation-issues .k-icon{right:1.5rem}.k-installation-issues .k-icon{position:absolute;top:calc(1.5rem + 2px)}.k-installation-issues .k-icon svg *{fill:var(--color-negative)}.k-installation-issues li:not(:last-child){margin-bottom:2px}.k-installation-issues li code{font:inherit;color:var(--color-negative)}[dir=ltr] .k-installation-view .k-button[type=submit]{margin-left:-1rem}[dir=rtl] .k-installation-view .k-button[type=submit]{margin-right:-1rem}.k-installation-view .k-button[type=submit]{padding:1rem}.k-languages-view .k-header{margin-bottom:1.5rem}.k-languages-view-section-header{margin-bottom:.5rem}.k-languages-view-section{margin-bottom:3rem}.k-login-fields{position:relative}[dir=ltr] .k-login-toggler{right:0}[dir=rtl] .k-login-toggler{left:0}.k-login-toggler{position:absolute;top:0;z-index:1;text-decoration:underline;font-size:.875rem}.k-login-form label abbr{visibility:hidden}.k-login-buttons{display:flex;align-items:center;justify-content:flex-end;padding:1.5rem 0}[dir=ltr] .k-login-button{margin-right:-1rem}[dir=rtl] .k-login-button{margin-left:-1rem}.k-login-button{padding:.5rem 1rem;font-weight:500;transition:opacity .3s}.k-login-button span{opacity:1}.k-login-button[disabled]{opacity:.25}.k-login-back-button,.k-login-checkbox{display:flex;align-items:center;flex-grow:1}[dir=ltr] .k-login-back-button{margin-left:-1rem}[dir=rtl] .k-login-back-button{margin-right:-1rem}.k-login-checkbox{padding:.5rem 0;font-size:var(--text-sm);cursor:pointer}.k-login-checkbox .k-checkbox-text{opacity:.75;transition:opacity .3s}.k-login-checkbox:focus span,.k-login-checkbox:hover span{opacity:1}.k-password-reset-view .k-user-info{height:38px;margin-bottom:2.25rem;padding:.5rem;background:var(--color-white);border-radius:var(--rounded-xs);box-shadow:var(--shadow)}.k-system-view .k-header{margin-bottom:1.5rem}.k-system-view-section-header{margin-bottom:.5rem;display:flex;justify-content:space-between}.k-system-view-section{margin-bottom:3rem}.k-system-info .k-stat-label{color:var(--theme, var(--color-black))}.k-block-type-code-editor{position:relative;font-size:var(--text-sm);line-height:1.5em;background:#000;border-radius:var(--rounded);padding:.5rem .75rem 3rem;color:#fff;font-family:var(--font-mono)}.k-block-type-code-editor .k-editor{white-space:pre-wrap;line-height:1.75em}[dir=ltr] .k-block-type-code-editor-language{right:0}[dir=ltr] .k-block-type-code-editor-language .k-icon,[dir=rtl] .k-block-type-code-editor-language{left:0}.k-block-type-code-editor-language{font-size:var(--text-sm);position:absolute;bottom:0}[dir=rtl] .k-block-type-code-editor-language .k-icon{right:0}.k-block-type-code-editor-language .k-icon{position:absolute;top:0;height:1.5rem;display:flex;width:2rem;z-index:0}.k-block-type-code-editor-language .k-select-input{position:relative;padding:.325rem .75rem .5rem 2rem;z-index:1;font-size:var(--text-xs)}.k-block-type-default .k-block-title{line-height:1.5em}.k-block-type-gallery ul{display:grid;grid-gap:.75rem;grid-template-columns:repeat(auto-fit,minmax(6rem,1fr));line-height:0;align-items:center;justify-content:center;cursor:pointer}.k-block-type-gallery-placeholder{background:var(--color-background)}.k-block-type-gallery figcaption{padding-top:.5rem;color:var(--color-gray-600);font-size:var(--text-sm);text-align:center}.k-block-type-heading-input{line-height:1.25em;font-weight:var(--font-bold)}.k-block-type-heading-input[data-level=h1]{font-size:var(--text-3xl);line-height:1.125em}.k-block-type-heading-input[data-level=h2]{font-size:var(--text-2xl)}.k-block-type-heading-input[data-level=h3]{font-size:var(--text-xl)}.k-block-type-heading-input[data-level=h4]{font-size:var(--text-lg)}.k-block-type-heading-input[data-level=h5]{line-height:1.5em;font-size:var(--text-base)}.k-block-type-heading-input[data-level=h6]{line-height:1.5em;font-size:var(--text-sm)}.k-block-type-heading-input .ProseMirror strong{font-weight:700}.k-block-type-image .k-block-figure-container{display:block;text-align:center;line-height:0}.k-block-type-image-auto{max-width:100%;max-height:30rem}.k-block-type-line hr{margin-top:.75rem;margin-bottom:.75rem;border:0;border-top:2px solid var(--color-gray-400)}.k-block-type-markdown-input{position:relative;font-size:var(--text-sm);line-height:1.5em;background:var(--color-background);border-radius:var(--rounded);padding:.5rem .5rem 0;font-family:var(--font-mono)}[dir=ltr] .k-block-type-quote-editor{padding-left:1rem}[dir=rtl] .k-block-type-quote-editor{padding-right:1rem}[dir=ltr] .k-block-type-quote-editor{border-left:2px solid var(--color-black)}[dir=rtl] .k-block-type-quote-editor{border-right:2px solid var(--color-black)}.k-block-type-quote-text{font-size:var(--text-xl);margin-bottom:.25rem;line-height:1.25em}.k-block-type-quote-citation{font-style:italic;font-size:var(--text-sm);color:var(--color-gray-600)}.k-block-type-table-preview{cursor:pointer;border:1px solid var(--color-gray-300);border-spacing:0;border-radius:var(--rounded-sm);overflow:hidden}[dir=ltr] .k-block-type-table-preview td,[dir=ltr] .k-block-type-table-preview th{text-align:left}[dir=rtl] .k-block-type-table-preview td,[dir=rtl] .k-block-type-table-preview th{text-align:right}.k-block-type-table-preview td,.k-block-type-table-preview th{line-height:1.5em;font-size:var(--text-sm)}.k-block-type-table-preview th{padding:.5rem .75rem}.k-block-type-table-preview td:not(.k-table-index-column){padding:0 .75rem}.k-block-type-table-preview td [class$=-field-preview],.k-block-type-table-preview td>*{padding:0}.k-block-type-text-input{font-size:var(--text-base);line-height:1.5em;height:100%}.k-block-container-type-text,.k-block-type-text,.k-block-type-text .k-writer .ProseMirror{height:100%}.k-block-container{position:relative;padding:.75rem;background:var(--color-white);border-radius:var(--rounded)}.k-block-container:not(:last-of-type){border-bottom:1px dashed rgba(0,0,0,.1)}.k-block-container:focus{outline:0}.k-block-container[data-batched=true],.k-block-container[data-selected=true]{z-index:2;border-bottom-color:transparent}.k-block-container[data-batched=true]:after{position:absolute;top:0;right:0;bottom:0;left:0;content:"";background:rgba(238,242,246,.375);mix-blend-mode:multiply;border:1px solid var(--color-focus)}.k-block-container[data-selected=true]{box-shadow:var(--color-focus) 0 0 0 1px,var(--color-focus-outline) 0 0 0 3px}[dir=ltr] .k-block-container .k-block-options{right:.75rem}[dir=rtl] .k-block-container .k-block-options{left:.75rem}.k-block-container .k-block-options{display:none;position:absolute;top:0;margin-top:calc(-1.75rem + 2px)}.k-block-container[data-last-in-batch=true]>.k-block-options,.k-block-container[data-selected=true]>.k-block-options{display:flex}.k-block-container[data-hidden=true] .k-block{opacity:.25}.k-drawer-options .k-button[data-disabled=true]{vertical-align:middle;display:inline-grid}[data-disabled=true] .k-block-container{background:var(--color-background)}.k-block-importer.k-dialog{background:#313740;color:var(--color-white)}.k-block-importer .k-dialog-body{padding:0}.k-block-importer label{display:block;padding:var(--spacing-6) var(--spacing-6)0;color:var(--color-gray-400)}.k-block-importer label kbd{background:rgba(0,0,0,.5);font-family:var(--font-mono);letter-spacing:.1em;padding:.25rem;border-radius:var(--rounded);margin:0 .25rem}.k-block-importer textarea{width:100%;height:20rem;background:0 0;font:inherit;color:var(--color-white);border:0;padding:var(--spacing-6);resize:none}.k-block-importer textarea:focus{outline:0}.k-blocks{background:var(--color-white);box-shadow:var(--shadow);border-radius:var(--rounded)}[data-disabled=true] .k-blocks{background:var(--color-background)}.k-blocks[data-multi-select-key=true] .k-block-container>*{pointer-events:none}.k-blocks[data-empty=true]{padding:0;background:0 0;box-shadow:none}.k-blocks .k-sortable-ghost{outline:2px solid var(--color-focus);box-shadow:#11111140 0 5px 10px;cursor:grabbing;cursor:-webkit-grabbing}.k-blocks-list>.k-blocks-empty{display:flex;align-items:center}.k-blocks-list>.k-blocks-empty:not(:only-child){display:none}.k-block-figure{cursor:pointer}.k-block-figure iframe{border:0;pointer-events:none;background:var(--color-black)}.k-block-figure figcaption{padding-top:.5rem;color:var(--color-gray-600);font-size:var(--text-sm);text-align:center}.k-block-figure-empty.k-button{display:flex;width:100%;height:6rem;border-radius:var(--rounded-sm);align-items:center;justify-content:center;color:var(--color-gray-600);background:var(--color-background)}.k-block-options{display:flex;align-items:center;background:var(--color-white);z-index:var(--z-dropdown);box-shadow:#0000001a -2px 0 5px,var(--shadow),var(--shadow-xl);color:var(--color-black);border-radius:var(--rounded)}[dir=ltr] .k-block-options-button{border-right:1px solid var(--color-background)}[dir=rtl] .k-block-options-button{border-left:1px solid var(--color-background)}.k-block-options-button{--block-options-button-size:30px;width:var(--block-options-button-size);height:var(--block-options-button-size);line-height:1;display:inline-flex;align-items:center;justify-content:center}[dir=ltr] .k-block-options-button:first-child{border-top-left-radius:var(--rounded)}[dir=rtl] .k-block-options-button:first-child{border-top-right-radius:var(--rounded)}[dir=ltr] .k-block-options-button:first-child{border-bottom-left-radius:var(--rounded)}[dir=rtl] .k-block-options-button:first-child{border-bottom-right-radius:var(--rounded)}[dir=ltr] .k-block-options-button:last-child{border-top-right-radius:var(--rounded)}[dir=rtl] .k-block-options-button:last-child{border-top-left-radius:var(--rounded)}[dir=ltr] .k-block-options-button:last-child{border-bottom-right-radius:var(--rounded)}[dir=rtl] .k-block-options-button:last-child{border-bottom-left-radius:var(--rounded)}[dir=ltr] .k-block-options-button:last-of-type{border-right:0}[dir=rtl] .k-block-options-button:last-of-type{border-left:0}.k-block-options-button[aria-current]{color:var(--color-focus)}.k-block-options-button:hover{background:var(--color-gray-100)}.k-block-options .k-dropdown-content{margin-top:.5rem}.k-block-selector.k-dialog{background:var(--color-dark);color:var(--color-white)}.k-block-selector .k-headline{margin-bottom:1rem}.k-block-selector details:not(:last-of-type){margin-bottom:1.5rem}.k-block-selector summary{font-size:var(--text-xs);cursor:pointer;color:var(--color-gray-400)}.k-block-selector details:only-of-type summary{pointer-events:none}.k-block-selector summary:focus{outline:0}.k-block-selector summary:focus-visible{color:var(--color-green-400)}.k-block-types{display:grid;grid-gap:2px;margin-top:.75rem;grid-template-columns:repeat(1,1fr)}[dir=ltr] .k-block-types .k-button{text-align:left}[dir=rtl] .k-block-types .k-button{text-align:right}.k-block-types .k-button{display:flex;align-items:flex-start;border-radius:var(--rounded);background:rgba(0,0,0,.5);width:100%;padding:0 .75rem 0 0;line-height:1.5em}.k-block-types .k-button:focus{outline:2px solid var(--color-green-300)}.k-block-types .k-button .k-button-text{padding:.5rem 0 .5rem .5rem}.k-block-types .k-button .k-icon{width:38px;height:38px}.k-clipboard-hint{padding-top:1.5rem;font-size:var(--text-xs);color:var(--color-gray-400)}.k-clipboard-hint kbd{background:rgba(0,0,0,.5);font-family:var(--font-mono);letter-spacing:.1em;padding:.25rem;border-radius:var(--rounded);margin:0 .25rem}[dir=ltr] .k-block-title{padding-right:.75rem}[dir=rtl] .k-block-title{padding-left:.75rem}.k-block-title{display:flex;align-items:center;min-width:0;font-size:var(--text-sm);line-height:1}[dir=ltr] .k-block-icon{margin-right:.5rem}[dir=rtl] .k-block-icon{margin-left:.5rem}.k-block-icon{width:1rem;color:var(--color-gray-500)}[dir=ltr] .k-block-name{margin-right:.5rem}[dir=rtl] .k-block-name{margin-left:.5rem}.k-block-label{color:var(--color-gray-600);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.k-bubbles-field-preview{padding:.325rem .75rem}.k-text-field-preview{padding:.325rem .75rem;overflow-x:hidden;text-overflow:ellipsis;white-space:nowrap}.k-url-field-preview{padding:.325rem .75rem;overflow-x:hidden;text-overflow:ellipsis}.k-url-field-preview[data-link]{color:var(--color-focus)}.k-url-field-preview a{text-decoration:underline;transition:color .3s;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.k-url-field-preview a:hover{color:var(--color-black)}.k-flag-field-preview{height:var(--table-row-height);width:var(--table-row-height);display:flex;justify-content:center;align-items:center}.k-html-field-preview{padding:.325rem .75rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:1.5em}.k-html-field-preview p:not(:last-child){margin-bottom:1.5em}[dir=ltr] .k-html-field-preview ol,[dir=ltr] .k-html-field-preview ul{margin-left:1rem}[dir=rtl] .k-html-field-preview ol,[dir=rtl] .k-html-field-preview ul{margin-right:1rem}.k-html-field-preview ul>li{list-style:disc}.k-html-field-preview ol ul>li,.k-html-field-preview ul ul>li{list-style:circle}.k-html-field-preview ol>li{list-style:decimal}.k-html-field-preview ol>li::marker{color:var(--color-gray-500);font-size:var(--text-xs)}.k-html-field-preview a{color:var(--color-focus);text-decoration:underline}.k-toggle-field-preview label{padding:0 .25rem 0 .75rem;display:flex;height:38px;cursor:pointer;overflow:hidden;white-space:nowrap}[dir=ltr] .k-toggle-field-preview .k-toggle-input-label{padding-left:.5rem}[dir=ltr] [data-align=right] .k-toggle-field-preview .k-toggle-input-label,[dir=rtl] .k-toggle-field-preview .k-toggle-input-label{padding-right:.5rem}[dir=rtl] [data-align=right] .k-toggle-field-preview .k-toggle-input-label{padding-left:.5rem}[dir=ltr] .k-toggle-field-preview .k-toggle-input{padding-left:.75rem;padding-right:.25rem}.k-toggle-field-preview .k-toggle-input{padding-top:0;padding-bottom:0}[dir=ltr] [data-align=right] .k-toggle-field-preview .k-toggle-input,[dir=rtl] .k-toggle-field-preview .k-toggle-input{padding-left:.25rem;padding-right:.75rem}[dir=rtl] [data-align=right] .k-toggle-field-preview .k-toggle-input{padding-right:.25rem;padding-left:.75rem}[data-align=right] .k-toggle-field-preview .k-toggle-input{flex-direction:row-reverse}:root{--color-backdrop:rgba(0, 0, 0, .6);--color-black:#000;--color-dark:#313740;--color-light:var(--color-gray-200);--color-white:#fff;--color-gray-100:#f7f7f7;--color-gray-200:#efefef;--color-gray-300:#ddd;--color-gray-400:#ccc;--color-gray-500:#999;--color-gray-600:#777;--color-gray-700:#555;--color-gray-800:#333;--color-gray-900:#111;--color-gray:var(--color-gray-600);--color-red-200:#edc1c1;--color-red-300:#e3a0a0;--color-red-400:#d16464;--color-red-600:#ce1f1f;--color-red:var(--color-red-600);--color-orange-200:#f2d4bf;--color-orange-300:#ebbe9e;--color-orange-400:#de935f;--color-orange-600:#f4861f;--color-orange:var(--color-orange-600);--color-yellow-200:#f9e8c7;--color-yellow-300:#f7e2b8;--color-yellow-400:#f0c674;--color-yellow-600:#cca000;--color-yellow:var(--color-yellow-600);--color-green-200:#dce5c2;--color-green-300:#c6d49d;--color-green-400:#a7bd68;--color-green-600:#678f00;--color-green:var(--color-green-600);--color-aqua-200:#d0e5e2;--color-aqua-300:#bbd9d5;--color-aqua-400:#8abeb7;--color-aqua-600:#398e93;--color-aqua:var(--color-aqua-600);--color-blue-200:#cbd7e5;--color-blue-300:#b1c2d8;--color-blue-400:#7e9abf;--color-blue-600:#4271ae;--color-blue:var(--color-blue-600);--color-purple-200:#e0d4e4;--color-purple-300:#d4c3d9;--color-purple-400:#b294bb;--color-purple-600:#9c48b9;--color-purple:var(--color-purple-600);--container:80rem;--font-sans:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";--font-mono:"SFMono-Regular", Consolas, Liberation Mono, Menlo, Courier, monospace;--font-normal:400;--font-bold:600;--leading-none:1;--leading-tight:1.25;--leading-snug:1.375;--leading-normal:1.5;--leading-relaxed:1.625;--leading-loose:2;--rounded-xs:1px;--rounded-sm:.125rem;--rounded:.25rem;--rounded-md:.375rem;--shadow:0 1px 3px 0 rgba(0, 0, 0, .1), 0 1px 2px 0 rgba(0, 0, 0, .06);--shadow-md:0 4px 6px -1px rgba(0, 0, 0, .1), 0 2px 4px -1px rgba(0, 0, 0, .06);--shadow-lg:0 10px 15px -3px rgba(0, 0, 0, .1), 0 4px 6px -2px rgba(0, 0, 0, .05);--shadow-xl:0 20px 25px -5px rgba(0, 0, 0, .1), 0 10px 10px -5px rgba(0, 0, 0, .04);--shadow-outline:currentColor 0 0 0 2px;--shadow-inset:inset 0 2px 4px 0 rgba(0, 0, 0, .06);--spacing-0:0;--spacing-px:1px;--spacing-2px:2px;--spacing-1:.25rem;--spacing-2:.5rem;--spacing-3:.75rem;--spacing-4:1rem;--spacing-5:1.25rem;--spacing-6:1.5rem;--spacing-8:2rem;--spacing-10:2.5rem;--spacing-12:3rem;--spacing-16:4rem;--spacing-20:5rem;--spacing-24:6rem;--spacing-36:9rem;--text-xs:.75rem;--text-sm:.875rem;--text-base:1rem;--text-lg:1.125rem;--text-xl:1.25rem;--text-2xl:1.5rem;--text-3xl:1.75rem;--text-4xl:2.5rem;--text-5xl:3rem;--text-6xl:4rem;--color-background:var(--color-light);--color-border:var(--color-gray-400);--color-focus:var(--color-blue-600);--color-focus-light:var(--color-blue-400);--color-focus-outline:rgba(113, 143, 183, .25);--color-negative:var(--color-red-600);--color-negative-light:var(--color-red-400);--color-negative-outline:rgba(212, 110, 110, .25);--color-notice:var(--color-orange-600);--color-notice-light:var(--color-orange-400);--color-positive:var(--color-green-600);--color-positive-light:var(--color-green-400);--color-positive-outline:rgba(128, 149, 65, .25);--color-text:var(--color-gray-900);--color-text-light:var(--color-gray-600);--z-offline:1200;--z-fatal:1100;--z-loader:1000;--z-notification:900;--z-dialog:800;--z-navigation:700;--z-dropdown:600;--z-drawer:500;--z-dropzone:400;--z-toolbar:300;--z-content:200;--z-background:100;--bg-pattern:repeating-conic-gradient( rgba(0, 0, 0, 0) 0% 25%, rgba(0, 0, 0, .2) 0% 50% ) 50% / 20px 20px;--shadow-sticky:rgba(0, 0, 0, .05) 0 2px 5px;--shadow-dropdown:var(--shadow-lg);--shadow-item:var(--shadow);--field-input-padding:.5rem;--field-input-height:2.25rem;--field-input-line-height:1.25rem;--field-input-font-size:var(--text-base);--field-input-color-before:var(--color-gray-700);--field-input-color-after:var(--color-gray-700);--field-input-border:1px solid var(--color-border);--field-input-focus-border:1px solid var(--color-focus);--field-input-focus-outline:2px solid var(--color-focus-outline);--field-input-invalid-border:1px solid var(--color-negative-outline);--field-input-invalid-outline:0;--field-input-invalid-focus-border:1px solid var(--color-negative);--field-input-invalid-focus-outline:2px solid var(--color-negative-outline);--field-input-background:var(--color-white);--field-input-disabled-color:var(--color-gray-500);--field-input-disabled-background:var(--color-white);--field-input-disabled-border:1px solid var(--color-gray-300);--font-family-sans:var(--font-sans);--font-family-mono:var(--font-mono);--font-size-tiny:var(--text-xs);--font-size-small:var(--text-sm);--font-size-medium:var(--text-base);--font-size-large:var(--text-xl);--font-size-huge:var(--text-2xl);--font-size-monster:var(--text-3xl);--box-shadow-dropdown:var(--shadow-dropdown);--box-shadow-item:var(--shadow);--box-shadow-focus:var(--shadow-xl)}*,:after,:before{margin:0;padding:0;box-sizing:border-box}noscript{padding:1.5rem;display:flex;align-items:center;justify-content:center;height:100vh;text-align:center}html{font-family:var(--font-sans);background:var(--color-background)}body,html{color:var(--color-gray-900);height:100%;overflow:hidden}a{color:inherit;text-decoration:none}li{list-style:none}b,strong{font-weight:var(--font-bold)}@keyframes LoadingCursor{to{cursor:progress}}@keyframes Spin{to{transform:rotate(360deg)}}[data-align=left]{--align:start}[data-align=center]{--align:center}[data-align=right]{--align:end}[data-invalid=true]{border:1px solid var(--color-negative-outline);box-shadow:var(--color-negative-outline) 0 0 3px 2px}[data-invalid=true]:focus-within{border:var(--field-input-invalid-focus-border)!important;box-shadow:var(--color-negative-outline) 0 0 0 2px!important}[data-tabbed=true]{box-shadow:var(--shadow-outline);border-radius:var(--rounded)}[data-theme=positive],[data-theme=success]{--theme:var(--color-positive);--theme-light:var(--color-positive-light);--theme-bg:var(--color-green-300)}[data-theme=error],[data-theme=negative]{--theme:var(--color-negative);--theme-light:var(--color-negative-light);--theme-bg:var(--color-red-300)}[data-theme=notice]{--theme:var(--color-notice);--theme-light:var(--color-notice-light);--theme-bg:var(--color-orange-300)}[data-theme=info]{--theme:var(--color-focus);--theme-light:var(--color-focus-light);--theme-bg:var(--color-blue-200)}.scroll-x,.scroll-x-auto,.scroll-y,.scroll-y-auto{-webkit-overflow-scrolling:touch;transform:translateZ(0)}.scroll-x,.scroll-x-auto{overflow-x:scroll;overflow-y:hidden}.scroll-x-auto{overflow-x:auto}.scroll-y,.scroll-y-auto{overflow-x:hidden;overflow-y:scroll}.scroll-y-auto{overflow-y:auto}.input-hidden{position:absolute;-webkit-appearance:none;-moz-appearance:none;appearance:none;width:0;height:0;opacity:0}.k-offscreen,.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0} diff --git a/kirby/panel/dist/css/style.min.css b/kirby/panel/dist/css/style.min.css new file mode 100644 index 0000000..de85587 --- /dev/null +++ b/kirby/panel/dist/css/style.min.css @@ -0,0 +1 @@ +.k-items{position:relative;display:grid;container-type:inline-size}.k-items[data-layout=list]{gap:2px}.k-items[data-layout=cardlets]{--items-size: 1fr;display:grid;gap:.75rem;grid-template-columns:repeat(auto-fill,minmax(var(--items-size),1fr))}@container (min-width: 15rem){.k-items[data-layout=cardlets]{--items-size: 15rem}}.k-items[data-layout=cards]{display:grid;gap:1.5rem;grid-template-columns:1fr}@container (min-width: 6rem){.k-items[data-layout=cards][data-size=tiny]{grid-template-columns:repeat(auto-fill,minmax(6rem,1fr))}}@container (min-width: 9rem){.k-items[data-layout=cards][data-size=small]{grid-template-columns:repeat(auto-fill,minmax(9rem,1fr))}}@container (min-width: 12rem){.k-items[data-layout=cards][data-size=auto],.k-items[data-layout=cards][data-size=medium]{grid-template-columns:repeat(auto-fill,minmax(12rem,1fr))}}@container (min-width: 15rem){.k-items[data-layout=cards][data-size=large]{grid-template-columns:repeat(auto-fill,minmax(15rem,1fr))}}@container (min-width: 18rem){.k-items[data-layout=cards][data-size=huge]{grid-template-columns:repeat(auto-fill,minmax(18rem,1fr))}}.k-collection-footer{display:flex;justify-content:space-between;align-items:flex-start;flex-wrap:nowrap;gap:var(--spacing-12);margin-top:var(--spacing-2)}.k-empty{max-width:100%}:root{--item-button-height: var(--height-md);--item-button-width: var(--height-md);--item-height: auto;--item-height-cardlet: calc(var(--height-md) * 3)}.k-item{position:relative;background:var(--color-white);box-shadow:var(--shadow);border-radius:var(--rounded);height:var(--item-height);container-type:inline-size}.k-item:has(a:focus){outline:2px solid var(--color-focus)}@supports not selector(:has(*)){.k-item:focus-within{outline:2px solid var(--color-focus)}}.k-item .k-icon-frame{--back: var(--color-gray-300)}.k-item-content{line-height:1.25;overflow:hidden;padding:var(--spacing-2)}.k-item-content a:focus{outline:0}.k-item-content a:after{content:"";position:absolute;top:0;right:0;bottom:0;left:0}.k-item-info{color:var(--color-text-dimmed)}.k-item-options{transform:translate(0);z-index:1;display:flex;align-items:center;justify-content:space-between}.k-item-options[data-only-option=true]{justify-content:flex-end}.k-item-options .k-button{--button-height: var(--item-button-height);--button-width: var(--item-button-width)}.k-item .k-sort-button{position:absolute;z-index:2}.k-item:not(:hover):not(.k-sortable-fallback) .k-sort-button{opacity:0}.k-item[data-layout=list]{--item-height: var( --field-input-height );--item-button-height: var(--item-height);--item-button-width: auto;display:grid;height:var(--item-height);align-items:center;grid-template-columns:1fr auto}.k-item[data-layout=list][data-has-image=true]{grid-template-columns:var(--item-height) 1fr auto}.k-item[data-layout=list] .k-frame{--ratio: 1/1;border-start-start-radius:var(--rounded);border-end-start-radius:var(--rounded);height:var(--item-height)}.k-item[data-layout=list] .k-item-content{display:flex;min-width:0;white-space:nowrap;gap:var(--spacing-2);justify-content:space-between}.k-item[data-layout=list] .k-item-title,.k-item[data-layout=list] .k-item-info{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.k-item[data-layout=list] .k-item-title{flex-shrink:1}.k-item[data-layout=list] .k-item-info{flex-shrink:2}@container (max-width: 30rem){.k-item[data-layout=list] .k-item-title{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.k-item[data-layout=list] .k-item-info{display:none}}.k-item[data-layout=list] .k-sort-button{--button-width: calc(1.5rem + var(--spacing-1));--button-height: var(--item-height);left:calc(-1 * var(--button-width))}.k-item:is([data-layout=cardlets],[data-layout=cards]) .k-sort-button{top:var(--spacing-2);inset-inline-start:var(--spacing-2);background:#ffffff7f;-webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px);box-shadow:0 2px 5px #0003;--button-width: 1.5rem;--button-height: 1.5rem;--button-rounded: var(--rounded-sm);--button-padding: 0;--icon-size: 14px}.k-item:is([data-layout=cardlets],[data-layout=cards]) .k-sort-button:hover{background:#fffffff2}.k-item[data-layout=cardlets]{--item-height: var(--item-height-cardlet);display:grid;grid-template-areas:"content" "options";grid-template-columns:1fr;grid-template-rows:1fr var(--height-md)}.k-item[data-layout=cardlets][data-has-image=true]{grid-template-areas:"image content" "image options";grid-template-columns:minmax(0,var(--item-height)) 1fr}.k-item[data-layout=cardlets] .k-frame{grid-area:image;border-start-start-radius:var(--rounded);border-end-start-radius:var(--rounded);aspect-ratio:auto;height:var(--item-height)}.k-item[data-layout=cardlets] .k-item-content{grid-area:content}.k-item[data-layout=cardlets] .k-item-info{margin-top:.125em;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.k-item[data-layout=cardlets] .k-item-options{grid-area:options}.k-item[data-layout=cards]{display:flex;flex-direction:column}.k-item[data-layout=cards] .k-frame{border-start-start-radius:var(--rounded);border-start-end-radius:var(--rounded)}.k-item[data-layout=cards] .k-item-content{flex-grow:1;padding:var(--spacing-2)}.k-item[data-layout=cards] .k-item-info{margin-top:.125em}.k-item[data-theme=disabled]{background:transparent;box-shadow:none;outline:1px solid var(--color-border);outline-offset:-1px}.k-dialog-body{padding:var(--dialog-padding)}.k-dialog[data-has-footer=true] .k-dialog-body{padding-bottom:0}.k-button-group.k-dialog-buttons{display:grid;grid-template-columns:1fr 1fr;gap:var(--spacing-3);--button-height: var(--height-lg)}.k-dialog-fields{padding-bottom:.5rem;container-type:inline-size}.k-dialog-footer{padding:var(--dialog-padding);line-height:1;flex-shrink:0}.k-dialog .k-notification{padding-block:.325rem;border-start-start-radius:var(--dialog-rounded);border-start-end-radius:var(--dialog-rounded);margin-top:-1px}.k-dialog-search{margin-bottom:.75rem;--input-color-border: transparent;--input-color-back: var(--color-gray-300)}:root{--dialog-color-back: var(--color-light);--dialog-color-text: currentColor;--dialog-margin: var(--spacing-6);--dialog-padding: var(--spacing-6);--dialog-rounded: var(--rounded-xl);--dialog-shadow: var(--shadow-xl);--dialog-width: 22rem}.k-dialog-portal{padding:var(--dialog-margin)}.k-dialog{position:relative;background:var(--dialog-color-back);color:var(--dialog-color-text);width:clamp(10rem,100%,var(--dialog-width));box-shadow:var(--dialog-shadow);border-radius:var(--dialog-rounded);line-height:1;display:flex;flex-direction:column;overflow:clip;container-type:inline-size}@media screen and (min-width: 20rem){.k-dialog[data-size=small]{--dialog-width: 20rem}}@media screen and (min-width: 22rem){.k-dialog[data-size=default]{--dialog-width: 22rem}}@media screen and (min-width: 30rem){.k-dialog[data-size=medium]{--dialog-width: 30rem}}@media screen and (min-width: 40rem){.k-dialog[data-size=large]{--dialog-width: 40rem}}@media screen and (min-width: 60rem){.k-dialog[data-size=huge]{--dialog-width: 60rem}}.k-dialog .k-pagination{margin-bottom:-1.5rem;display:flex;justify-content:center;align-items:center}.k-changes-dialog .k-headline{margin-top:-.5rem;margin-bottom:var(--spacing-3)}.k-error-details{background:var(--color-white);display:block;overflow:auto;padding:1rem;font-size:var(--text-sm);line-height:1.25em;margin-top:.75rem}.k-error-details dt{color:var(--color-red-500);margin-bottom:.25rem}.k-error-details dd{overflow:hidden;overflow-wrap:break-word;text-overflow:ellipsis}.k-error-details dd:not(:last-of-type){margin-bottom:1.5em}.k-error-details li{white-space:pre-line}.k-error-details li:not(:last-child){border-bottom:1px solid var(--color-background);padding-bottom:.25rem;margin-bottom:.25rem}.k-models-dialog .k-list-item{cursor:pointer}.k-models-dialog .k-choice-input{--choice-color-checked: var(--color-focus);display:flex;align-items:center;height:var(--item-button-height);margin-inline-end:var(--spacing-3)}.k-models-dialog .k-choice-input input{top:0}.k-models-dialog .k-collection-footer .k-pagination{margin-bottom:0}.k-license-dialog-status{display:flex;align-items:center;gap:var(--spacing-2)}.k-license-dialog .k-icon{color:var(--theme-color-700)}.k-page-template-switch{margin-bottom:var(--spacing-6);padding-bottom:var(--spacing-6);border-bottom:1px dashed var(--color-gray-300)}.k-page-move-dialog .k-headline{margin-bottom:var(--spacing-2)}.k-page-move-parent{--tree-color-back: var(--color-white);--tree-color-hover-back: var(--color-light);padding:var(--spacing-3);background:var(--color-white);border-radius:var(--rounded);box-shadow:var(--shadow)}.k-pages-dialog-navbar{display:flex;align-items:center;justify-content:center;margin-bottom:.5rem;padding-inline-end:38px}.k-pages-dialog-navbar .k-button[aria-disabled]{opacity:0}.k-pages-dialog-navbar .k-headline{flex-grow:1;text-align:center}.k-pages-dialog-option[aria-disabled]{opacity:.25}.k-search-dialog{--dialog-padding: 0;--dialog-rounded: var(--rounded);overflow:visible}.k-overlay[open][data-type=dialog]>.k-portal>.k-search-dialog{margin-top:0}.k-totp-dialog-headline{margin-bottom:var(--spacing-1)}.k-totp-dialog-intro{margin-bottom:var(--spacing-6)}.k-totp-dialog-grid{display:grid;gap:var(--spacing-6)}@media screen and (min-width: 40rem){.k-totp-dialog-grid{grid-template-columns:1fr 1fr;gap:var(--spacing-8)}}.k-totp-qrcode .k-box[data-theme]{padding:var(--box-padding-inline)}.k-totp-dialog-fields .k-field-name-confirm{--input-height: var(--height-xl);--input-rounded: var(--rounded);--input-font-size: var(--text-3xl)}.k-upload-dialog.k-dialog{--dialog-width: 40rem}.k-upload-replace-dialog .k-upload-items{display:flex;gap:var(--spacing-3);align-items:center}.k-upload-original{width:6rem;border-radius:var(--rounded);box-shadow:var(--shadow);overflow:hidden}.k-upload-replace-dialog .k-upload-item{flex-grow:1}.k-drawer-body{padding:var(--drawer-body-padding);flex-grow:1;background:var(--color-background)}.k-drawer-body .k-writer-input:focus-within .k-toolbar:not([data-inline=true]),.k-drawer-body .k-textarea-input-wrapper:focus-within .k-toolbar,.k-drawer-body .k-table th{top:-1.5rem}.k-drawer-header{--button-height: calc(var(--drawer-header-height) - var(--spacing-1));flex-shrink:0;height:var(--drawer-header-height);padding-inline-start:var(--drawer-header-padding);display:flex;align-items:center;line-height:1;justify-content:space-between;background:var(--color-white);font-size:var(--text-sm)}.k-drawer-breadcrumb{flex-grow:1}.k-drawer-options{display:flex;align-items:center;padding-inline-end:.75rem}.k-drawer-option{--button-width: var(--button-height)}.k-drawer-option[aria-disabled]{opacity:var(--opacity-disabled)}.k-notification.k-drawer-notification{padding:.625rem 1.5rem}.k-drawer-tabs{display:flex;align-items:center;line-height:1}.k-drawer-tab.k-button{--button-height: calc(var(--drawer-header-height) - var(--spacing-1));--button-padding: var(--spacing-3);display:flex;align-items:center;font-size:var(--text-xs);overflow-x:visible}.k-drawer-tab.k-button[aria-current]:after{position:absolute;bottom:-2px;inset-inline:var(--button-padding);content:"";background:var(--color-black);height:2px;z-index:1}:root{--drawer-body-padding: 1.5rem;--drawer-color-back: var(--color-light);--drawer-header-height: 2.5rem;--drawer-header-padding: 1rem;--drawer-shadow: var(--shadow-xl);--drawer-width: 50rem}.k-drawer-overlay+.k-drawer-overlay{--overlay-color-back: none}.k-drawer{--header-sticky-offset: calc(var(--drawer-body-padding) * -1);z-index:var(--z-toolbar);flex-basis:var(--drawer-width);position:relative;display:flex;flex-direction:column;background:var(--drawer-color-back);box-shadow:var(--drawer-shadow);container-type:inline-size}.k-drawer[aria-disabled]{display:none;pointer-events:none}.k-dropdown{position:relative}:root{--dropdown-color-bg: var(--color-black);--dropdown-color-text: var(--color-white);--dropdown-color-current: var(--color-blue-500);--dropdown-color-hr: rgba(255, 255, 255, .25);--dropdown-padding: var(--spacing-2);--dropdown-rounded: var(--rounded);--dropdown-shadow: var(--shadow-xl)}.k-dropdown-content{--dropdown-x: 0;--dropdown-y: 0;position:absolute;inset-block-start:0;inset-inline-start:initial;left:0;width:max-content;padding:var(--dropdown-padding);background:var(--dropdown-color-bg);border-radius:var(--dropdown-rounded);color:var(--dropdown-color-text);box-shadow:var(--dropdown-shadow);text-align:start;transform:translate(var(--dropdown-x),var(--dropdown-y))}.k-dropdown-content::backdrop{background:none}.k-dropdown-content[data-align-x=end]{--dropdown-x: -100%}.k-dropdown-content[data-align-x=center]{--dropdown-x: -50%}.k-dropdown-content[data-align-y=top]{--dropdown-y: -100%}.k-dropdown-content hr{margin:.5rem 0;height:1px;background:var(--dropdown-color-hr)}.k-dropdown-content[data-theme=light]{--dropdown-color-bg: var(--color-white);--dropdown-color-text: var(--color-black);--dropdown-color-current: var(--color-blue-800);--dropdown-color-hr: rgba(0, 0, 0, .1)}.k-dropdown-item.k-button{--button-align: flex-start;--button-color-text: var(--dropdown-color-text);--button-height: var(--height-sm);--button-rounded: var(--rounded-sm);--button-width: 100%;display:flex}.k-dropdown-item.k-button:focus{outline:var(--outline)}.k-dropdown-item.k-button[aria-current]{--button-color-text: var(--dropdown-color-current)}.k-dropdown-item.k-button[aria-current]:after{margin-inline-start:auto;text-align:center;content:"✓";padding-inline-start:var(--spacing-1)}.k-dropdown-item.k-button:not([aria-disabled]):hover{--button-color-back: var(--dropdown-color-hr)}.k-options-dropdown{display:flex;justify-content:center;align-items:center}:root{--picklist-rounded: var(--rounded-sm);--picklist-highlight: var(--color-yellow-500)}.k-picklist-input{--choice-color-text: currentColor;--button-rounded: var(--picklist-rounded)}.k-picklist-input-header{--input-rounded: var(--picklist-rounded)}.k-picklist-input-search{display:flex;align-items:center;border-radius:var(--picklist-rounded)}.k-picklist-input-search .k-search-input{height:var(--button-height)}.k-picklist-input-search:focus-within{outline:var(--outline)}.k-picklist-dropdown .k-picklist-input-create:focus{outline:0}.k-picklist-dropdown .k-picklist-input-create[aria-disabled=true]{visibility:hidden}.k-picklist-input-options.k-grid{--columns: 1}.k-picklist-input-options li+li{margin-top:var(--spacing-1)}.k-picklist-input-options .k-choice-input{padding-inline:var(--spacing-2)}.k-picklist-input-options .k-choice-input{--choice-color-checked: var(--color-focus)}.k-picklist-input-options .k-choice-input:has(:checked){--choice-color-text: var(--color-focus)}.k-picklist-input-options .k-choice-input[aria-disabled=true]{--choice-color-text: var(--color-text-dimmed)}.k-picklist-input-options .k-choice-input:has(:focus-within){outline:var(--outline)}.k-picklist-input-options .k-choice-input b{font-weight:var(--font-normal);color:var(--picklist-highlight)}.k-picklist-input-more.k-button{--button-width: 100%;--button-align: start;--button-color-text: var(--color-text-dimmed);padding-inline:var(--spacing-2)}.k-picklist-input-more.k-button .k-button-icon{position:relative;inset-inline-start:-1px}.k-picklist-input-empty{height:var(--button-height);line-height:1.25rem;padding:var(--spacing-1) var(--spacing-2);color:var(--color-text-dimmed)}.k-picklist-dropdown{--color-text-dimmed: var(--color-gray-400);padding:0;max-width:30rem;min-width:8rem}.k-picklist-dropdown :where(.k-picklist-input-header,.k-picklist-input-body,.k-picklist-input-footer){padding:var(--dropdown-padding)}.k-picklist-dropdown .k-picklist-input-header{border-bottom:1px solid var(--dropdown-color-hr)}.k-picklist-dropdown .k-picklist-input-search{background:var(--dropdown-color-hr);padding-inline-end:var(--input-padding)}.k-picklist-dropdown .k-picklist-input-create{--button-rounded: 1rem;--button-height: 1.125rem}.k-picklist-dropdown .k-picklist-input-create:focus{--button-color-back: var(--color-blue-500);--button-color-text: var(--color-black)}.k-picklist-dropdown .k-picklist-input-body{max-height:calc(var(--button-height) * 9.5 + 2px * 9 + var(--dropdown-padding));overflow-y:auto;outline-offset:-2px;overscroll-behavior:contain;scroll-padding-top:var(--dropdown-padding);scroll-padding-bottom:var(--dropdown-padding)}.k-picklist-dropdown .k-picklist-input-options .k-choice-input{--choice-color-border: var(--dropdown-color-hr);--choice-color-back: var(--dropdown-color-hr);--choice-color-info: var(--color-text-dimmed);min-height:var(--button-height);border-radius:var(--picklist-rounded);padding-block:.375rem}.k-picklist-dropdown .k-picklist-input-options li+li{margin-top:0}.k-picklist-dropdown .k-picklist-input-options .k-choice-input[aria-disabled=true] input{--choice-color-border: var(--dropdown-color-hr);--choice-color-back: var(--dropdown-color-hr);--choice-color-checked: var(--dropdown-color-hr);opacity:var(--opacity-disabled)}.k-picklist-dropdown .k-picklist-input-options .k-choice-input:not([aria-disabled=true]):hover{background-color:var(--dropdown-color-hr)}.k-picklist-dropdown .k-picklist-input-options .k-choice-input:not([aria-disabled=true]):focus-within{--choice-color-text: var(--color-blue-500)}.k-picklist-dropdown .k-picklist-input-more.k-button:hover{--button-color-back: var(--dropdown-color-hr)}.k-picklist-dropdown .k-picklist-input-body+.k-picklist-input-footer{border-top:1px solid var(--dropdown-color-hr)}.k-counter{font-size:var(--text-xs);color:var(--color-gray-900)}.k-counter[data-invalid=true]{color:var(--color-red-700)}.k-counter-rules{color:var(--color-gray-600);padding-inline-start:.5rem}.k-form-submitter{display:none}.k-field[data-disabled=true]{cursor:not-allowed}.k-field[data-disabled=true] *{pointer-events:none}.k-field[data-disabled=true] .k-text[data-theme=help] *{pointer-events:initial}.k-field-header{display:flex;justify-content:space-between;align-items:center;gap:var(--spacing-6);position:relative;margin-bottom:var(--spacing-2)}.k-field-options{flex-shrink:0}.k-field:focus-within>.k-field-header>.k-field-counter{display:block}.k-field-footer{margin-top:var(--spacing-2)}.k-fieldset{border:0}:root{--input-color-back: var(--color-white);--input-color-border: var(--color-border);--input-color-description: var(--color-text-dimmed);--input-color-icon: currentColor;--input-color-placeholder: var(--color-gray-600);--input-color-text: currentColor;--input-font-family: var(--font-sans);--input-font-size: var(--text-sm);--input-height: 2.25rem;--input-leading: 1;--input-outline-focus: var(--outline);--input-padding: var(--spacing-2);--input-padding-multiline: .475rem var(--input-padding);--input-rounded: var(--rounded);--input-shadow: none}@media (pointer: coarse){:root{--input-font-size: var(--text-md);--input-padding-multiline: .375rem var(--input-padding)}}.k-input{display:flex;align-items:center;line-height:var(--input-leading);border:0;background:var(--input-color-back);border-radius:var(--input-rounded);outline:1px solid var(--input-color-border);color:var(--input-color-text);min-height:var(--input-height);box-shadow:var(--input-shadow);font-family:var(--input-font-family);font-size:var(--input-font-size)}.k-input:focus-within{outline:var(--input-outline-focus)}.k-input-element{flex-grow:1}.k-input-icon{color:var(--input-color-icon);display:flex;justify-content:center;align-items:center;width:var(--input-height)}.k-input-icon-button{width:100%;height:100%;display:flex;align-items:center;justify-content:center;flex-shrink:0}.k-input-description{color:var(--input-color-description);padding-inline:var(--input-padding)}.k-input-before{padding-inline-end:0}.k-input-after{padding-inline-start:0}.k-input :where(.k-input-description,.k-input-icon){align-self:stretch;display:flex;align-items:center;flex-shrink:0}.k-input[data-disabled=true]{--input-color-back: var(--color-background);--input-color-icon: var(--color-gray-600);pointer-events:none}.k-block-title{display:flex;align-items:center;min-width:0;padding-inline-end:.75rem;line-height:1;gap:var(--spacing-2)}.k-block-icon{--icon-color: var(--color-gray-600);width:1rem}.k-block-label{color:var(--color-text-dimmed);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.k-block-type-default .k-block-title{line-height:1.5em}.k-block-options{--toolbar-size: 30px;box-shadow:var(--shadow-toolbar)}.k-block-options>.k-button:not(:last-of-type){border-inline-end:1px solid var(--color-background)}.k-block-options .k-dropdown-content{margin-top:.5rem}.k-block-container{position:relative;padding:var(--spacing-3);background:var(--color-white);border-radius:var(--rounded)}.k-block-container:not(:last-of-type){border-bottom:1px dashed rgba(0,0,0,.1)}.k-block-container:focus{outline:0}.k-block-container[data-selected=true]{z-index:2;outline:var(--outline);border-bottom-color:transparent}.k-block-container[data-batched=true]:after{position:absolute;top:0;right:0;bottom:0;left:0;content:"";background:#b1c2d82d;mix-blend-mode:multiply}.k-block-container .k-block-options{display:none;position:absolute;top:0;inset-inline-end:var(--spacing-3);margin-top:calc(-1.75rem + 2px)}.k-block-container[data-last-selected=true]>.k-block-options{display:flex}.k-block-container[data-hidden=true] .k-block{opacity:.25}.k-drawer-options .k-drawer-option[data-disabled=true]{vertical-align:middle;display:inline-grid}.k-block-container[data-disabled=true]{background:var(--color-background)}.k-block-container:is(.k-sortable-ghost,.k-sortable-fallback) .k-block{position:relative;max-height:4rem;overflow:hidden}.k-block-container:is(.k-sortable-ghost,.k-sortable-fallback) .k-block:after{position:absolute;bottom:0;content:"";height:2rem;width:100%;background:linear-gradient(to top,var(--color-white),transparent)}.k-blocks{border-radius:var(--rounded)}.k-blocks:not([data-empty=true],[data-disabled=true]){background:var(--color-white);box-shadow:var(--shadow)}.k-blocks[data-disabled=true]:not([data-empty=true]){border:1px solid var(--input-color-border)}.k-blocks-list[data-multi-select-key=true]>.k-block-container *{pointer-events:none}.k-blocks-list[data-multi-select-key=true]>.k-block-container .k-blocks *{pointer-events:all}.k-blocks .k-sortable-ghost{outline:2px solid var(--color-focus);box-shadow:#11111140 0 5px 10px;cursor:grabbing;cursor:-moz-grabbing;cursor:-webkit-grabbing}.k-blocks-list>.k-blocks-empty{display:flex;align-items:center}.k-block-importer .k-dialog-body{padding:0}.k-block-importer label{display:block;padding:var(--spacing-6) var(--spacing-6) 0;color:var(--color-text-dimmed);line-height:var(--leading-normal)}.k-block-importer label small{display:block;font-size:inherit}.k-block-importer textarea{width:100%;height:20rem;background:none;font:inherit;color:var(--color-white);border:0;padding:var(--spacing-6);resize:none}.k-block-importer textarea:focus{outline:0}.k-block-selector .k-headline{margin-bottom:1rem}.k-block-selector details+details{margin-top:var(--spacing-6)}.k-block-selector summary{font-size:var(--text-xs);cursor:pointer;color:var(--color-text-dimmed)}.k-block-selector details:only-of-type summary{pointer-events:none}.k-block-selector summary:focus{outline:0}.k-block-selector summary:focus-visible{color:var(--color-focus)}.k-block-types{display:grid;grid-gap:2px;margin-top:.75rem;grid-template-columns:repeat(1,1fr)}.k-block-types .k-button{--button-color-icon: var(--color-text);--button-color-back: var(--color-white);--button-padding: var(--spacing-3);width:100%;justify-content:start;gap:1rem;box-shadow:var(--shadow)}.k-block-types .k-button[aria-disabled]{opacity:var(--opacity-disabled);--button-color-back: var(--color-gray-200);box-shadow:none}.k-clipboard-hint{padding-top:1.5rem;line-height:var(--leading-normal);font-size:var(--text-xs);color:var(--color-text-dimmed)}.k-clipboard-hint small{display:block;font-size:inherit;color:var(--color-text-dimmed)}.k-block-background-dropdown>.k-button{--color-frame-rounded: 0;--color-frame-size: 1.5rem;--button-height: 1.5rem;--button-padding: 0 .125rem;--button-color-back: var(--color-white);gap:.25rem;box-shadow:var(--shadow-toolbar);border:1px solid var(--color-white)}.k-block-background-dropdown .k-color-frame{border-right:1px solid var(--color-gray-300)}.k-block-background-dropdown .k-color-frame:after{box-shadow:none}.k-block .k-block-background-dropdown{position:absolute;inset-inline-end:var(--spacing-3);bottom:var(--spacing-3);opacity:0;transition:opacity .2s ease-in-out}.k-block:hover .k-block-background-dropdown{opacity:1}.k-block-figure:not([data-empty=true]){--block-figure-back: var(--color-white);background:var(--block-figure-back)}.k-block-figure-container:not([data-disabled=true]){cursor:pointer}.k-block-figure iframe{border:0;pointer-events:none;background:var(--color-black)}.k-block-figure figcaption{padding-top:.5rem;color:var(--color-text-dimmed);font-size:var(--text-sm);text-align:center}.k-block-figure-empty{--button-width: 100%;--button-height: 6rem;--button-color-text: var(--color-text-dimmed);--button-color-back: var(--color-gray-200)}.k-block-figure-empty,.k-block-figure-container>*{border-radius:var(--rounded-sm)}.k-block-figure-caption{display:flex;justify-content:center;padding-top:var(--spacing-3)}.k-block-figure-caption .k-writer{width:max-content;text-align:center}.k-block-figure-caption .k-writer .k-text{color:var(--color-gray-600);font-size:var(--text-sm);mix-blend-mode:exclusion}.k-block-type-code-editor{position:relative}.k-block-type-code-editor .k-input{--input-color-border: none;--input-color-back: var(--color-black);--input-color-text: var(--color-white);--input-font-family: var(--font-mono);--input-outline-focus: none;--input-padding: var(--spacing-3);--input-padding-multiline: var(--input-padding)}.k-block-type-code-editor .k-input[data-type=textarea]{white-space:pre-wrap}.k-block-type-code-editor-language{--input-font-size: var(--text-xs);position:absolute;inset-inline-end:0;bottom:0}.k-block-type-code-editor-language .k-input-element{padding-inline-start:1.5rem}.k-block-type-code-editor-language .k-input-icon{inset-inline-start:0}.k-block-container.k-block-container-type-fields{padding-block:0}.k-block-container:not([data-hidden=true]) .k-block-type-fields>:not([data-collapsed=true]){padding-bottom:var(--spacing-3)}.k-block-type-fields-header{display:flex;justify-content:space-between}.k-block-type-fields-header .k-block-title{padding-block:var(--spacing-3);cursor:pointer;white-space:nowrap}.k-block-type-fields-form{background-color:var(--color-gray-200);padding:var(--spacing-6) var(--spacing-6) var(--spacing-8);border-radius:var(--rounded-sm);container:column / inline-size}.k-block-container-type-fields[data-hidden=true] :where(.k-drawer-tabs,.k-block-type-fields-form){display:none}.k-block-container.k-block-container-type-gallery{padding:0}.k-block-type-gallery-figure{padding:var(--spacing-3);border-radius:var(--rounded)}.k-block-type-gallery-figure:not([data-empty=true]){background:var(--block-back)}.k-block-type-gallery-figure ul{display:grid;grid-gap:.75rem;grid-template-columns:repeat(auto-fit,minmax(6rem,1fr));line-height:0;align-items:center;justify-content:center}.k-block-type-gallery:not([data-disabled=true]) .k-block-type-gallery-figure ul{cursor:pointer}.k-block-type-gallery-frame{border-radius:var(--rounded-sm)}.k-block-type-gallery[data-disabled=true] .k-block-type-gallery-placeholder{background:var(--color-gray-250)}.k-block-type-gallery-placeholder{background:var(--color-background)}.k-block-type-heading-input{display:flex;align-items:center;line-height:1.25em;font-size:var(--text-size);font-weight:var(--font-bold)}.k-block-type-heading-input[data-level=h1]{--text-size: var(--text-3xl);line-height:1.125em}.k-block-type-heading-input[data-level=h2]{--text-size: var(--text-2xl)}.k-block-type-heading-input[data-level=h3]{--text-size: var(--text-xl)}.k-block-type-heading-input[data-level=h4]{--text-size: var(--text-lg)}.k-block-type-heading-input[data-level=h5]{--text-size: var(--text-md);line-height:1.5em}.k-block-type-heading-input[data-level=h6]{--text-size: var(--text-sm);line-height:1.5em}.k-block-type-heading-input .k-writer .ProseMirror strong{font-weight:700}.k-block-type-heading-level{--input-color-back: transparent;--input-color-border: none;--input-color-text: var(--color-gray-600);font-weight:var(--font-bold);text-transform:uppercase}.k-block-container.k-block-container-type-image{padding:0}.k-block-type-image .k-block-figure{padding:var(--spacing-3);border-radius:var(--rounded)}.k-block-type-image .k-block-figure-container{text-align:center;line-height:0}.k-block-type-image .k-block-figure[data-empty=true]{padding:var(--spacing-3)}.k-block-type-image-auto{max-width:100%;max-height:30rem;margin-inline:auto}.k-block-type-image .k-background-dropdown{position:absolute;inset-inline-end:var(--spacing-3);bottom:var(--spacing-3);opacity:0;transition:opacity .2s ease-in-out}.k-block-type-image:hover .k-background-dropdown{opacity:1}.k-block-type-line hr{margin-block:.75rem;border:0;border-top:1px solid var(--color-border)}.k-block-type-list-input{--input-color-back: transparent;--input-color-border: none;--input-outline-focus: none}.k-block-type-markdown-input{--input-color-back: var(--color-light);--input-color-border: none;--input-outline-focus: none;--input-padding-multiline: var(--spacing-3)}.k-block-type-quote-editor{padding-inline-start:var(--spacing-3);border-inline-start:2px solid var(--color-black)}.k-block-type-quote-text{font-size:var(--text-xl);margin-bottom:var(--spacing-1);line-height:1.25em}.k-block-type-quote-citation{font-style:italic;color:var(--color-text-dimmed)}.k-block-type-table-preview{cursor:pointer;border:1px solid var(--color-gray-300);border-spacing:0;border-radius:var(--rounded-sm)}.k-block-type-table-preview :where(th,td){text-align:start;line-height:1.5em;font-size:var(--text-sm)}.k-block-type-table-preview th{padding:.5rem .75rem}.k-block-type-table-preview td:not(.k-table-index-column){padding:0 .75rem}.k-block-type-table-preview td>*,.k-block-type-table-preview td [class$=-field-preview]{padding:0}.k-block-type-text-input{line-height:1.5;height:100%}.k-block-container.k-block-container-type-text{padding:0}.k-block-type-text-input.k-writer[data-toolbar-inline=true]{padding:var(--spacing-3)}.k-block-type-text-input.k-writer:not([data-toolbar-inline=true])>.ProseMirror,.k-block-type-text-input.k-writer:not([data-toolbar-inline=true])[data-placeholder][data-empty=true]:before{padding:var(--spacing-3) var(--spacing-6)}.k-block-type-text-input.k-textarea-input .k-textarea-input-native{padding:var(--input-padding-multiline)}.k-block-type-video-figure video{pointer-events:none}.k-blocks-field{position:relative}.k-blocks-field>footer{display:flex;justify-content:center;margin-top:var(--spacing-3)}.k-string-input{padding:var(--input-padding);border-radius:var(--input-rounded)}.k-string-input:focus{outline:0}.k-string-input[data-font=monospace]{font-family:var(--font-mono)}.k-color-field{--color-frame-size: calc(var(--input-height) - var(--spacing-2))}.k-color-field .k-input-before{align-items:center;padding-inline-start:var(--spacing-1)}.k-color-field-options{--color-frame-size: var(--input-height)}.k-color-field-picker{padding:var(--spacing-3)}.k-color-field-picker-toggle{--color-frame-rounded: var(--rounded-sm);border-radius:var(--color-frame-rounded)}.k-color-field .k-colorname-input{padding-inline:var(--input-padding)}.k-color-field .k-colorname-input:focus{outline:0}.k-date-input:disabled::placeholder{opacity:0}.k-date-field-body{display:grid;gap:var(--spacing-2)}@container (min-width: 20rem){.k-date-field-body[data-has-time=true]{grid-template-columns:1fr minmax(6rem,9rem)}}.k-text-input{padding:var(--input-padding);border-radius:var(--input-rounded)}.k-text-input:focus{outline:0}.k-text-input[data-font=monospace]{font-family:var(--font-mono)}.k-text-input:disabled::placeholder{opacity:0}.k-models-field[data-disabled=true] .k-item *{pointer-events:all!important}.k-headline-field{position:relative;padding-top:1.5rem}.k-fieldset>.k-grid .k-column:first-child .k-headline-field{padding-top:0}.k-headline-field h2.k-headline{font-weight:var(--font-normal)}.k-headline-field footer{margin-top:var(--spacing-2)}.k-info-field .k-headline{padding-bottom:.75rem;line-height:1.25rem}.k-layout-column{position:relative;height:100%;display:flex;flex-direction:column;background:var(--color-white);min-height:6rem}.k-layout-column:focus{outline:0}.k-layout-column>.k-blocks{background:none;box-shadow:none;padding:0;height:100%;background:var(--color-white);min-height:4rem}.k-layout-column>.k-blocks[data-empty=true]{min-height:6rem}.k-layout-column>.k-blocks>.k-blocks-list{display:flex;flex-direction:column;height:100%}.k-layout-column>.k-blocks>.k-blocks-list>.k-block-container:last-of-type{flex-grow:1}.k-layout-column>.k-blocks>.k-blocks-list>.k-blocks-empty.k-box{--box-color-back: transparent;position:absolute;top:0;right:0;bottom:0;left:0;justify-content:center;opacity:0;transition:opacity .3s;border:0}.k-layout-column>.k-blocks>.k-blocks-list>.k-blocks-empty:hover{opacity:1}.k-layout{--layout-border-color: var(--color-gray-300);--layout-toolbar-width: 2rem;position:relative;padding-inline-end:var(--layout-toolbar-width);background:#fff;box-shadow:var(--shadow)}[data-disabled=true] .k-layout{padding-inline-end:0}.k-layout:not(:last-of-type){margin-bottom:1px}.k-layout:focus{outline:0}.k-layout-toolbar{position:absolute;inset-block:0;inset-inline-end:0;width:var(--layout-toolbar-width);display:flex;flex-direction:column;align-items:center;justify-content:space-between;padding-bottom:var(--spacing-2);font-size:var(--text-sm);background:var(--color-gray-100);border-inline-start:1px solid var(--color-light);color:var(--color-gray-500)}.k-layout-toolbar:hover{color:var(--color-black)}.k-layout-toolbar-button{width:var(--layout-toolbar-width);height:var(--layout-toolbar-width)}.k-layout-columns.k-grid{grid-gap:1px;background:var(--layout-border-color);background:var(--color-gray-300)}.k-layout:not(:first-child) .k-layout-columns.k-grid{border-top:0}.k-layouts .k-sortable-ghost{position:relative;box-shadow:#11111140 0 5px 10px;outline:2px solid var(--color-focus);cursor:grabbing;z-index:1}.k-layout-field>footer{display:flex;justify-content:center;margin-top:var(--spacing-3)}.k-line-field{position:relative;border:0;height:3rem;width:auto}.k-line-field:after{position:absolute;content:"";top:50%;margin-top:-1px;inset-inline:0;height:1px;background:var(--color-border)}.k-link-input-header{display:grid;grid-template-columns:max-content minmax(0,1fr);align-items:center;gap:.25rem;height:var(--input-height);grid-area:header}.k-link-input-toggle.k-button{--button-height: var(--height-sm);--button-rounded: var(--rounded-sm);--button-color-back: var(--color-gray-200);margin-inline-start:.25rem}.k-link-input-model{display:flex;justify-content:space-between;margin-inline-end:var(--spacing-1)}.k-link-input-model-placeholder.k-button{--button-align: flex-start;--button-color-text: var(--color-gray-600);--button-height: var(--height-sm);--button-padding: var(--spacing-2);--button-rounded: var(--rounded-sm);flex-grow:1;overflow:hidden;white-space:nowrap;align-items:center}.k-link-field .k-link-field-preview{--tag-height: var(--height-sm);padding-inline:0}.k-link-field .k-link-field-preview .k-tag:focus{outline:0}.k-link-field .k-link-field-preview .k-tag:focus-visible{outline:var(--outline)}.k-link-field .k-link-field-preview .k-tag-text{font-size:var(--text-sm)}.k-link-input-model-toggle{align-self:center;--button-height: var(--height-sm);--button-width: var(--height-sm);--button-rounded: var(--rounded-sm)}.k-link-input-body{display:grid;overflow:hidden;border-top:1px solid var(--color-gray-300);background:var(--color-gray-100);--tree-color-back: var(--color-gray-100);--tree-color-hover-back: var(--color-gray-200)}.k-link-input-body[data-type=page] .k-page-browser{padding:var(--spacing-2);padding-bottom:calc(var(--spacing-2) - 1px);width:100%;container-type:inline-size;overflow:auto}.k-link-field .k-bubbles-field-preview{--bubble-rounded: var(--rounded-sm);--bubble-size: var(--height-sm);padding-inline:0}.k-link-field .k-bubbles-field-preview .k-bubble{font-size:var(--text-sm)}.k-link-field[data-disabled=true] .k-link-input-model-placeholder{display:none}.k-link-field[data-disabled=true] input::placeholder{opacity:0}.k-writer{position:relative;width:100%;display:grid;grid-template-areas:"content";gap:var(--spacing-1)}.k-writer .ProseMirror{overflow-wrap:break-word;word-wrap:break-word;word-break:break-word;white-space:pre-wrap;font-variant-ligatures:none;grid-area:content;padding:var(--input-padding-multiline)}.k-writer .ProseMirror:focus{outline:0}.k-writer .ProseMirror *{caret-color:currentColor}.k-writer .ProseMirror hr.ProseMirror-selectednode{outline:var(--outline)}.k-writer[data-placeholder][data-empty=true]:before{grid-area:content;content:attr(data-placeholder);color:var(--input-color-placeholder);pointer-events:none;white-space:pre-wrap;word-wrap:break-word;line-height:var(--text-line-height);padding:var(--input-padding-multiline)}.k-list-input.k-writer[data-placeholder][data-empty=true]:before{padding-inline-start:2.5em}.k-list-field .k-list-input .ProseMirror,.k-list-field .k-list-input:before{padding:.475rem .5rem .475rem .75rem}:root{--tags-gap: .375rem}.k-tags{display:inline-flex;gap:var(--tags-gap);align-items:center;flex-wrap:wrap}.k-tags .k-sortable-ghost{outline:var(--outline)}.k-tags[data-layout=list],.k-tags[data-layout=list] .k-tag{width:100%}.k-tags.k-draggable .k-tag-text{cursor:grab}.k-tags.k-draggable .k-tag-text:active{cursor:grabbing}.k-multiselect-input{padding:var(--tags-gap);cursor:pointer}.k-multiselect-input-toggle.k-button{opacity:0}.k-tags-input{padding:var(--tags-gap)}.k-tags-input[data-can-add=true]{cursor:pointer}.k-tags-input-toggle.k-button{--button-color-text: var(--input-color-placeholder);opacity:0}.k-tags-input-toggle.k-button:focus{--button-color-text: var(--input-color-text)}.k-tags-input:focus-within .k-tags-input-toggle{opacity:1}.k-tags-input .k-picklist-dropdown{margin-top:var(--spacing-1)}.k-tags-input .k-picklist-dropdown .k-choice-input:focus-within{outline:var(--outline)}.k-number-input{padding:var(--input-padding);border-radius:var(--input-rounded)}.k-number-input:focus{outline:0}.k-table.k-object-field-table{table-layout:auto}.k-table.k-object-field-table tbody td{max-width:0}@container (max-width: 40rem){.k-object-field{overflow:hidden}.k-object-field-table.k-table tbody :where(th):is([data-mobile=true]){width:1px!important;white-space:normal;word-break:normal}}.k-range-input{--range-track-height: 1px;--range-track-back: var(--color-gray-300);--range-tooltip-back: var(--color-black);display:flex;align-items:center;border-radius:var(--range-track-height)}.k-range-input input[type=range]:focus{outline:0}.k-range-input-tooltip{position:relative;display:flex;align-items:center;color:var(--color-white);font-size:var(--text-xs);font-variant-numeric:tabular-nums;line-height:1;text-align:center;border-radius:var(--rounded-sm);background:var(--range-tooltip-back);margin-inline-start:var(--spacing-3);padding:0 var(--spacing-1);white-space:nowrap}.k-range-input-tooltip:after{position:absolute;top:50%;inset-inline-start:-3px;width:0;height:0;transform:translateY(-50%);border-block:3px solid transparent;border-inline-end:3px solid var(--range-tooltip-back);content:""}.k-range-input-tooltip>*{padding:var(--spacing-1)}.k-range-input[data-disabled=true]{--range-tooltip-back: var(--color-gray-600)}.k-input[data-type=range] .k-range-input{padding-inline:var(--input-padding)}.k-select-input{position:relative;display:block;overflow:hidden;padding:var(--input-padding);border-radius:var(--input-rounded)}.k-select-input[data-empty=true]{color:var(--input-color-placeholder)}.k-select-input-native{position:absolute;top:0;right:0;bottom:0;left:0;opacity:0;z-index:1}.k-select-input-native[disabled]{cursor:default}.k-input[data-type=select]{position:relative}.k-input[data-type=select] .k-input-icon{position:absolute;inset-block:0;inset-inline-end:0}.k-structure-field:not([data-disabled=true]) td.k-table-column{cursor:pointer}.k-structure-field .k-table+footer{display:flex;justify-content:center;margin-top:var(--spacing-3)}.k-field-counter{display:none}.k-text-field:focus-within .k-field-counter{display:block}.k-toolbar.k-textarea-toolbar{border-end-start-radius:0;border-end-end-radius:0;border-bottom:1px solid var(--toolbar-border)}.k-toolbar.k-textarea-toolbar>.k-button:first-child{border-end-start-radius:0}.k-toolbar.k-textarea-toolbar>.k-button:last-child{border-end-end-radius:0}.k-textarea-input[data-size=small]{--textarea-size: 7.5rem}.k-textarea-input[data-size=medium]{--textarea-size: 15rem}.k-textarea-input[data-size=large]{--textarea-size: 30rem}.k-textarea-input[data-size=huge]{--textarea-size: 45rem}.k-textarea-input-wrapper{position:relative;display:block}.k-textarea-input-native{resize:none;min-height:var(--textarea-size)}.k-textarea-input-native:focus{outline:0}.k-textarea-input-native[data-font=monospace]{font-family:var(--font-mono)}.k-input[data-type=textarea] .k-input-element{min-width:0}.k-input[data-type=textarea] .k-textarea-input-native{padding:var(--input-padding-multiline)}.k-time-input:disabled::placeholder{opacity:0}.k-input[data-type=toggle]{--input-color-border: transparent;--input-shadow: var(--shadow)}.k-input[data-type=toggle] .k-input-before{padding-inline-end:calc(var(--input-padding) / 2)}.k-input[data-type=toggle] .k-toggle-input{padding-inline-start:var(--input-padding)}.k-input[data-type=toggle][data-disabled]{box-shadow:none;border:1px solid var(--color-border)}.k-input[data-type=toggles]{display:inline-flex}.k-input[data-type=toggles].grow{display:flex}.k-input[data-type=toggles]:has(.k-empty){outline:0;display:flex}.k-toggles-input{display:grid;grid-template-columns:repeat(var(--options),minmax(0,1fr));gap:1px;border-radius:var(--rounded);line-height:1;background:var(--color-border);overflow:hidden}.k-toggles-input li{height:var(--field-input-height);background:var(--color-white)}.k-toggles-input label{align-items:center;background:var(--color-white);cursor:pointer;display:flex;font-size:var(--text-sm);justify-content:center;line-height:1.25;padding:0 var(--spacing-3);height:100%}.k-toggles-input li[data-disabled=true] label{color:var(--color-text-dimmed);background:var(--color-light)}.k-toggles-input .k-icon+.k-toggles-text{margin-inline-start:var(--spacing-2)}.k-toggles-input input:focus:not(:checked)+label{background:var(--color-blue-200)}.k-toggles-input input:checked+label{background:var(--color-black);color:var(--color-white)}.k-alpha-input{--range-track-back: linear-gradient(to right, transparent, currentColor);--range-track-height: var(--range-thumb-size);color:#000;background:var(--color-white) var(--pattern-light)}.k-calendar-input{--button-height: var(--height-sm);--button-width: var(--button-height);--button-padding: 0;padding:var(--spacing-2);width:min-content}.k-calendar-table{table-layout:fixed;min-width:15rem}.k-calendar-input .k-button{justify-content:center}.k-calendar-input>nav{display:flex;direction:ltr;align-items:center;margin-bottom:var(--spacing-2)}.k-calendar-selects{flex-grow:1;display:flex;align-items:center;justify-content:center}[dir=ltr] .k-calendar-selects{direction:ltr}[dir=rtl] .k-calendar-selects{direction:rtl}.k-calendar-selects .k-select-input{display:flex;align-items:center;text-align:center;height:var(--button-height);padding:0 .5rem;border-radius:var(--input-rounded)}.k-calendar-selects .k-select-input:focus-within{outline:var(--outline)}.k-calendar-input th{padding-block:.5rem;color:var(--color-gray-500);font-size:var(--text-xs);text-align:center}.k-calendar-day{padding:2px}.k-calendar-day[aria-current=date] .k-button{text-decoration:underline}.k-calendar-day[aria-selected=date] .k-button,.k-calendar-day[aria-selected=date] .k-button:focus{--button-color-text: var(--color-text);--button-color-back: var(--color-blue-500)}.k-calendar-day[aria-selected=date] .k-button:focus-visible{outline-offset:2px}.k-calendar-today{padding-top:var(--spacing-2);text-align:center}.k-calendar-today .k-button{--button-width: auto;--button-padding: var(--spacing-3);font-size:var(--text-xs);text-decoration:underline}.k-choice-input{display:flex;gap:var(--spacing-3);min-width:0}.k-choice-input input{top:2px}.k-choice-input-label{display:flex;line-height:1.25rem;flex-direction:column;min-width:0;color:var(--choice-color-text)}.k-choice-input-label>*{display:block;overflow:hidden;text-overflow:ellipsis}.k-choice-input-label-info{color:var(--choice-color-info)}.k-choice-input[aria-disabled]{cursor:not-allowed}:where(.k-checkboxes-field,.k-radio-field) .k-choice-input{min-height:var(--input-height);padding-block:var(--spacing-2);padding-inline:var(--spacing-3);border-radius:var(--input-rounded)}:where(.k-checkboxes-field,.k-radio-field) .k-choice-input:not([aria-disabled=true]){background:var(--input-color-back);box-shadow:var(--shadow)}:where(.k-checkboxes-field,.k-radio-field) .k-choice-input[aria-disabled=true]{border:1px solid var(--input-color-border)}.k-coloroptions-input{--color-preview-size: var(--input-height)}.k-coloroptions-input ul{display:grid;grid-template-columns:repeat(auto-fill,var(--color-preview-size));gap:var(--spacing-2)}.k-coloroptions-input input:focus+.k-color-frame{outline:var(--outline)}.k-coloroptions-input[disabled] label{opacity:var(--opacity-disabled);cursor:not-allowed}.k-coloroptions-input input:checked+.k-color-frame{outline:1px solid var(--color-gray-600);outline-offset:2px}.k-colorpicker-input{--h: 0;--s: 0%;--l: 0%;--a: 1;--range-thumb-size: .75rem;--range-track-height: .75rem;display:flex;flex-direction:column;gap:var(--spacing-3);width:max-content}.k-colorpicker-input .k-coords-input{border-radius:var(--rounded-sm);aspect-ratio:1/1;background:linear-gradient(to bottom,transparent,#000),linear-gradient(to right,#fff,hsl(var(--h),100%,50%))}.k-colorpicker-input .k-alpha-input{color:hsl(var(--h),var(--s),var(--l))}.k-colorpicker-input .k-coloroptions-input ul{grid-template-columns:repeat(6,1fr)}.k-coords-input{position:relative;display:block!important}.k-coords-input img{width:100%}.k-coords-input-thumb{position:absolute;aspect-ratio:1/1;width:var(--range-thumb-size);background:var(--range-thumb-color);border-radius:var(--range-thumb-size);box-shadow:var(--range-thumb-shadow);transform:translate(-50%,-50%);cursor:move}.k-coords-input[data-empty] .k-coords-input-thumb{opacity:0}.k-coords-input-thumb:active{cursor:grabbing}.k-coords-input:focus-within{outline:var(--outline)}.k-coords-input[aria-disabled]{pointer-events:none;opacity:var(--opacity-disabled)}.k-coords-input .k-coords-input-thumb:focus{outline:var(--outline)}.k-hue-input{--range-track-back: linear-gradient( to right, hsl(0, 100%, 50%) 0%, hsl(60, 100%, 50%) 16.67%, hsl(120, 100%, 50%) 33.33%, hsl(180, 100%, 50%) 50%, hsl(240, 100%, 50%) 66.67%, hsl(320, 100%, 50%) 83.33%, hsl(360, 100%, 50%) 100% ) no-repeat;--range-track-height: var(--range-thumb-size)}.k-timeoptions-input{--button-height: var(--height-sm);display:grid;grid-template-columns:1fr 1fr;gap:var(--spacing-3)}.k-timeoptions-input h3{display:flex;align-items:center;padding-inline:var(--button-padding);height:var(--button-height);margin-bottom:var(--spacing-1)}.k-timeoptions-input hr{margin:var(--spacing-2) var(--spacing-3)}.k-timeoptions-input .k-button[aria-selected=time]{--button-color-text: var(--color-text);--button-color-back: var(--color-blue-500)}.k-layout-selector h3{margin-top:-.5rem;margin-bottom:var(--spacing-3)}.k-layout-selector-options{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--spacing-6)}@media screen and (min-width: 65em){.k-layout-selector-options{grid-template-columns:repeat(var(--columns),1fr)}}.k-layout-selector-option{--color-border: hsla(var(--color-gray-hs), 0%, 6%);--color-back: var(--color-white);border-radius:var(--rounded)}.k-layout-selector-option:focus-visible{outline:var(--outline);outline-offset:-1px}.k-layout-selector-option .k-grid{border:1px solid var(--color-border);gap:1px;grid-template-columns:repeat(var(--columns),1fr);cursor:pointer;background:var(--color-border);border-radius:var(--rounded);overflow:hidden;box-shadow:var(--shadow);height:5rem}.k-layout-selector-option .k-column{grid-column:span var(--span);background:var(--color-back);height:100%}.k-layout-selector-option:hover{--color-border: var(--color-gray-500);--color-back: var(--color-gray-100)}.k-layout-selector-option[aria-current]{--color-border: var(--color-focus);--color-back: var(--color-blue-300)}.k-bubbles{display:flex;gap:.25rem}.k-bubbles-field-preview{--bubble-back: var(--color-light);--bubble-text: var(--color-black);padding:.375rem var(--table-cell-padding);overflow:hidden}.k-bubbles-field-preview .k-bubbles{gap:.375rem}.k-color-field-preview{--color-frame-rounded: var(--tag-rounded);--color-frame-size: var(--tag-height);padding:.375rem var(--table-cell-padding);display:flex;align-items:center;gap:var(--spacing-2)}.k-text-field-preview{padding:.325rem .75rem;overflow-x:hidden;text-overflow:ellipsis;white-space:nowrap}.k-url-field-preview{padding-inline:var(--table-cell-padding)}.k-url-field-preview[data-link]{color:var(--link-color)}.k-url-field-preview a{display:inline-flex;align-items:center;height:var(--height-xs);padding-inline:var(--spacing-1);margin-inline:calc(var(--spacing-1) * -1);border-radius:var(--rounded);max-width:100%;min-width:0}.k-url-field-preview a>*{overflow:hidden;white-space:nowrap;text-overflow:ellipsis;text-decoration:underline;text-underline-offset:var(--link-underline-offset)}.k-url-field-preview a:hover{color:var(--color-black)}.k-flag-field-preview{--button-height: var(--table-row-height);--button-width: 100%;outline-offset:-2px}.k-html-field-preview{padding:.375rem var(--table-cell-padding);overflow:hidden;text-overflow:ellipsis}.k-image-field-preview{height:100%}.k-link-field-preview{--tag-height: var(--height-xs);--tag-color-back: var(--color-gray-200);--tag-color-text: var(--color-black);--tag-color-toggle: var(--tag-color-text);--tag-color-toggle-border: var(--color-gray-300);--tag-color-focus-back: var(--tag-color-back);--tag-color-focus-text: var(--tag-color-text);padding-inline:var(--table-cell-padding);min-width:0}.k-link-field-preview .k-tag{min-width:0;max-width:100%}.k-link-field-preview .k-tag-text{font-size:var(--text-xs);min-width:0}.k-toggle-field-preview{padding-inline:var(--table-cell-padding)}:root{--toolbar-size: var(--height);--toolbar-text: var(--color-black);--toolbar-back: var(--color-white);--toolbar-hover: rgba(239, 239, 239, .5);--toolbar-border: rgba(0, 0, 0, .1);--toolbar-current: var(--color-focus)}.k-toolbar{display:flex;max-width:100%;height:var(--toolbar-size);align-items:center;overflow-x:auto;overflow-y:hidden;color:var(--toolbar-text);background:var(--toolbar-back);border-radius:var(--rounded)}.k-toolbar[data-theme=dark]{--toolbar-text: var(--color-white);--toolbar-back: var(--color-black);--toolbar-hover: rgba(255, 255, 255, .2);--toolbar-border: var(--color-gray-800)}.k-toolbar>hr{height:var(--toolbar-size);width:1px;border-left:1px solid var(--toolbar-border)}.k-toolbar-button.k-button{--button-width: var(--toolbar-size);--button-height: var(--toolbar-size);--button-rounded: 0;outline-offset:-2px}.k-toolbar-button:hover{--button-color-back: var(--toolbar-hover)}.k-toolbar .k-button[aria-current]{--button-color-text: var(--toolbar-current)}.k-toolbar>.k-button:first-child{border-start-start-radius:var(--rounded);border-end-start-radius:var(--rounded)}.k-toolbar>.k-button:last-child{border-start-end-radius:var(--rounded);border-end-end-radius:var(--rounded)}:where(.k-textarea-input,.k-writer-input):not(:focus-within){--toolbar-text: var(--color-gray-400);--toolbar-border: var(--color-background)}:where(.k-textarea-input,.k-writer-input):focus-within .k-toolbar:not([data-inline=true]){position:sticky;top:var(--header-sticky-offset);inset-inline:0;z-index:1;box-shadow:#0000000d 0 2px 5px}.k-writer:not([data-toolbar-inline=true]):not([data-disabled=true]){grid-template-areas:"topbar" "content";grid-template-rows:var(--toolbar-size) 1fr;gap:0}.k-writer:not(:focus-within){--toolbar-current: currentColor}.k-writer-toolbar[data-inline=true]{position:absolute;z-index:calc(var(--z-dropdown) + 1);max-width:none;box-shadow:var(--shadow-toolbar)}.k-writer-toolbar:not([data-inline=true]){border-end-start-radius:0;border-end-end-radius:0;border-bottom:1px solid var(--toolbar-border)}.k-writer-toolbar:not([data-inline=true])>.k-button:first-child{border-end-start-radius:0}.k-writer-toolbar:not([data-inline=true])>.k-button:last-child{border-end-end-radius:0}.k-aspect-ratio{position:relative;display:block;overflow:hidden;padding-bottom:100%}.k-aspect-ratio>*{position:absolute!important;top:0;right:0;bottom:0;left:0;height:100%;width:100%;object-fit:contain}.k-aspect-ratio[data-cover=true]>*{object-fit:cover}:root{--bar-height: var(--height-xs)}.k-bar{display:flex;align-items:center;gap:var(--spacing-3);height:var(--bar-height);justify-content:space-between}.k-bar:where([data-align=center]){justify-content:center}.k-bar:where([data-align=end]):has(:first-child:last-child){justify-content:end}.k-bar-slot{flex-grow:1}.k-bar-slot[data-position=center]{text-align:center}.k-bar-slot[data-position=right]{text-align:end}:root{--box-height: var( --field-input-height );--box-padding-inline: var(--spacing-2);--box-font-size: var(--text-sm);--box-color-back: none;--box-color-text: currentColor}.k-box{--icon-color: var(--box-color-icon);--text-font-size: var(--box-font-size);display:flex;width:100%;align-items:center;gap:var(--spacing-2);color:var(--box-color-text);background:var(--box-color-back);word-wrap:break-word}.k-box[data-theme]{--box-color-back: var(--theme-color-back);--box-color-text: var(--theme-color-text);--box-color-icon: var(--theme-color-700);min-height:var(--box-height);line-height:1.25;padding:.375rem var(--box-padding-inline);border-radius:var(--rounded)}.k-box[data-theme=text],.k-box[data-theme=white]{box-shadow:var(--shadow)}.k-box[data-theme=text]{padding:var(--spacing-6)}.k-box[data-theme=none]{padding:0}.k-box[data-align=center]{justify-content:center}:root{--bubble-size: 1.525rem;--bubble-back: var(--color-light);--bubble-rounded: var(--rounded-sm);--bubble-text: var(--color-black)}.k-bubble{width:min-content;height:var(--bubble-size);white-space:nowrap;line-height:1.5;background:var(--bubble-back);color:var(--bubble-text);border-radius:var(--bubble-rounded);overflow:hidden}.k-bubble .k-frame{width:var(--bubble-size);height:var(--bubble-size)}.k-bubble[data-has-text=true]{display:flex;gap:var(--spacing-2);align-items:center;padding-inline-end:.5rem;font-size:var(--text-xs)}.k-column{min-width:0}.k-column[data-sticky=true]{align-self:stretch}.k-column[data-sticky=true]>div{position:sticky;top:calc(var(--header-sticky-offset) + 2vh);z-index:2}.k-column[data-disabled=true]{cursor:not-allowed;opacity:.4}.k-column[data-disabled=true] *{pointer-events:none}.k-column[data-disabled=true] .k-text[data-theme=help] *{pointer-events:initial}.k-frame{--fit: contain;--ratio: 1/1;position:relative;display:flex;justify-content:center;align-items:center;aspect-ratio:var(--ratio);background:var(--back);overflow:hidden}.k-frame:where([data-theme]){--back: var(--theme-color-back);color:var(--theme-color-text)}.k-frame *:where(img,video,iframe,button){position:absolute;top:0;right:0;bottom:0;left:0;height:100%;width:100%;object-fit:var(--fit)}.k-frame>*{overflow:hidden;text-overflow:ellipsis;min-width:0;min-height:0}:root{--color-frame-back: none;--color-frame-rounded: var(--rounded);--color-frame-size: 100%;--color-frame-darkness: 0%}.k-color-frame.k-frame{background:var(--pattern-light);width:var(--color-frame-size);color:transparent;border-radius:var(--color-frame-rounded);overflow:hidden;background-clip:padding-box}.k-color-frame:after{border-radius:var(--color-frame-rounded);box-shadow:0 0 0 1px inset hsla(0,0%,var(--color-frame-darkness),.175);position:absolute;top:0;right:0;bottom:0;left:0;background:var(--color-frame-back);content:""}.k-dropzone{position:relative}.k-dropzone:after{content:"";position:absolute;top:0;right:0;bottom:0;left:0;display:none;pointer-events:none;z-index:1;border-radius:var(--rounded)}.k-dropzone[data-over=true]:after{display:block;background:hsla(var(--color-blue-hs),var(--color-blue-l-300),.6);outline:var(--outline)}.k-grid{--columns: 12;--grid-inline-gap: 0;--grid-block-gap: 0;display:grid;align-items:start;grid-column-gap:var(--grid-inline-gap);grid-row-gap:var(--grid-block-gap)}.k-grid>*{--width: calc(1 / var(--columns));--span: calc(var(--columns) * var(--width))}@container (min-width: 30rem){.k-grid{grid-template-columns:repeat(var(--columns),1fr)}.k-grid>*{grid-column:span var(--span)}.k-grid[data-gutter=small]{--grid-inline-gap: 1rem;--grid-block-gap: 1rem}.k-grid:where([data-gutter=medium],[data-gutter=large],[data-gutter=huge]){--grid-inline-gap: 1.5rem;--grid-block-gap: 1.5rem}}@container (min-width: 65em){.k-grid[data-gutter=large]{--grid-inline-gap: 3rem}.k-grid[data-gutter=huge]{--grid-inline-gap: 4.5rem}}@container (min-width: 90em){.k-grid[data-gutter=large]{--grid-inline-gap: 4.5rem}.k-grid[data-gutter=huge]{--grid-inline-gap: 6rem}}@container (min-width: 120em){.k-grid[data-gutter=large]{--grid-inline-gap: 6rem}.k-grid[data-gutter=huge]{--grid-inline-gap: 7.5rem}}:root{--columns-inline-gap: clamp(.75rem, 6cqw, 6rem);--columns-block-gap: var(--spacing-8)}.k-grid[data-variant=columns]{--grid-inline-gap: var(--columns-inline-gap);--grid-block-gap: var(--columns-block-gap)}.k-grid:where([data-variant=columns],[data-variant=fields])>*{container:column / inline-size}.k-grid[data-variant=fields]{gap:var(--spacing-8)}.k-grid[data-variant=choices]{align-items:stretch;gap:2px}:root{--header-color-back: var(--color-light);--header-padding-block: var(--spacing-4);--header-sticky-offset: var(--scroll-top)}.k-header{position:relative;display:flex;flex-wrap:wrap;align-items:baseline;justify-content:space-between;border-bottom:1px solid var(--color-border);background:var(--header-color-back);padding-top:var(--header-padding-block);margin-bottom:var(--spacing-12);box-shadow:2px 0 0 0 var(--header-color-back),-2px 0 0 0 var(--header-color-back)}.k-header-title{font-size:var(--text-h1);font-weight:var(--font-h1);line-height:var(--leading-h1);margin-bottom:var(--header-padding-block);min-width:0}.k-header-title-button{display:inline-flex;text-align:start;gap:var(--spacing-2);align-items:baseline;max-width:100%;outline:0}.k-header-title-text{overflow-x:clip;text-overflow:ellipsis}.k-header-title-icon{--icon-color: var(--color-text-dimmed);border-radius:var(--rounded);transition:opacity .2s;display:grid;flex-shrink:0;place-items:center;height:var(--height-sm);width:var(--height-sm);opacity:0}.k-header-title-button:is(:hover,:focus) .k-header-title-icon{opacity:1}.k-header-title-button:is(:focus) .k-header-title-icon{outline:var(--outline)}.k-header-buttons{display:flex;flex-shrink:0;gap:var(--spacing-2);margin-bottom:var(--header-padding-block)}.k-header[data-has-buttons=true]{position:sticky;top:var(--scroll-top);z-index:var(--z-toolbar)}:root:has(.k-header[data-has-buttons=true]){--header-sticky-offset: calc(var(--scroll-top) + 4rem)}:root{--icon-size: 18px;--icon-color: currentColor}.k-icon{width:var(--icon-size);height:var(--icon-size);flex-shrink:0;color:var(--icon-color)}.k-icon[data-type=loader]{animation:Spin 1.5s linear infinite}@media only screen and (-webkit-min-device-pixel-ratio: 2),not all,not all,not all,only screen and (min-resolution: 192dpi),only screen and (min-resolution: 2dppx){.k-button-icon [data-type=emoji]{font-size:1.25em}}.k-icon-frame [data-type=emoji]{overflow:visible}.k-image[data-back=pattern]{--back: var(--color-black) var(--pattern)}.k-image[data-back=black]{--back: var(--color-black)}.k-image[data-back=white]{--back: var(--color-white);color:var(--color-gray-900)}:root{--overlay-color-back: var(--color-backdrop)}.k-overlay[open]{position:fixed;overscroll-behavior:contain;top:0;right:0;bottom:0;left:0;width:100%;height:100vh;height:100dvh;background:none;z-index:var(--z-dialog);transform:translateZ(0);overflow:hidden}.k-overlay[open]::backdrop{background:none}.k-overlay[open]>.k-portal{position:fixed;top:0;right:0;bottom:0;left:0;background:var(--overlay-color-back);overflow:auto}.k-overlay[open][data-type=dialog]>.k-portal{display:inline-flex}.k-overlay[open][data-type=dialog]>.k-portal>*{margin:auto}.k-overlay[open][data-type=drawer]>.k-portal{--overlay-color-back: rgba(0, 0, 0, .2);display:flex;align-items:stretch;justify-content:flex-end}html[data-overlay]{overflow:hidden}html[data-overlay] body{overflow:scroll}:root{--stat-value-text-size: var(--text-2xl);--stat-info-text-color: var(--color-text-dimmed)}.k-stat{display:flex;flex-direction:column;padding:var(--spacing-3) var(--spacing-6);background:var(--color-white);border-radius:var(--rounded);box-shadow:var(--shadow);line-height:var(--leading-normal)}.k-stat.k-link:hover{cursor:pointer;background:var(--color-gray-100)}.k-stat :where(dt,dd){display:block}.k-stat-value{order:1;font-size:var(--stat-value-text-size);margin-bottom:var(--spacing-1)}.k-stat-label{--icon-size: var(--text-sm);order:2;display:flex;justify-content:start;align-items:center;gap:var(--spacing-1);font-size:var(--text-xs)}.k-stat-info{order:3;font-size:var(--text-xs);color:var(--stat-info-text-color)}.k-stat:is([data-theme]) .k-stat-info{--stat-info-text-color: var(--theme-color-700)}.k-stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(14rem,1fr));grid-auto-rows:1fr;grid-gap:var(--spacing-2px)}.k-stats[data-size=small]{--stat-value-text-size: var(--text-md)}.k-stats[data-size=medium]{--stat-value-text-size: var(--text-xl)}.k-stats[data-size=large]{--stat-value-text-size: var(--text-2xl)}.k-stats[data-size=huge]{--stat-value-text-size: var(--text-3xl)}:root{--table-cell-padding: var(--spacing-3);--table-color-back: var(--color-white);--table-color-border: var(--color-background);--table-color-hover: var(--color-gray-100);--table-color-th-back: var(--color-gray-100);--table-color-th-text: var(--color-text-dimmed);--table-row-height: var(--input-height)}.k-table{position:relative;background:var(--table-color-back);box-shadow:var(--shadow);border-radius:var(--rounded)}.k-table table{table-layout:fixed}.k-table th,.k-table td{padding-inline:var(--table-cell-padding);height:var(--table-row-height);overflow:hidden;text-overflow:ellipsis;width:100%;border-inline-end:1px solid var(--table-color-border);line-height:1.25}.k-table tr>*:last-child{border-inline-end:0}.k-table th,.k-table tr:not(:last-child) td{border-block-end:1px solid var(--table-color-border)}.k-table :where(td,th)[data-align]{text-align:var(--align)}.k-table th{padding-inline:var(--table-cell-padding);font-family:var(--font-mono);font-size:var(--text-xs);color:var(--table-color-th-text);background:var(--table-color-th-back)}.k-table th[data-has-button]{padding:0}.k-table th button{padding-inline:var(--table-cell-padding);height:100%;width:100%;border-radius:var(--rounded);text-align:start}.k-table th button:focus-visible{outline-offset:-2px}.k-table thead th:first-child{border-start-start-radius:var(--rounded)}.k-table thead th:last-child{border-start-end-radius:var(--rounded)}.k-table thead th{position:sticky;top:var(--header-sticky-offset);inset-inline:0;z-index:1}.k-table tbody tr:hover td{background:var(--table-color-hover)}.k-table tbody th{width:auto;white-space:nowrap;overflow:visible;border-radius:0}.k-table tbody tr:first-child th{border-start-start-radius:var(--rounded)}.k-table tbody tr:last-child th{border-end-start-radius:var(--rounded);border-block-end:0}.k-table-row-ghost{background:var(--color-white);outline:var(--outline);border-radius:var(--rounded);margin-bottom:2px;cursor:grabbing}.k-table-row-fallback{opacity:0!important}.k-table .k-table-index-column{width:var(--table-row-height);text-align:center}.k-table .k-table-index{font-size:var(--text-xs);color:var(--color-text-dimmed);line-height:1.1em}.k-table .k-table-index-column .k-sort-handle{--button-width: 100%;display:none}.k-table tr.k-table-sortable-row:hover .k-table-index-column .k-table-index{display:none}.k-table tr.k-table-sortable-row:hover .k-table-index-column .k-sort-handle{display:flex}.k-table .k-table-options-column{padding:0;width:var(--table-row-height);text-align:center}.k-table .k-table-options-column .k-options-dropdown-toggle{--button-width: 100%;--button-height: 100%;outline-offset:-2px}.k-table-empty{color:var(--color-text-dimmed);font-size:var(--text-sm)}.k-table[aria-disabled=true]{--table-color-back: transparent;--table-color-border: var(--color-border);--table-color-hover: transparent;--table-color-th-back: transparent;border:1px solid var(--table-color-border);box-shadow:none}.k-table[aria-disabled=true] thead th{position:static}@container (max-width: 40rem){.k-table{overflow-x:auto}.k-table thead th{position:static}.k-table .k-options-dropdown-toggle{aspect-ratio:auto!important}.k-table :where(th,td):not(.k-table-index-column,.k-table-options-column,[data-column-id=image],[data-column-id=flag]){width:auto!important}.k-table :where(th,td):not([data-mobile=true]){display:none}}.k-table-pagination{border-top:1px solid var(--table-color-border);height:var(--table-row-height);background:var(--table-color-th-back);display:flex;justify-content:center;border-end-start-radius:var(--rounded);border-end-end-radius:var(--rounded)}.k-table-pagination>.k-button{--button-color-back: transparent;border-left:0!important}.k-table .k-table-cell{padding:0}.k-tabs{--button-height: var(--height-md);--button-padding: var(--spacing-2);display:flex;gap:var(--spacing-1);margin-bottom:var(--spacing-12);margin-inline:calc(var(--button-padding) * -1)}.k-tabs-tab{position:relative}.k-tab-button.k-button{margin-block:2px;overflow-x:visible}.k-tab-button[aria-current]:after{position:absolute;content:"";height:2px;inset-inline:var(--button-padding);bottom:-2px;background:currentColor}.k-tabs-badge{position:absolute;top:2px;font-variant-numeric:tabular-nums;inset-inline-end:var(--button-padding);transform:translate(75%);line-height:1.5;padding:0 var(--spacing-1);border-radius:1rem;text-align:center;font-size:10px;box-shadow:var(--shadow-md);background:var(--theme-color-back);border:1px solid var(--theme-color-500);color:var(--theme-color-text);z-index:1}.k-view{padding-inline:1.5rem}@container (min-width: 30rem){.k-view{padding-inline:3rem}}.k-view[data-align=center]{height:100vh;display:flex;align-items:center;justify-content:center;padding:0 3rem;overflow:auto}.k-view[data-align=center]>*{flex-basis:22.5rem}.k-fatal[open]{background:var(--overlay-color-back);padding:var(--spacing-6)}.k-fatal-box{position:relative;width:100%;box-shadow:var(--dialog-shadow);border-radius:var(--dialog-rounded);line-height:1;height:calc(100vh - 3rem);height:calc(100dvh - 3rem);display:flex;flex-direction:column;overflow:hidden}.k-fatal-iframe{border:0;width:100%;flex-grow:1;background:var(--color-white);padding:var(--spacing-3)}.k-icons{position:absolute;width:0;height:0}.k-loader{z-index:1}.k-loader-icon{animation:Spin .9s linear infinite}.k-notification{padding:.75rem 1.5rem;background:var(--color-gray-900);width:100%;line-height:1.25rem;color:var(--color-white);display:flex;flex-shrink:0;align-items:center}.k-notification[data-theme]{background:var(--theme-color-back);color:var(--color-black)}.k-notification p{flex-grow:1;word-wrap:break-word;overflow:hidden}.k-notification .k-button{display:flex;margin-inline-start:1rem}.k-offline-warning{position:fixed;top:0;right:0;bottom:0;left:0;z-index:var(--z-offline);background:var(--color-backdrop);display:flex;align-items:center;justify-content:center;line-height:1}.k-offline-warning p{display:flex;align-items:center;gap:.5rem;background:var(--color-white);box-shadow:var(--shadow);padding:.75rem;border-radius:var(--rounded)}.k-offline-warning p .k-icon{color:var(--color-red-400)}:root{--progress-height: var(--spacing-2);--progress-color-back: var(--color-gray-300);--progress-color-value: var(--color-focus)}progress{display:block;width:100%;height:var(--progress-height);border-radius:var(--progress-height);overflow:hidden;border:0}progress::-webkit-progress-bar{background:var(--progress-color-back)}progress::-webkit-progress-value{background:var(--progress-color-value);border-radius:var(--progress-height)}progress::-moz-progress-bar{background:var(--progress-color-value)}progress:not([value])::-webkit-progress-bar{background:var(--progress-color-value)}progress:not([value])::-moz-progress-bar{background:var(--progress-color-value)}.k-sort-handle{cursor:grab;z-index:1}.k-sort-handle:active{cursor:grabbing}.k-breadcrumb{--breadcrumb-divider: "/";overflow-x:clip;padding:2px}.k-breadcrumb ol{display:none;gap:.125rem;align-items:center}.k-breadcrumb ol li{display:flex;align-items:center;min-width:0;transition:flex-shrink .1s}.k-breadcrumb ol li:has(.k-icon){min-width:2.25rem}.k-breadcrumb ol li:not(:last-child):after{content:var(--breadcrumb-divider);opacity:.175;flex-shrink:0}.k-breadcrumb .k-icon[data-type=loader]{opacity:.5}.k-breadcrumb ol li:is(:hover,:focus-within){flex-shrink:0}.k-button.k-breadcrumb-link{flex-shrink:1;min-width:0;justify-content:flex-start}.k-breadcrumb-dropdown{display:grid}.k-breadcrumb-dropdown .k-dropdown-content{width:15rem}@container (min-width: 40em){.k-breadcrumb ol{display:flex}.k-breadcrumb-dropdown{display:none}}.k-browser{container-type:inline-size;font-size:var(--text-sm)}.k-browser-items{--browser-item-gap: 1px;--browser-item-size: 1fr;--browser-item-height: var(--height-sm);--browser-item-padding: .25rem;--browser-item-rounded: var(--rounded);display:grid;column-gap:var(--browser-item-gap);row-gap:var(--browser-item-gap);grid-template-columns:repeat(auto-fill,minmax(var(--browser-item-size),1fr))}.k-browser-item{display:flex;overflow:hidden;gap:.5rem;align-items:center;flex-shrink:0;height:var(--browser-item-height);padding-inline:calc(var(--browser-item-padding) + 1px);border-radius:var(--browser-item-rounded);white-space:nowrap;cursor:pointer}.k-browser-item-image{height:calc(var(--browser-item-height) - var(--browser-item-padding) * 2);aspect-ratio:1/1;border-radius:var(--rounded-sm);box-shadow:var(--shadow);flex-shrink:0}.k-browser-item-image.k-icon-frame{box-shadow:none;background:var(--color-white)}.k-browser-item-image svg{transform:scale(.8)}.k-browser-item input{position:absolute;box-shadow:var(--shadow);opacity:0;width:0}.k-browser-item[aria-selected]{background:var(--color-blue-300)}:root{--button-align: center;--button-height: var(--height-md);--button-width: auto;--button-color-back: none;--button-color-text: currentColor;--button-color-icon: currentColor;--button-padding: var(--spacing-2);--button-rounded: var(--spacing-1);--button-text-display: block;--button-icon-display: block}.k-button{position:relative;display:inline-flex;align-items:center;justify-content:var(--button-align);gap:.5rem;padding-inline:var(--button-padding);white-space:nowrap;line-height:1;border-radius:var(--button-rounded);background:var(--button-color-back);height:var(--button-height);width:var(--button-width);color:var(--button-color-text);font-variant-numeric:tabular-nums;overflow-x:clip;text-align:var(--button-align);flex-shrink:0}.k-button-icon{--icon-color: var(--button-color-icon);flex-shrink:0;display:var(--button-icon-display)}.k-button-text{text-overflow:ellipsis;overflow-x:clip;display:var(--button-text-display);min-width:0}.k-button:where([data-theme]){--button-color-icon: var(--theme-color-icon);--button-color-text: var(--theme-color-text)}.k-button:where([data-theme$=-icon]){--button-color-text: currentColor}.k-button:where([data-variant=dimmed]){--button-color-icon: var(--color-text);--button-color-text: var(--color-text-dimmed)}.k-button:where([data-variant=dimmed]):not([aria-disabled]):is(:hover,[aria-current]) .k-button-text{filter:brightness(75%)}.k-button:where([data-variant=dimmed][data-theme]){--button-color-icon: var(--theme-color-icon);--button-color-text: var(--theme-color-text-dimmed)}.k-button:where([data-variant=dimmed][data-theme$=-icon]){--button-color-text: var(--color-text-dimmed)}.k-button:where([data-variant=filled]){--button-color-back: var(--color-gray-300)}.k-button:where([data-variant=filled]):not([aria-disabled]):hover{filter:brightness(97%)}.k-button:where([data-variant=filled][data-theme]){--button-color-icon: var(--theme-color-700);--button-color-back: var(--theme-color-back)}.k-button:where([data-theme$=-icon][data-variant=filled]){--button-color-icon: hsl( var(--theme-color-hs), 57% );--button-color-back: var(--color-gray-300)}.k-button:not([data-has-text=true]){--button-padding: 0;aspect-ratio:1/1}@container (max-width: 30rem){.k-button[data-responsive=true][data-has-icon=true]{--button-padding: 0;aspect-ratio:1/1;--button-text-display: none}.k-button[data-responsive=text][data-has-text=true]{--button-icon-display: none}.k-button[data-responsive][data-has-icon=true] .k-button-arrow{display:none}}.k-button:not(button,a,summary,label,.k-link){pointer-events:none}.k-button:where([data-size=xs]){--button-height: var(--height-xs);--button-padding: .325rem}.k-button:where([data-size=sm]){--button-height: var(--height-sm);--button-padding: .5rem}.k-button:where([data-size=lg]){--button-height: var(--height-lg)}.k-button-arrow{width:max-content;margin-inline-start:-.25rem;margin-inline-end:-.125rem}.k-button:where([aria-disabled]){cursor:not-allowed}.k-button:where([aria-disabled])>*{opacity:var(--opacity-disabled)}.k-button-group{display:flex;flex-wrap:wrap;gap:.5rem;align-items:center}.k-button-group:where([data-layout=collapsed]){gap:0;flex-wrap:nowrap}.k-button-group[data-layout=collapsed]>.k-button[data-variant=filled]:not(:last-child){border-start-end-radius:0;border-end-end-radius:0}.k-button-group[data-layout=collapsed]>.k-button[data-variant=filled]:not(:first-child){border-start-start-radius:0;border-end-start-radius:0;border-left:1px solid var(--theme-color-500, var(--color-gray-400))}.k-button-group[data-layout=collapsed]>.k-button[data-variant=filled]:focus-visible{z-index:1;border-radius:var(--button-rounded)}.k-file-browser{container-type:inline-size;overflow:hidden}.k-file-browser-layout{display:grid;grid-template-columns:minmax(10rem,15rem) 1fr;grid-template-rows:1fr auto;grid-template-areas:"tree items" "tree pagination"}.k-file-browser-tree{grid-area:tree;padding:var(--spacing-2);border-right:1px solid var(--color-gray-300)}.k-file-browser-items{grid-area:items;padding:var(--spacing-2);background:var(--color-gray-100)}.k-file-browser-back-button{display:none}.k-file-browser-pagination{background:var(--color-gray-100);padding:var(--spacing-2);display:flex;justify-content:end}@container (max-width: 30rem){.k-file-browser-layout{grid-template-columns:minmax(0,1fr);min-height:10rem}.k-file-browser-back-button{width:100%;height:var(--height-sm);display:flex;align-items:center;justify-content:flex-start;padding-inline:.25rem;margin-bottom:.5rem;background:var(--color-gray-200);border-radius:var(--rounded)}.k-file-browser-tree{border-right:0}.k-file-browser-pagination{justify-content:start}.k-file-browser[data-view=files] .k-file-browser-layout{grid-template-rows:1fr auto;grid-template-areas:"items" "pagination"}.k-file-browser[data-view=files] .k-file-browser-tree,.k-file-browser[data-view=tree] .k-file-browser-items,.k-file-browser[data-view=tree] .k-file-browser-pagination{display:none}}:root{--tree-color-back: var(--color-gray-200);--tree-color-hover-back: var(--color-gray-300);--tree-color-selected-back: var(--color-blue-300);--tree-color-selected-text: var(--color-black);--tree-color-text: var(--color-gray-dimmed);--tree-level: 0;--tree-indentation: .6rem}.k-tree-branch{display:flex;align-items:center;padding-inline-start:calc(var(--tree-level) * var(--tree-indentation));margin-bottom:1px}.k-tree-branch[data-has-subtree=true]{inset-block-start:calc(var(--tree-level) * 1.5rem);z-index:calc(100 - var(--tree-level));background:var(--tree-color-back)}.k-tree-branch:hover,li[aria-current]>.k-tree-branch{--tree-color-text: var(--tree-color-selected-text);background:var(--tree-color-hover-back);border-radius:var(--rounded)}li[aria-current]>.k-tree-branch{background:var(--tree-color-selected-back)}.k-tree-toggle{--icon-size: 12px;width:1rem;aspect-ratio:1/1;display:grid;place-items:center;padding:0;border-radius:var(--rounded-sm);margin-inline-start:.25rem;flex-shrink:0}.k-tree-toggle:hover{background:#00000013}.k-tree-toggle[disabled]{visibility:hidden}.k-tree-folder{display:flex;height:var(--height-sm);border-radius:var(--rounded-sm);padding-inline:.25rem;width:100%;align-items:center;gap:.325rem;min-width:3rem;line-height:1.25;font-size:var(--text-sm)}@container (max-width: 15rem){.k-tree{--tree-indentation: .375rem}.k-tree-folder{padding-inline:.125rem}.k-tree-folder .k-icon{display:none}}.k-tree-folder>.k-frame{flex-shrink:0}.k-tree-folder-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:currentColor}.k-tree-folder[disabled]{opacity:var(--opacity-disabled)}.k-pagination{flex-shrink:0}.k-pagination-details{--button-padding: var(--spacing-3);font-size:var(--text-xs)}.k-pagination-selector{--button-height: var(--height);--dropdown-padding: 0;overflow:visible}.k-pagination-selector form{display:flex;align-items:center;justify-content:space-between}.k-pagination-selector label{display:flex;align-items:center;gap:var(--spacing-2);padding-inline-start:var(--spacing-3)}.k-pagination-selector select{--height: calc(var(--button-height) - .5rem);width:auto;min-width:var(--height);height:var(--height);text-align:center;background:var(--color-gray-800);color:var(--color-white);border-radius:var(--rounded-sm)}.k-prev-next{direction:ltr;flex-shrink:0}.k-search-bar-input{--button-height: var(--input-height);display:flex;align-items:center}.k-search-bar-types{flex-shrink:0;border-inline-end:1px solid var(--color-border)}.k-search-bar-input input{flex-grow:1;padding-inline:.75rem;height:var(--input-height);line-height:var(--input-height);border-radius:var(--rounded);font-size:var(--input-font-size)}.k-search-bar-input input:focus{outline:0}.k-search-bar-input .k-search-bar-close{flex-shrink:0}.k-search-bar-results{border-top:1px solid var(--color-border);padding:1rem}.k-search-bar-results .k-item[data-selected=true]{outline:var(--outline)}.k-search-bar-footer{text-align:center}.k-search-bar-footer p{color:var(--color-text-dimmed)}.k-search-bar-footer .k-button{margin-top:var(--spacing-4)}:root{--tag-color-back: var(--color-black);--tag-color-text: var(--color-white);--tag-color-toggle: currentColor;--tag-color-disabled-back: var(--color-gray-600);--tag-color-disabled-text: var(--tag-color-text);--tag-height: var(--height-xs);--tag-rounded: var(--rounded-sm)}.k-tag{position:relative;height:var(--tag-height);display:flex;align-items:center;justify-content:space-between;font-size:var(--text-sm);line-height:1;color:var(--tag-color-text);background-color:var(--tag-color-back);border-radius:var(--tag-rounded);cursor:pointer;-webkit-user-select:none;user-select:none}.k-tag:not([aria-disabled]):focus{outline:var(--outline)}.k-tag-image{height:100%;border-radius:var(--rounded-xs);overflow:hidden;flex-shrink:0;border-radius:0;border-start-start-radius:var(--tag-rounded);border-end-start-radius:var(--tag-rounded);background-clip:padding-box}.k-tag-text{padding-inline:var(--spacing-2);line-height:var(--leading-tight);overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.k-tag[data-has-toggle=true] .k-tag-text{padding-inline-end:0}.k-tag-toggle{--icon-size: 14px;width:var(--tag-height);height:var(--tag-height);filter:brightness(70%);flex-shrink:0}.k-tag-toggle:hover{filter:brightness(100%)}.k-tag:where([aria-disabled]){background-color:var(--tag-color-disabled-back);color:var(--tag-color-disabled-text);cursor:not-allowed}.k-button[data-disabled=true]{opacity:.5;pointer-events:none;cursor:default}.k-card-options>.k-button[data-disabled=true]{display:inline-flex}.k-section+.k-section{margin-top:var(--columns-block-gap)}.k-section-header{display:flex;justify-content:space-between;align-items:center;gap:var(--spacing-6);margin-bottom:var(--spacing-2)}.k-section-buttons{flex-shrink:0}.k-fields-section input[type=submit]{display:none}[data-locked=true] .k-fields-section{opacity:.2;pointer-events:none}.k-models-section[data-processing=true]{pointer-events:none}.k-models-section-search.k-input{--input-color-back: var(--color-gray-300);--input-color-border: transparent;margin-bottom:var(--spacing-3)}:root{--code-color-back: var(--color-black);--code-color-icon: var(--color-gray-500);--code-color-text: var(--color-gray-200, white);--code-font-family: var(--font-mono);--code-font-size: 1em;--code-inline-color-back: var(--color-blue-300);--code-inline-color-border: var(--color-blue-400);--code-inline-color-text: var(--color-blue-900);--code-inline-font-size: .9em;--code-padding: var(--spacing-3)}code{font-family:var(--code-font-family);font-size:var(--code-font-size);font-weight:var(--font-normal)}.k-code,.k-text pre{position:relative;display:block;max-width:100%;padding:var(--code-padding);border-radius:var(--rounded, .5rem);background:var(--code-color-back);color:var(--code-color-text);white-space:nowrap;overflow-y:hidden;overflow-x:auto;line-height:1.5;-moz-tab-size:2;tab-size:2}.k-code:not(code),.k-text pre{white-space:pre-wrap}.k-code:before{position:absolute;content:attr(data-language);inset-block-start:0;inset-inline-end:0;padding:.5rem .5rem .25rem .25rem;font-size:calc(.75 * var(--text-xs));background:var(--code-color-back);border-radius:var(--rounded, .5rem)}.k-text>code,.k-text *:not(pre)>code{display:inline-flex;padding-inline:var(--spacing-1);font-size:var(--code-inline-font-size);color:var(--code-inline-color-text);background:var(--code-inline-color-back);border-radius:var(--rounded);outline:1px solid var(--code-inline-color-border);outline-offset:-1px}:root{--text-h1: 2em;--text-h2: 1.75em;--text-h3: 1.5em;--text-h4: 1.25em;--text-h5: 1.125em;--text-h6: 1em;--font-h1: var(--font-semi);--font-h2: var(--font-semi);--font-h3: var(--font-semi);--font-h4: var(--font-semi);--font-h5: var(--font-semi);--font-h6: var(--font-semi);--leading-h1: 1.125;--leading-h2: 1.125;--leading-h3: 1.25;--leading-h4: 1.375;--leading-h5: 1.5;--leading-h6: 1.5}.k-headline{line-height:1.5em;font-weight:var(--font-bold)}.h1,.k-text h1,.k-headline[data-size=huge]{color:var(--color-h1, var(--color-h));font-family:var(--font-family-h1);font-size:var(--text-h1);font-weight:var(--font-h1);line-height:var(--leading-h1)}.h2,.k-text h2,.k-headline[data-size=large]{color:var(--color-h2, var(--color-h));font-family:var(--font-family-h2);font-size:var(--text-h2);font-weight:var(--font-h2);line-height:var(--leading-h2)}.h3,.k-text h3{color:var(--color-h3, var(--color-h));font-family:var(--font-family-h3);font-size:var(--text-h3);font-weight:var(--font-h3);line-height:var(--leading-h3)}.h4,.k-text h4,.k-headline[data-size=small]{color:var(--color-h4, var(--color-h));font-family:var(--font-family-h4);font-size:var(--text-h4);font-weight:var(--font-h4);line-height:var(--leading-h4)}.h5,.k-text h5{color:var(--color-h5, var(--color-h));font-family:var(--font-family-h5);font-size:var(--text-h5);font-weight:var(--font-h5);line-height:var(--leading-h5)}.h6,.k-text h6{color:var(--color-h6, var(--color-h));font-family:var(--font-family-h6);font-size:var(--text-h6);font-weight:var(--font-h6);line-height:var(--leading-h6)}.k-text>*+h6{margin-block-start:calc(var(--text-line-height) * 1.5em)}.k-headline[data-theme]{color:var(--theme)}.k-label{position:relative;display:flex;align-items:center;height:var(--height-xs);font-weight:var(--font-semi);min-width:0;flex:1}[aria-disabled] .k-label{opacity:var(--opacity-disabled);cursor:not-allowed}.k-label>a{display:inline-flex;height:var(--height-xs);align-items:center;padding-inline:var(--spacing-2);margin-inline-start:calc(-1 * var(--spacing-2));border-radius:var(--rounded);min-width:0}.k-label-text{text-overflow:ellipsis;white-space:nowrap;overflow-x:clip;min-width:0}.k-label abbr{font-size:var(--text-xs);color:var(--color-gray-500);margin-inline-start:var(--spacing-1)}.k-label abbr.k-label-invalid{display:none;color:var(--color-red-700)}:where(.k-field:has([data-invalid]),.k-section:has([data-invalid]))>header>.k-label abbr.k-label-invalid{display:inline-block}.k-field:has([data-invalid])>.k-field-header>.k-label abbr:has(+abbr.k-label-invalid){display:none}:root{--text-font-size: 1em;--text-line-height: 1.5;--link-color: var(--color-blue-800);--link-underline-offset: 2px}.k-text{font-size:var(--text-font-size);line-height:var(--text-line-height)}.k-text[data-size=tiny]{--text-font-size: var(--text-xs)}.k-text[data-size=small]{--text-font-size: var(--text-sm)}.k-text[data-size=medium]{--text-font-size: var(--text-md)}.k-text[data-size=large]{--text-font-size: var(--text-xl)}.k-text[data-align]{text-align:var(--align)}.k-text>:where(audio,blockquote,details,div,figure,h1,h2,h3,h4,h5,h6,hr,iframe,img,object,ol,p,picture,pre,table,ul)+*{margin-block-start:calc(var(--text-line-height) * 1em)}.k-text :where(.k-link,a){color:var(--link-color);text-decoration:underline;text-underline-offset:var(--link-underline-offset);border-radius:var(--rounded-xs);outline-offset:2px}.k-text ol,.k-text ul{padding-inline-start:1.75em}.k-text ol{list-style:numeric}.k-text ol>li{list-style:decimal}.k-text ul>li{list-style:disc}.k-text ul ul>li{list-style:circle}.k-text ul ul ul>li{list-style:square}.k-text blockquote{font-size:var(--text-lg);line-height:1.25;padding-inline-start:var(--spacing-4);border-inline-start:2px solid var(--color-black)}.k-text img{border-radius:var(--rounded)}.k-text iframe{width:100%;aspect-ratio:16/9;border-radius:var(--rounded)}.k-text hr{background:var(--color-border);height:1px}.k-help{color:var(--color-text-dimmed)}.k-upload-item-preview{--icon-size: 24px;grid-area:preview;display:flex;aspect-ratio:1/1;width:100%;height:100%;overflow:hidden;border-start-start-radius:var(--rounded);border-end-start-radius:var(--rounded)}.k-upload-item-preview:focus{border-radius:var(--rounded);outline:2px solid var(--color-focus);z-index:1}.k-upload-item{accent-color:var(--color-focus);display:grid;grid-template-areas:"preview input input" "preview body toggle";grid-template-columns:6rem 1fr auto;grid-template-rows:var(--input-height) 1fr;border-radius:var(--rounded);background:var(--color-white);box-shadow:var(--shadow);min-height:6rem}.k-upload-item-body{grid-area:body;display:flex;flex-direction:column;justify-content:space-between;padding:var(--spacing-2) var(--spacing-3);min-width:0}.k-upload-item-input.k-input{--input-color-border: transparent;--input-padding: var(--spacing-2) var(--spacing-3);--input-rounded: 0;grid-area:input;font-size:var(--text-sm);border-bottom:1px solid var(--color-light);border-start-end-radius:var(--rounded)}.k-upload-item-input.k-input:focus-within{outline:2px solid var(--color-focus);z-index:1;border-radius:var(--rounded)}.k-upload-item-input.k-input[data-disabled=true]{--input-color-back: var(--color-white)}.k-upload-item-input .k-input-after{color:var(--color-gray-600)}.k-upload-item-meta{font-size:var(--text-xs);color:var(--color-gray-600)}.k-upload-item-error{font-size:var(--text-xs);margin-top:.25rem;color:var(--color-red-700)}.k-upload-item-progress{--progress-height: .25rem;--progress-color-back: var(--color-light);margin-bottom:.3125rem}.k-upload-item-toggle{grid-area:toggle;align-self:end}.k-upload-item-toggle>*{padding:var(--spacing-3)}.k-upload-item[data-completed] .k-upload-item-progress{--progress-color-value: var(--color-green-400)}.k-upload-items{display:grid;gap:.25rem}.k-activation{position:relative;display:flex;color:var(--dropdown-color-text);background:var(--dropdown-color-bg);border-radius:var(--dropdown-rounded);box-shadow:var(--dropdown-shadow);justify-content:space-between}.k-activation p{padding-inline-start:var(--spacing-3);padding-inline-end:var(--spacing-2);padding-block:.425rem;line-height:1.25}.k-activation p strong{font-weight:var(--font-normal);margin-inline-end:var(--spacing-1)}.k-activation p :where(button,a){color:var(--color-pink-400);text-decoration:underline;text-decoration-color:currentColor;text-underline-offset:2px;border-radius:var(--rounded-sm)}.k-activation-toggle{--button-color-text: var(--color-gray-400);--button-rounded: 0;border-left:1px solid var(--dropdown-color-hr)}.k-activation-toggle:is(:hover,:focus){--button-color-text: var(--color-white)}.k-activation-toggle:focus{--button-rounded: var(--rounded)}:root{--main-padding-inline: clamp(var(--spacing-6), 5cqw, var(--spacing-24))}.k-panel-main{min-height:100vh;min-height:100dvh;padding:var(--spacing-3) var(--main-padding-inline) var(--spacing-24);container:main / inline-size;margin-inline-start:var(--main-start)}.k-panel-notification{--button-height: var(--height-md);--button-color-icon: var(--theme-color-900);--button-color-text: var(--theme-color-900);border:1px solid var(--theme-color-500);position:fixed;inset-block-end:var(--menu-padding);inset-inline-end:var(--menu-padding);box-shadow:var(--dropdown-shadow);z-index:var(--z-notification)}:root{--menu-button-height: var(--height);--menu-button-width: 100%;--menu-color-back: var(--color-gray-250);--menu-color-border: var(--color-gray-300);--menu-display: none;--menu-display-backdrop: block;--menu-padding: var(--spacing-3);--menu-shadow: var(--shadow-xl);--menu-toggle-height: var(--menu-button-height);--menu-toggle-width: 1rem;--menu-width-closed: calc( var(--menu-button-height) + 2 * var(--menu-padding) );--menu-width-open: 12rem;--menu-width: var(--menu-width-open)}.k-panel-menu{position:fixed;inset-inline-start:0;inset-block:0;z-index:var(--z-navigation);display:var(--menu-display);width:var(--menu-width);background-color:var(--menu-color-back);border-right:1px solid var(--menu-color-border);box-shadow:var(--menu-shadow)}.k-panel-menu-body{display:flex;flex-direction:column;gap:var(--spacing-4);padding:var(--menu-padding);overscroll-behavior:contain;overflow-x:hidden;overflow-y:auto;height:100%}.k-panel-menu-search{margin-bottom:var(--spacing-8)}.k-panel-menu-buttons{display:flex;flex-direction:column;width:100%}.k-panel-menu-buttons[data-second-last=true]{flex-grow:1}.k-panel-menu-buttons:last-child{justify-content:flex-end}.k-panel-menu-button{--button-align: flex-start;--button-height: var(--menu-button-height);--button-width: var(--menu-button-width);--button-padding: 7px;flex-shrink:0}.k-panel-menu-button[aria-current]{--button-color-back: var(--color-white);box-shadow:var(--shadow)}.k-panel-menu-button:focus{z-index:1}.k-panel[data-menu=true]{--menu-button-width: 100%;--menu-display: block;--menu-width: var(--menu-width-open)}.k-panel[data-menu=true]:after{content:"";position:fixed;top:0;right:0;bottom:0;left:0;background:var(--color-backdrop);display:var(--menu-display-backdrop);pointer-events:none;z-index:var(--z-drawer)}.k-panel-menu-toggle{--button-align: flex-start;--button-height: 100%;--button-width: var(--menu-toggle-width);position:absolute;inset-block:0;inset-inline-start:100%;align-items:flex-start;border-radius:0;overflow:visible;opacity:0;transition:opacity .2s}.k-panel-menu-toggle:focus{outline:0}.k-panel-menu-toggle .k-button-icon{display:grid;place-items:center;height:var(--menu-toggle-height);width:var(--menu-toggle-width);margin-top:var(--menu-padding);border-block:1px solid var(--menu-color-border);border-inline-end:1px solid var(--menu-color-border);background:var(--menu-color-back);border-start-end-radius:var(--button-rounded);border-end-end-radius:var(--button-rounded)}@media (max-width: 60rem){.k-panel-menu .k-activation-button{margin-bottom:var(--spacing-3)}.k-panel-menu .k-activation-toggle{display:none}}@media (min-width: 60rem){.k-panel{--menu-display: block;--menu-display-backdrop: none;--menu-shadow: none;--main-start: var(--menu-width)}.k-panel[data-menu=false]{--menu-button-width: var(--menu-button-height);--menu-width: var(--menu-width-closed)}.k-panel-menu-proxy{display:none}.k-panel-menu-toggle:focus-visible,.k-panel-menu[data-hover] .k-panel-menu-toggle{opacity:1}.k-panel-menu-toggle:focus-visible .k-button-icon{outline:var(--outline);border-radius:var(--button-rounded)}.k-panel-menu-search[aria-disabled=true]{opacity:0}.k-panel-menu .k-activation{position:absolute;bottom:var(--menu-padding);inset-inline-start:100%;height:var(--height-md);width:max-content;margin-left:var(--menu-padding)}.k-panel-menu .k-activation:before{position:absolute;content:"";top:50%;left:-4px;margin-top:-4px;border-top:4px solid transparent;border-right:4px solid var(--color-black);border-bottom:4px solid transparent}.k-panel-menu .k-activation p :where(button,a){padding-inline:var(--spacing-1)}.k-panel-menu .k-activation-toggle{border-left:1px solid var(--dropdown-color-hr)}}.k-panel.k-panel-outside{display:grid;grid-template-rows:1fr;place-items:center;min-height:100vh;min-height:100dvh;padding:var(--spacing-6)}:root{--scroll-top: 0rem}html{overflow-x:hidden;overflow-y:scroll;background:var(--color-light)}body{font-size:var(--text-sm)}.k-panel[data-loading=true]{animation:LoadingCursor .5s}.k-panel[data-loading=true]:after,.k-panel[data-dragging=true]{-webkit-user-select:none;user-select:none}.k-topbar{position:relative;margin-inline:calc(var(--button-padding) * -1);margin-bottom:var(--spacing-8);display:flex;align-items:center;gap:var(--spacing-1)}.k-topbar-breadcrumb{margin-inline-start:-2px;flex-shrink:1;min-width:0}.k-topbar-spacer{flex-grow:1}.k-topbar-signals{display:flex;align-items:center}.k-search-view .k-header{margin-bottom:0}.k-header+.k-search-view-results{margin-top:var(--spacing-12)}.k-search-view-input{--input-color-border: transparent;--input-color-back: var(--color-gray-300);--input-height: var(--height-md);width:40cqw}.k-file-view-header,.k-file-view[data-has-tabs=true] .k-file-preview{margin-bottom:0}.k-file-preview{display:grid;align-items:stretch;background:var(--color-gray-900);border-radius:var(--rounded-lg);margin-bottom:var(--spacing-12);overflow:hidden}.k-file-preview-thumb-column{background:var(--pattern);aspect-ratio:1/1}.k-file-preview-thumb{display:flex;align-items:center;justify-content:center;height:100%;padding:var(--spacing-12);container-type:size}.k-file-preview-thumb img{width:auto;max-width:100cqw;max-height:100cqh}.k-file-preview-thumb>.k-icon{--icon-size: 3rem}.k-file-preview-thumb>.k-button{position:absolute;top:var(--spacing-2);inset-inline-start:var(--spacing-2)}.k-file-preview .k-coords-input{--opacity-disabled: 1;--range-thumb-color: hsl(216 60% 60% / .75);--range-thumb-size: 1.25rem;--range-thumb-shadow: none;cursor:crosshair}.k-file-preview .k-coords-input-thumb:after{--size: .4rem;--pos: calc(50% - (var(--size) / 2));position:absolute;top:var(--pos);inset-inline-start:var(--pos);width:var(--size);height:var(--size);content:"";background:var(--color-white);border-radius:50%}.k-file-preview:not([data-has-focus=true]) .k-coords-input-thumb{display:none}.k-file-preview-details{display:grid}.k-file-preview-details dl{display:grid;grid-template-columns:repeat(auto-fill,minmax(14rem,1fr));grid-gap:var(--spacing-6) var(--spacing-12);align-self:center;line-height:1.5em;padding:var(--spacing-6)}.k-file-preview-details dt{font-size:var(--text-sm);font-weight:500;font-weight:var(--font-semi);color:var(--color-gray-500);margin-bottom:var(--spacing-1)}.k-file-preview-details :where(dd,a){font-size:var(--text-xs);color:#ffffff80;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:#ffffffbf;font-size:var(--text-sm)}.k-file-preview-focus-info dd{display:flex;align-items:center}.k-file-preview-focus-info .k-button{--button-padding: var(--spacing-2);--button-color-back: var(--color-gray-800)}.k-file-preview[data-has-focus=true] .k-file-preview-focus-info .k-button{flex-direction:row-reverse}@container (min-width: 36rem){.k-file-preview{grid-template-columns:50% auto}.k-file-preview-thumb-column{aspect-ratio:auto}}@container (min-width: 65rem){.k-file-preview{grid-template-columns:33.333% auto}.k-file-preview-thumb-column{aspect-ratio:1/1}}@container (min-width: 90rem){.k-file-preview-layout{grid-template-columns:25% auto}}.k-installation-dialog{--dialog-color-back: var(--color-white);--dialog-shadow: var(--shadow);container-type:inline-size}.k-installation-view .k-button{margin-top:var(--spacing-3);width:100%}.k-installation-view form .k-button{margin-top:var(--spacing-10)}.k-installation-view .k-headline{font-weight:var(--font-semi);margin-top:-.5rem;margin-bottom:.75rem}.k-installation-issues{line-height:1.5em;font-size:var(--text-sm)}.k-installation-issues li{position:relative;padding:var(--spacing-6);background:var(--color-red-300);padding-inline-start:3.5rem;border-radius:var(--rounded)}.k-installation-issues .k-icon{position:absolute;top:calc(1.5rem + 2px);inset-inline-start:1.5rem}.k-installation-issues .k-icon{color:var(--color-red-700)}.k-installation-issues li:not(:last-child){margin-bottom:2px}.k-installation-issues li code{font:inherit;color:var(--color-red-700)}.k-login-code-form .k-user-info{margin-bottom:var(--spacing-6)}.k-login-form{position:relative}.k-login-form label abbr{visibility:hidden}.k-login-toggler{position:absolute;top:-2px;inset-inline-end:calc(var(--spacing-2) * -1);color:var(--link-color);text-decoration:underline;text-decoration-color:var(--link-color);text-underline-offset:1px;height:var(--height-xs);line-height:1;padding-inline:var(--spacing-2);border-radius:var(--rounded);z-index:1}.k-login{--dialog-color-back: var(--color-white);--dialog-shadow: var(--shadow);container-type:inline-size}.k-login-buttons{--button-padding: var(--spacing-3);display:flex;gap:1.5rem;align-items:center;justify-content:space-between;margin-top:var(--spacing-10)}.k-page-view[data-has-tabs=true] .k-page-view-header,.k-site-view[data-has-tabs=true] .k-site-view-header{margin-bottom:0}.k-user-name-placeholder{color:var(--color-gray-500);transition:color .3s}.k-user-view-header[data-editable=true] .k-user-name-placeholder:hover{color:var(--color-gray-900)}.k-user-view-header{margin-bottom:0;border-bottom:0}.k-user-view .k-user-profile{margin-bottom:var(--spacing-12)}.k-user-view[data-has-tabs=true] .k-user-profile{margin-bottom:0}.k-password-reset-view .k-user-info{margin-bottom:var(--spacing-8)}.k-user-view-image{padding:0}.k-user-view-image .k-frame{width:6rem;height:6rem;border-radius:var(--rounded);line-height:0}.k-user-view-image .k-icon-frame{--back: var(--color-black);--icon-color: var(--color-gray-200)}.k-user-info{display:flex;align-items:center;font-size:var(--text-sm);height:var(--height-lg);gap:.75rem;padding-inline:var(--spacing-2);background:var(--color-white);box-shadow:var(--shadow)}.k-user-info :where(.k-image-frame,.k-icon-frame){width:1.5rem;border-radius:var(--rounded-sm)}.k-user-profile{--button-height: auto;padding:var(--spacing-2);background:var(--color-white);border-radius:var(--rounded-lg);display:flex;align-items:center;gap:var(--spacing-3);box-shadow:var(--shadow)}.k-user-profile .k-button-group{display:flex;flex-direction:column;align-items:flex-start}.k-users-view-header{margin-bottom:0}.k-system-info .k-stat-label{color:var(--theme, var(--color-black))}.k-table-update-status-cell{padding:0 .75rem;display:flex;align-items:center;height:100%}.k-table-update-status-cell-version,.k-table-update-status-cell-button{font-variant-numeric:tabular-nums}.k-plugin-info{display:grid;column-gap:var(--spacing-3);row-gap:2px;padding:var(--button-padding)}.k-plugin-info dt{color:var(--color-gray-400)}.k-plugin-info dd[data-theme]{color:var(--theme-color-600)}@container (max-width: 30em){.k-plugin-info dd:not(:last-of-type){margin-bottom:var(--spacing-2)}}@container (min-width: 30em){.k-plugin-info{width:20rem;grid-template-columns:1fr auto}}:root{--color-l-100: 98%;--color-l-200: 94%;--color-l-300: 88%;--color-l-400: 80%;--color-l-500: 70%;--color-l-600: 60%;--color-l-700: 45%;--color-l-800: 30%;--color-l-900: 15%;--color-red-h: 0;--color-red-s: 80%;--color-red-hs: var(--color-red-h), var(--color-red-s);--color-red-boost: 3%;--color-red-l-100: calc(var(--color-l-100) + var(--color-red-boost));--color-red-l-200: calc(var(--color-l-200) + var(--color-red-boost));--color-red-l-300: calc(var(--color-l-300) + var(--color-red-boost));--color-red-l-400: calc(var(--color-l-400) + var(--color-red-boost));--color-red-l-500: calc(var(--color-l-500) + var(--color-red-boost));--color-red-l-600: calc(var(--color-l-600) + var(--color-red-boost));--color-red-l-700: calc(var(--color-l-700) + var(--color-red-boost));--color-red-l-800: calc(var(--color-l-800) + var(--color-red-boost));--color-red-l-900: calc(var(--color-l-900) + var(--color-red-boost));--color-red-100: hsl(var(--color-red-hs), var(--color-red-l-100));--color-red-200: hsl(var(--color-red-hs), var(--color-red-l-200));--color-red-300: hsl(var(--color-red-hs), var(--color-red-l-300));--color-red-400: hsl(var(--color-red-hs), var(--color-red-l-400));--color-red-500: hsl(var(--color-red-hs), var(--color-red-l-500));--color-red-600: hsl(var(--color-red-hs), var(--color-red-l-600));--color-red-700: hsl(var(--color-red-hs), var(--color-red-l-700));--color-red-800: hsl(var(--color-red-hs), var(--color-red-l-800));--color-red-900: hsl(var(--color-red-hs), var(--color-red-l-900));--color-orange-h: 28;--color-orange-s: 80%;--color-orange-hs: var(--color-orange-h), var(--color-orange-s);--color-orange-boost: 2.5%;--color-orange-l-100: calc(var(--color-l-100) + var(--color-orange-boost));--color-orange-l-200: calc(var(--color-l-200) + var(--color-orange-boost));--color-orange-l-300: calc(var(--color-l-300) + var(--color-orange-boost));--color-orange-l-400: calc(var(--color-l-400) + var(--color-orange-boost));--color-orange-l-500: calc(var(--color-l-500) + var(--color-orange-boost));--color-orange-l-600: calc(var(--color-l-600) + var(--color-orange-boost));--color-orange-l-700: calc(var(--color-l-700) + var(--color-orange-boost));--color-orange-l-800: calc(var(--color-l-800) + var(--color-orange-boost));--color-orange-l-900: calc(var(--color-l-900) + var(--color-orange-boost));--color-orange-100: hsl(var(--color-orange-hs), var(--color-orange-l-100));--color-orange-200: hsl(var(--color-orange-hs), var(--color-orange-l-200));--color-orange-300: hsl(var(--color-orange-hs), var(--color-orange-l-300));--color-orange-400: hsl(var(--color-orange-hs), var(--color-orange-l-400));--color-orange-500: hsl(var(--color-orange-hs), var(--color-orange-l-500));--color-orange-600: hsl(var(--color-orange-hs), var(--color-orange-l-600));--color-orange-700: hsl(var(--color-orange-hs), var(--color-orange-l-700));--color-orange-800: hsl(var(--color-orange-hs), var(--color-orange-l-800));--color-orange-900: hsl(var(--color-orange-hs), var(--color-orange-l-900));--color-yellow-h: 47;--color-yellow-s: 80%;--color-yellow-hs: var(--color-yellow-h), var(--color-yellow-s);--color-yellow-boost: 0%;--color-yellow-l-100: calc(var(--color-l-100) + var(--color-yellow-boost));--color-yellow-l-200: calc(var(--color-l-200) + var(--color-yellow-boost));--color-yellow-l-300: calc(var(--color-l-300) + var(--color-yellow-boost));--color-yellow-l-400: calc(var(--color-l-400) + var(--color-yellow-boost));--color-yellow-l-500: calc(var(--color-l-500) + var(--color-yellow-boost));--color-yellow-l-600: calc(var(--color-l-600) + var(--color-yellow-boost));--color-yellow-l-700: calc(var(--color-l-700) + var(--color-yellow-boost));--color-yellow-l-800: calc(var(--color-l-800) + var(--color-yellow-boost));--color-yellow-l-900: calc(var(--color-l-900) + var(--color-yellow-boost));--color-yellow-100: hsl(var(--color-yellow-hs), var(--color-yellow-l-100));--color-yellow-200: hsl(var(--color-yellow-hs), var(--color-yellow-l-200));--color-yellow-300: hsl(var(--color-yellow-hs), var(--color-yellow-l-300));--color-yellow-400: hsl(var(--color-yellow-hs), var(--color-yellow-l-400));--color-yellow-500: hsl(var(--color-yellow-hs), var(--color-yellow-l-500));--color-yellow-600: hsl(var(--color-yellow-hs), var(--color-yellow-l-600));--color-yellow-700: hsl(var(--color-yellow-hs), var(--color-yellow-l-700));--color-yellow-800: hsl(var(--color-yellow-hs), var(--color-yellow-l-800));--color-yellow-900: hsl(var(--color-yellow-hs), var(--color-yellow-l-900));--color-green-h: 80;--color-green-s: 60%;--color-green-hs: var(--color-green-h), var(--color-green-s);--color-green-boost: -2.5%;--color-green-l-100: calc(var(--color-l-100) + var(--color-green-boost));--color-green-l-200: calc(var(--color-l-200) + var(--color-green-boost));--color-green-l-300: calc(var(--color-l-300) + var(--color-green-boost));--color-green-l-400: calc(var(--color-l-400) + var(--color-green-boost));--color-green-l-500: calc(var(--color-l-500) + var(--color-green-boost));--color-green-l-600: calc(var(--color-l-600) + var(--color-green-boost));--color-green-l-700: calc(var(--color-l-700) + var(--color-green-boost));--color-green-l-800: calc(var(--color-l-800) + var(--color-green-boost));--color-green-l-900: calc(var(--color-l-900) + var(--color-green-boost));--color-green-100: hsl(var(--color-green-hs), var(--color-green-l-100));--color-green-200: hsl(var(--color-green-hs), var(--color-green-l-200));--color-green-300: hsl(var(--color-green-hs), var(--color-green-l-300));--color-green-400: hsl(var(--color-green-hs), var(--color-green-l-400));--color-green-500: hsl(var(--color-green-hs), var(--color-green-l-500));--color-green-600: hsl(var(--color-green-hs), var(--color-green-l-600));--color-green-700: hsl(var(--color-green-hs), var(--color-green-l-700));--color-green-800: hsl(var(--color-green-hs), var(--color-green-l-800));--color-green-900: hsl(var(--color-green-hs), var(--color-green-l-900));--color-aqua-h: 180;--color-aqua-s: 50%;--color-aqua-hs: var(--color-aqua-h), var(--color-aqua-s);--color-aqua-boost: 0%;--color-aqua-l-100: calc(var(--color-l-100) + var(--color-aqua-boost));--color-aqua-l-200: calc(var(--color-l-200) + var(--color-aqua-boost));--color-aqua-l-300: calc(var(--color-l-300) + var(--color-aqua-boost));--color-aqua-l-400: calc(var(--color-l-400) + var(--color-aqua-boost));--color-aqua-l-500: calc(var(--color-l-500) + var(--color-aqua-boost));--color-aqua-l-600: calc(var(--color-l-600) + var(--color-aqua-boost));--color-aqua-l-700: calc(var(--color-l-700) + var(--color-aqua-boost));--color-aqua-l-800: calc(var(--color-l-800) + var(--color-aqua-boost));--color-aqua-l-900: calc(var(--color-l-900) + var(--color-aqua-boost));--color-aqua-100: hsl(var(--color-aqua-hs), var(--color-aqua-l-100));--color-aqua-200: hsl(var(--color-aqua-hs), var(--color-aqua-l-200));--color-aqua-300: hsl(var(--color-aqua-hs), var(--color-aqua-l-300));--color-aqua-400: hsl(var(--color-aqua-hs), var(--color-aqua-l-400));--color-aqua-500: hsl(var(--color-aqua-hs), var(--color-aqua-l-500));--color-aqua-600: hsl(var(--color-aqua-hs), var(--color-aqua-l-600));--color-aqua-700: hsl(var(--color-aqua-hs), var(--color-aqua-l-700));--color-aqua-800: hsl(var(--color-aqua-hs), var(--color-aqua-l-800));--color-aqua-900: hsl(var(--color-aqua-hs), var(--color-aqua-l-900));--color-blue-h: 210;--color-blue-s: 65%;--color-blue-hs: var(--color-blue-h), var(--color-blue-s);--color-blue-boost: 3%;--color-blue-l-100: calc(var(--color-l-100) + var(--color-blue-boost));--color-blue-l-200: calc(var(--color-l-200) + var(--color-blue-boost));--color-blue-l-300: calc(var(--color-l-300) + var(--color-blue-boost));--color-blue-l-400: calc(var(--color-l-400) + var(--color-blue-boost));--color-blue-l-500: calc(var(--color-l-500) + var(--color-blue-boost));--color-blue-l-600: calc(var(--color-l-600) + var(--color-blue-boost));--color-blue-l-700: calc(var(--color-l-700) + var(--color-blue-boost));--color-blue-l-800: calc(var(--color-l-800) + var(--color-blue-boost));--color-blue-l-900: calc(var(--color-l-900) + var(--color-blue-boost));--color-blue-100: hsl(var(--color-blue-hs), var(--color-blue-l-100));--color-blue-200: hsl(var(--color-blue-hs), var(--color-blue-l-200));--color-blue-300: hsl(var(--color-blue-hs), var(--color-blue-l-300));--color-blue-400: hsl(var(--color-blue-hs), var(--color-blue-l-400));--color-blue-500: hsl(var(--color-blue-hs), var(--color-blue-l-500));--color-blue-600: hsl(var(--color-blue-hs), var(--color-blue-l-600));--color-blue-700: hsl(var(--color-blue-hs), var(--color-blue-l-700));--color-blue-800: hsl(var(--color-blue-hs), var(--color-blue-l-800));--color-blue-900: hsl(var(--color-blue-hs), var(--color-blue-l-900));--color-purple-h: 275;--color-purple-s: 60%;--color-purple-hs: var(--color-purple-h), var(--color-purple-s);--color-purple-boost: 0%;--color-purple-l-100: calc(var(--color-l-100) + var(--color-purple-boost));--color-purple-l-200: calc(var(--color-l-200) + var(--color-purple-boost));--color-purple-l-300: calc(var(--color-l-300) + var(--color-purple-boost));--color-purple-l-400: calc(var(--color-l-400) + var(--color-purple-boost));--color-purple-l-500: calc(var(--color-l-500) + var(--color-purple-boost));--color-purple-l-600: calc(var(--color-l-600) + var(--color-purple-boost));--color-purple-l-700: calc(var(--color-l-700) + var(--color-purple-boost));--color-purple-l-800: calc(var(--color-l-800) + var(--color-purple-boost));--color-purple-l-900: calc(var(--color-l-900) + var(--color-purple-boost));--color-purple-100: hsl(var(--color-purple-hs), var(--color-purple-l-100));--color-purple-200: hsl(var(--color-purple-hs), var(--color-purple-l-200));--color-purple-300: hsl(var(--color-purple-hs), var(--color-purple-l-300));--color-purple-400: hsl(var(--color-purple-hs), var(--color-purple-l-400));--color-purple-500: hsl(var(--color-purple-hs), var(--color-purple-l-500));--color-purple-600: hsl(var(--color-purple-hs), var(--color-purple-l-600));--color-purple-700: hsl(var(--color-purple-hs), var(--color-purple-l-700));--color-purple-800: hsl(var(--color-purple-hs), var(--color-purple-l-800));--color-purple-900: hsl(var(--color-purple-hs), var(--color-purple-l-900));--color-pink-h: 320;--color-pink-s: 70%;--color-pink-hs: var(--color-pink-h), var(--color-pink-s);--color-pink-boost: 0%;--color-pink-l-100: calc(var(--color-l-100) + var(--color-pink-boost));--color-pink-l-200: calc(var(--color-l-200) + var(--color-pink-boost));--color-pink-l-300: calc(var(--color-l-300) + var(--color-pink-boost));--color-pink-l-400: calc(var(--color-l-400) + var(--color-pink-boost));--color-pink-l-500: calc(var(--color-l-500) + var(--color-pink-boost));--color-pink-l-600: calc(var(--color-l-600) + var(--color-pink-boost));--color-pink-l-700: calc(var(--color-l-700) + var(--color-pink-boost));--color-pink-l-800: calc(var(--color-l-800) + var(--color-pink-boost));--color-pink-l-900: calc(var(--color-l-900) + var(--color-pink-boost));--color-pink-100: hsl(var(--color-pink-hs), var(--color-pink-l-100));--color-pink-200: hsl(var(--color-pink-hs), var(--color-pink-l-200));--color-pink-300: hsl(var(--color-pink-hs), var(--color-pink-l-300));--color-pink-400: hsl(var(--color-pink-hs), var(--color-pink-l-400));--color-pink-500: hsl(var(--color-pink-hs), var(--color-pink-l-500));--color-pink-600: hsl(var(--color-pink-hs), var(--color-pink-l-600));--color-pink-700: hsl(var(--color-pink-hs), var(--color-pink-l-700));--color-pink-800: hsl(var(--color-pink-hs), var(--color-pink-l-800));--color-pink-900: hsl(var(--color-pink-hs), var(--color-pink-l-900));--color-gray-h: 0;--color-gray-s: 0%;--color-gray-hs: var(--color-gray-h), var(--color-gray-s);--color-gray-boost: 0%;--color-gray-l-100: calc(var(--color-l-100) + var(--color-gray-boost));--color-gray-l-200: calc(var(--color-l-200) + var(--color-gray-boost));--color-gray-l-300: calc(var(--color-l-300) + var(--color-gray-boost));--color-gray-l-400: calc(var(--color-l-400) + var(--color-gray-boost));--color-gray-l-500: calc(var(--color-l-500) + var(--color-gray-boost));--color-gray-l-600: calc(var(--color-l-600) + var(--color-gray-boost));--color-gray-l-700: calc(var(--color-l-700) + var(--color-gray-boost));--color-gray-l-800: calc(var(--color-l-800) + var(--color-gray-boost));--color-gray-l-900: calc(var(--color-l-900) + var(--color-gray-boost));--color-gray-100: hsl(var(--color-gray-hs), var(--color-gray-l-100));--color-gray-200: hsl(var(--color-gray-hs), var(--color-gray-l-200));--color-gray-250: #e8e8e8;--color-gray-300: hsl(var(--color-gray-hs), var(--color-gray-l-300));--color-gray-400: hsl(var(--color-gray-hs), var(--color-gray-l-400));--color-gray-500: hsl(var(--color-gray-hs), var(--color-gray-l-500));--color-gray-600: hsl(var(--color-gray-hs), var(--color-gray-l-600));--color-gray-700: hsl(var(--color-gray-hs), var(--color-gray-l-700));--color-gray-800: hsl(var(--color-gray-hs), var(--color-gray-l-800));--color-gray-900: hsl(var(--color-gray-hs), var(--color-gray-l-900));--color-backdrop: rgba(0, 0, 0, .6);--color-black: black;--color-border: var(--color-gray-300);--color-dark: var(--color-gray-900);--color-focus: var(--color-blue-600);--color-light: var(--color-gray-200);--color-text: var(--color-black);--color-text-dimmed: var(--color-gray-700);--color-white: white;--color-background: var(--color-light);--color-gray: var(--color-gray-600);--color-red: var(--color-red-600);--color-orange: var(--color-orange-600);--color-yellow: var(--color-yellow-600);--color-green: var(--color-green-600);--color-aqua: var(--color-aqua-600);--color-blue: var(--color-blue-600);--color-purple: var(--color-purple-600);--color-focus-light: var(--color-focus);--color-focus-outline: var(--color-focus);--color-negative: var(--color-red-700);--color-negative-light: var(--color-red-500);--color-negative-outline: var(--color-red-900);--color-notice: var(--color-orange-700);--color-notice-light: var(--color-orange-500);--color-positive: var(--color-green-700);--color-positive-light: var(--color-green-500);--color-positive-outline: var(--color-green-900);--color-text-light: var(--color-text-dimmed)}:root{--font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";--font-mono: "SFMono-Regular", Consolas, Liberation Mono, Menlo, Courier, monospace}:root{--text-xs: .75rem;--text-sm: .875rem;--text-md: 1rem;--text-lg: 1.125rem;--text-xl: 1.25rem;--text-2xl: 1.5rem;--text-3xl: 1.75rem;--text-4xl: 2.5rem;--text-5xl: 3rem;--text-6xl: 4rem;--text-base: var(--text-md);--font-size-tiny: var(--text-xs);--font-size-small: var(--text-sm);--font-size-medium: var(--text-base);--font-size-large: var(--text-xl);--font-size-huge: var(--text-2xl);--font-size-monster: var(--text-3xl)}:root{--font-thin: 300;--font-normal: 400;--font-semi: 500;--font-bold: 600}:root{--height-xs: 1.5rem;--height-sm: 1.75rem;--height-md: 2rem;--height-lg: 2.25rem;--height-xl: 2.5rem;--height: var(--height-md)}:root{--opacity-disabled: .5}:root{--rounded-xs: 1px;--rounded-sm: .125rem;--rounded-md: .25rem;--rounded-lg: .375rem;--rounded-xl: .5rem;--rounded: var(--rounded-md)}:root{--shadow-sm: 0 1px 3px 0 rgba(0, 0, 0, .05), 0 1px 2px 0 rgba(0, 0, 0, .025);--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, .1), 0 2px 4px -1px rgba(0, 0, 0, .05);--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, .1), 0 4px 6px -2px rgba(0, 0, 0, .05);--shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, .1), 0 10px 10px -5px rgba(0, 0, 0, .05);--shadow: var(--shadow-sm);--shadow-toolbar: rgba(0, 0, 0, .1) -2px 0 5px, var(--shadow), var(--shadow-xl);--shadow-outline: var(--color-focus, currentColor) 0 0 0 2px;--shadow-inset: inset 0 2px 4px 0 rgba(0, 0, 0, .06);--shadow-sticky: rgba(0, 0, 0, .05) 0 2px 5px;--box-shadow-dropdown: var(--shadow-dropdown);--box-shadow-item: var(--shadow);--box-shadow-focus: var(--shadow-xl);--shadow-dropdown: var(--shadow-lg);--shadow-item: var(--shadow-sm)}:root{--spacing-0: 0;--spacing-1: .25rem;--spacing-2: .5rem;--spacing-3: .75rem;--spacing-4: 1rem;--spacing-6: 1.5rem;--spacing-8: 2rem;--spacing-12: 3rem;--spacing-16: 4rem;--spacing-24: 6rem;--spacing-36: 9rem;--spacing-48: 12rem;--spacing-px: 1px;--spacing-2px: 2px;--spacing-5: 1.25rem;--spacing-10: 2.5rem;--spacing-20: 5rem}:root{--z-offline: 1200;--z-fatal: 1100;--z-loader: 1000;--z-notification: 900;--z-dialog: 800;--z-navigation: 700;--z-dropdown: 600;--z-drawer: 500;--z-dropzone: 400;--z-toolbar: 300;--z-content: 200;--z-background: 100}:root{--pattern-size: 16px;--pattern-light: repeating-conic-gradient( hsl(0, 0%, 100%) 0% 25%, hsl(0, 0%, 90%) 0% 50% ) 50% / var(--pattern-size) var(--pattern-size);--pattern-dark: repeating-conic-gradient( hsla(0, 0%, 15%) 0% 25%, hsl(0, 0%, 22%) 0% 50% ) 50% / var(--pattern-size) var(--pattern-size);--pattern: var(--pattern-dark)}:root{--container: 80rem;--leading-none: 1;--leading-tight: 1.25;--leading-snug: 1.375;--leading-normal: 1.5;--leading-relaxed: 1.625;--leading-loose: 2;--field-input-padding: var(--input-padding);--field-input-height: var(--input-height);--field-input-line-height: var(--input-leading);--field-input-font-size: var(--input-font-size);--bg-pattern: var(--pattern)}:root{--choice-color-back: var(--color-white);--choice-color-border: var(--color-gray-500);--choice-color-checked: var(--color-black);--choice-color-disabled: var(--color-gray-400);--choice-color-icon: var(--color-light);--choice-color-info: var(--color-text-dimmed);--choice-color-text: var(--color-text);--choice-color-toggle: var(--choice-color-disabled);--choice-height: 1rem;--choice-rounded: var(--rounded-sm)}input:where([type=checkbox],[type=radio]){position:relative;cursor:pointer;overflow:hidden;flex-shrink:0;height:var(--choice-height);aspect-ratio:1/1;border:1px solid var(--choice-color-border);-webkit-appearance:none;-moz-appearance:none;appearance:none;border-radius:var(--choice-rounded);background:var(--choice-color-back);box-shadow:var(--shadow-sm)}input:where([type=checkbox],[type=radio]):after{position:absolute;content:"";display:none;place-items:center;text-align:center}input:where([type=checkbox],[type=radio]):focus{outline:var(--outline);outline-offset:-1px;color:var(--color-focus)}input:where([type=checkbox]):checked{border-color:var(--choice-color-checked)}input:where([type=checkbox],[type=radio]):checked:after{background:var(--choice-color-checked);display:grid}input:where([type=checkbox],[type=radio]):checked:focus{--choice-color-checked: var(--color-focus)}input:where([type=checkbox],[type=radio])[disabled]{--choice-color-back: none;--choice-color-border: var(--color-gray-300);--choice-color-checked: var(--choice-color-disabled);box-shadow:none;cursor:not-allowed}input[type=checkbox]:checked:after{content:"✓";top:0;right:0;bottom:0;left:0;font-weight:700;color:var(--choice-color-icon);line-height:1}input[type=radio]{--choice-rounded: 50%}input[type=radio]:after{top:3px;right:3px;bottom:3px;left:3px;font-size:9px;border-radius:var(--choice-rounded)}input[type=checkbox][data-variant=toggle]{--choice-rounded: var(--choice-height);width:calc(var(--choice-height) * 2);aspect-ratio:2/1}input[type=checkbox][data-variant=toggle]:after{background:var(--choice-color-toggle);display:grid;top:1px;right:1px;bottom:1px;left:1px;width:.8rem;font-size:7px;border-radius:var(--choice-rounded);transition:margin-inline-start 75ms ease-in-out,background .1s ease-in-out}input[type=checkbox][data-variant=toggle]:checked{border-color:var(--choice-color-border)}input[type=checkbox][data-variant=toggle]:checked:after{background:var(--choice-color-checked);margin-inline-start:50%}:root{--range-thumb-color: var(--color-white);--range-thumb-focus-outline: var(--outline);--range-thumb-size: 1rem;--range-thumb-shadow: rgba(0, 0, 0, .1) 0 2px 4px 2px, rgba(0, 0, 0, .125) 0 0 0 1px;--range-track-back: var(--color-gray-250);--range-track-height: var(--range-thumb-size)}:where(input[type=range]){display:flex;align-items:center;-webkit-appearance:none;-moz-appearance:none;appearance:none;padding:0;height:var(--range-thumb-size);border-radius:var(--range-track-size);width:100%}:where(input[type=range])::-webkit-slider-thumb{-webkit-appearance:none;-moz-appearance:none;appearance:none;width:var(--range-thumb-size);height:var(--range-thumb-size);border:0;background:var(--range-thumb-color);box-shadow:var(--range-thumb-shadow);transform:translateZ(0);margin-top:calc(((var(--range-thumb-size) - var(--range-track-height)) / 2) * -1);border-radius:50%;z-index:1;cursor:grab}:where(input[type=range])::-moz-range-thumb{-webkit-appearance:none;-moz-appearance:none;appearance:none;width:var(--range-thumb-size);height:var(--range-thumb-size);border:0;background:var(--range-thumb-color);box-shadow:var(--range-thumb-shadow);border-radius:50%;transform:translateZ(0);z-index:1;cursor:grab}:where(input[type=range])::-webkit-slider-thumb:active{cursor:grabbing}:where(input[type=range])::-moz-range-thumb:active{cursor:grabbing}:where(input[type=range])::-webkit-slider-runnable-track{background:var(--range-track-back);height:var(--range-track-height);border-radius:var(--range-track-height)}:where(input[type=range])::-moz-range-track{background:var(--range-track-back);height:var(--range-track-height);border-radius:var(--range-track-height)}:where(input[type=range][disabled]){--range-thumb-color: rgba(255, 255, 255, .2)}:where(input[type=range][disabled])::-webkit-slider-thumb{cursor:not-allowed}:where(input[type=range][disabled])::-moz-range-thumb{cursor:not-allowed}:where(input[type=range]):focus{outline:var(--outline)}:where(input[type=range]):focus::-webkit-slider-thumb{outline:var(--range-thumb-focus-outline)}:where(input[type=range]):focus::-moz-range-thumb{outline:var(--range-thumb-focus-outline)}*,*:before,*:after{margin:0;padding:0;box-sizing:border-box}:where(b,strong){font-weight:var(--font-bold, 600)}:where([hidden]){display:none!important}:where(abbr){text-decoration:none}:where(input,button,textarea,select){border:0;font:inherit;line-height:inherit;color:inherit;background:none}:where(fieldset){border:0}:where(legend){width:100%;float:left}:where(legend+*){clear:both}:where(select){-webkit-appearance:none;-moz-appearance:none;appearance:none;background:var(--color-white);color:var(--color-black);cursor:pointer}:where(textarea,select,input:not([type=checkbox],[type=radio],[type=reset],[type=submit])){width:100%;font-variant-numeric:tabular-nums}:where(textarea){resize:vertical;line-height:1.5}:where(input)::-webkit-calendar-picker-indicator{display:none}:where(input[type=search]){-webkit-appearance:none;-moz-appearance:none;appearance:none}:where(input)::-webkit-search-cancel-button{display:none}:where(button,label,select,summary,[role=button],[role=option]){cursor:pointer}:where(select[multiple]) option{display:flex;align-items:center}:where(input:-webkit-autofill){-webkit-text-fill-color:var(--input-color-text)!important;-webkit-background-clip:text}:where(:disabled){cursor:not-allowed}*::placeholder{color:var(--input-color-placeholder);opacity:1}:where(a){color:currentColor;text-decoration:none;text-underline-offset:.2ex}:where(ul,ol){list-style:none}:where(img,svg,video,canvas,audio,iframe,embed,object){display:block}:where(iframe){border:0}:where(img,picture,svg){max-inline-size:100%;block-size:auto}:where(p,h1,h2,h3,h4,h5,h6){overflow-wrap:break-word}:where(h1,h2,h3,h4,h5,h6){font:inherit}:where(:focus,:focus-visible,:focus-within){outline-color:var(--color-focus, currentColor);outline-offset:0}:where(:focus-visible){outline:var(--outline, 2px solid var(--color-focus, currentColor))}:where(:invalid){box-shadow:none;outline:0}:where(dialog){border:0;max-width:none;max-height:none}:where(hr){border:0}:where(table){font:inherit;width:100%;border-spacing:0;font-variant-numeric:tabular-nums}:where(table th){font:inherit;text-align:start}:where(svg){fill:currentColor}body{font-family:var(--font-sans, sans-serif);font-size:var(--text-sm);line-height:1;position:relative;accent-color:var(--color-focus, currentColor)}:where(sup,sub){position:relative;line-height:0;vertical-align:baseline;font-size:75%}:where(sup){top:-.5em}:where(sub){bottom:-.25em}:where(mark){background:var(--color-yellow-300)}:where(kbd){display:inline-block;padding-inline:var(--spacing-2);border-radius:var(--rounded);background:var(--color-white);box-shadow:var(--shadow)}[data-align=left]{--align: start}[data-align=center]{--align: center}[data-align=right]{--align: end}@keyframes LoadingCursor{to{cursor:progress}}@keyframes Spin{to{transform:rotate(360deg)}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}[data-theme]{--theme-color-h: 0;--theme-color-s: 0%;--theme-color-hs: var(--theme-color-h), var(--theme-color-s);--theme-color-boost: 3%;--theme-color-l-100: calc(var(--color-l-100) + var(--theme-color-boost));--theme-color-l-200: calc(var(--color-l-200) + var(--theme-color-boost));--theme-color-l-300: calc(var(--color-l-300) + var(--theme-color-boost));--theme-color-l-400: calc(var(--color-l-400) + var(--theme-color-boost));--theme-color-l-500: calc(var(--color-l-500) + var(--theme-color-boost));--theme-color-l-600: calc(var(--color-l-600) + var(--theme-color-boost));--theme-color-l-700: calc(var(--color-l-700) + var(--theme-color-boost));--theme-color-l-800: calc(var(--color-l-800) + var(--theme-color-boost));--theme-color-l-900: calc(var(--color-l-900) + var(--theme-color-boost));--theme-color-100: hsl(var(--theme-color-hs), var(--theme-color-l-100));--theme-color-200: hsl(var(--theme-color-hs), var(--theme-color-l-200));--theme-color-300: hsl(var(--theme-color-hs), var(--theme-color-l-300));--theme-color-400: hsl(var(--theme-color-hs), var(--theme-color-l-400));--theme-color-500: hsl(var(--theme-color-hs), var(--theme-color-l-500));--theme-color-600: hsl(var(--theme-color-hs), var(--theme-color-l-600));--theme-color-700: hsl(var(--theme-color-hs), var(--theme-color-l-700));--theme-color-800: hsl(var(--theme-color-hs), var(--theme-color-l-800));--theme-color-900: hsl(var(--theme-color-hs), var(--theme-color-l-900));--theme-color-text: var(--theme-color-900);--theme-color-text-dimmed: hsl( var(--theme-color-h), calc(var(--theme-color-s) - 60%), 50% );--theme-color-back: var(--theme-color-400);--theme-color-hover: var(--theme-color-500);--theme-color-icon: var(--theme-color-600)}[data-theme^=red],[data-theme^=error],[data-theme^=negative]{--theme-color-h: var(--color-red-h);--theme-color-s: var(--color-red-s);--theme-color-boost: var(--color-red-boost)}[data-theme^=orange],[data-theme^=notice]{--theme-color-h: var(--color-orange-h);--theme-color-s: var(--color-orange-s);--theme-color-boost: var(--color-orange-boost)}[data-theme^=yellow],[data-theme^=warning]{--theme-color-h: var(--color-yellow-h);--theme-color-s: var(--color-yellow-s);--theme-color-boost: var(--color-yellow-boost)}[data-theme^=blue],[data-theme^=info]{--theme-color-h: var(--color-blue-h);--theme-color-s: var(--color-blue-s);--theme-color-boost: var(--color-blue-boost)}[data-theme^=pink],[data-theme^=love]{--theme-color-h: var(--color-pink-h);--theme-color-s: var(--color-pink-s);--theme-color-boost: var(--color-pink-boost)}[data-theme^=green],[data-theme^=positive]{--theme-color-h: var(--color-green-h);--theme-color-s: var(--color-green-s);--theme-color-boost: var(--color-green-boost)}[data-theme^=aqua]{--theme-color-h: var(--color-aqua-h);--theme-color-s: var(--color-aqua-s);--theme-color-boost: var(--color-aqua-boost)}[data-theme^=purple]{--theme-color-h: var(--color-purple-h);--theme-color-s: var(--color-purple-s);--theme-color-boost: var(--color-purple-boost)}[data-theme^=gray],[data-theme^=passive]{--theme-color-h: var(--color-gray-h);--theme-color-s: var(--color-gray-s);--theme-color-boost: 10%}[data-theme^=white],[data-theme^=text]{--theme-color-back: var(--color-white);--theme-color-icon: var(--color-gray-800);--theme-color-text: var(--color-text);--color-h: var(--color-black)}[data-theme^=dark]{--theme-color-h: var(--color-gray-h);--theme-color-s: var(--color-gray-s);--theme-color-boost: var(--color-gray-boost);--theme-color-back: var(--color-gray-800);--theme-color-icon: var(--color-gray-500);--theme-color-text: var(--color-gray-200)}[data-theme=code]{--theme-color-back: var(--code-color-back);--theme-color-hover: var(--color-black);--theme-color-icon: var(--code-color-icon);--theme-color-text: var(--code-color-text);font-family:var(--code-font-family);font-size:var(--code-font-size)}[data-theme=empty]{--theme-color-back: var(--color-light);--theme-color-border: var(--color-gray-400);--theme-color-icon: var(--color-gray-600);--theme-color-text: var(--color-text-dimmed);border:1px dashed var(--theme-color-border)}[data-theme=none]{--theme-color-back: transparent;--theme-color-border: transparent;--theme-color-icon: var(--color-text);--theme-color-text: var(--color-text)}[data-theme]{--theme: var(--theme-color-700);--theme-light: var(--theme-color-500);--theme-bg: var(--theme-color-500)}:root{--outline: 2px solid var(--color-focus, currentColor)}.scroll-x,.scroll-x-auto,.scroll-y,.scroll-y-auto{-webkit-overflow-scrolling:touch;transform:translateZ(0)}.scroll-x{overflow-x:scroll;overflow-y:hidden}.scroll-x-auto{overflow-x:auto;overflow-y:hidden}.scroll-y{overflow-x:hidden;overflow-y:scroll}.scroll-y-auto{overflow-x:hidden;overflow-y:auto}.input-hidden{position:absolute;-webkit-appearance:none;-moz-appearance:none;appearance:none;width:0;height:0;opacity:0}.k-lab-index-view .k-header{margin-bottom:0}.k-lab-index-view .k-panel-main>.k-box{margin-bottom:var(--spacing-8)}.k-lab-index-view .k-list-items{grid-template-columns:repeat(auto-fill,minmax(12rem,1fr))}.k-lab-docs-deprecated .k-box{box-shadow:var(--shadow)}.k-lab-docs-examples .k-code+.k-code{margin-top:var(--spacing-4)}.k-lab-docs-prop-values{font-size:var(--text-xs);border-left:2px solid var(--color-blue-300);padding-inline-start:var(--spacing-2)}.k-lab-docs-prop-values dl{font-weight:var(--font-bold)}.k-lab-docs-prop-values dl+dl{margin-top:var(--spacing-2)}.k-lab-docs-prop-values dd{display:inline-flex;flex-wrap:wrap;gap:var(--spacing-1)}.k-lab-docs-desc-header{display:flex;justify-content:space-between;align-items:center}.k-table .k-lab-docs-deprecated{--box-height: var(--height-xs);--text-font-size: var(--text-xs)}.k-labs-docs-params li{list-style:square;margin-inline-start:var(--spacing-3)}.k-labs-docs-params .k-lab-docs-types{margin-inline:1ch}.k-lab-docs-types{display:inline-flex;flex-wrap:wrap;gap:var(--spacing-1)}.k-lab-docs-types.k-text code{color:var(--color-gray-800);outline-color:var(--color-gray-400);background:var(--color-gray-300)}.k-lab-docs-types code:is([data-type=boolean],[data-type=Boolean]){color:var(--color-purple-800);outline-color:var(--color-purple-400);background:var(--color-purple-300)}.k-lab-docs-types code:is([data-type=string],[data-type=String]){color:var(--color-green-800);outline-color:var(--color-green-500);background:var(--color-green-300)}.k-lab-docs-types code:is([data-type=number],[data-type=Number]){color:var(--color-orange-800);outline-color:var(--color-orange-500);background:var(--color-orange-300)}.k-lab-docs-types code:is([data-type=array],[data-type=Array]){color:var(--color-aqua-800);outline-color:var(--color-aqua-500);background:var(--color-aqua-300)}.k-lab-docs-types code:is([data-type=object],[data-type=Object]){color:var(--color-yellow-800);outline-color:var(--color-yellow-500);background:var(--color-yellow-300)}.k-lab-docs-types code[data-type=func]{color:var(--color-pink-800);outline-color:var(--color-pink-400);background:var(--color-pink-300)}.k-lab-docs-section+.k-lab-docs-section{margin-top:var(--spacing-12)}.k-lab-docs-section .k-headline{margin-bottom:var(--spacing-3)}.k-lab-docs-section .k-table td{padding:.375rem var(--table-cell-padding);vertical-align:top;line-height:1.5;word-break:break-word}.k-lab-docs-description :where(.k-text,.k-box)+:where(.k-text,.k-box){margin-top:var(--spacing-3)}.k-lab-docs-required{margin-inline-start:var(--spacing-1);font-size:.7rem;vertical-align:super;color:var(--color-red-600)}.k-lab-docs-since{margin-top:var(--spacing-1);font-size:var(--text-xs);color:var(--color-gray-600)}.k-lab-example{position:relative;container-type:inline-size;max-width:100%;outline-offset:-2px;border-radius:var(--rounded);border:1px solid var(--color-gray-300)}.k-lab-example+.k-lab-example{margin-top:var(--spacing-12)}.k-lab-example-header{display:flex;justify-content:space-between;align-items:center;height:var(--height-md);padding-block:var(--spacing-3);padding-inline:var(--spacing-2);border-bottom:1px solid var(--color-gray-300)}.k-lab-example-label{font-size:12px;color:var(--color-text-dimmed)}.k-lab-example-canvas,.k-lab-example-code{padding:var(--spacing-16)}.k-lab-example[data-flex] .k-lab-example-canvas{display:flex;align-items:center;gap:var(--spacing-6)}.k-lab-example-inspector{--icon-size: 13px;--button-color-icon: var(--color-gray-500)}.k-lab-example-inspector .k-button:not([data-theme]):hover{--button-color-icon: var(--color-gray-600)}.k-lab-example-inspector .k-button:where([data-theme]){--button-color-icon: var(--color-gray-800)}.k-lab-examples>:where(.k-text,.k-box){margin-bottom:var(--spacing-6)}.k-lab-form>footer{border-top:1px dashed var(--color-border);padding-top:var(--spacing-6)}.k-lab-playground-view[data-has-tabs=true] .k-header{margin-bottom:0}.k-lab-examples h2{margin-bottom:var(--spacing-6)}.k-lab-examples *+h2{margin-top:var(--spacing-12)}.k-lab-input-examples .k-lab-example:has(:invalid){outline:2px solid var(--color-red-500);outline-offset:-2px}.k-lab-input-examples-focus .k-lab-example-canvas>.k-button{margin-top:var(--spacing-6)}.k-lab-helpers-examples .k-lab-example .k-text{margin-bottom:var(--spacing-6)}.k-lab-helpers-examples h2{margin-bottom:var(--spacing-3);font-weight:var(--font-bold)}.token.punctuation,.token.comment,.token.doctype{color:var(--color-gray-500)}.token.tag,.token.markup,.token.variable,.token.this,.token.selector,.token.key,.token.kirbytag-bracket,.token.prolog,.token.delimiter{color:var(--color-red-500)}.token.constant,.token.number,.token.boolean,.token.boolean.important,.token.attr-name,.token.kirbytag-attr,.token.kirbytag-name,.token.entity,.token.bold,.token.bold>.punctuation{color:var(--color-orange-500)}.token.keyword,.token.italic,.token.italic>.punctuation{color:var(--color-purple-500)}.token.function{color:var(--color-blue-500)}.token.operator,.token.title{color:var(--color-aqua-500)}.token.string,.token.attr-value,.token.attr-value .punctuation,.token.list.punctuation{color:var(--color-green-500)}.token.scope,.token.class-name,.token.property,.token.url{color:var(--color-yellow-500)}.token.title,.token.kirbytag-bracket,.token.list.punctuation,.token.bold{font-weight:var(--font-bold)}.token.title .punctuation{color:var(--color-gray-500)}.token.italic{font-style:italic} diff --git a/kirby/panel/dist/favicon-dark.png b/kirby/panel/dist/favicon-dark.png new file mode 100644 index 0000000..dcf5e58 Binary files /dev/null and b/kirby/panel/dist/favicon-dark.png differ diff --git a/kirby/panel/dist/img/icons.svg b/kirby/panel/dist/img/icons.svg index 81446cd..a95141e 100644 --- a/kirby/panel/dist/img/icons.svg +++ b/kirby/panel/dist/img/icons.svg @@ -1,708 +1,653 @@ + diff --git a/kirby/panel/dist/js/Docs.min.js b/kirby/panel/dist/js/Docs.min.js new file mode 100644 index 0000000..7a252c4 --- /dev/null +++ b/kirby/panel/dist/js/Docs.min.js @@ -0,0 +1 @@ +import{n as t}from"./index.min.js";const e={props:{deprecated:String}};const s=t({mixins:[e]},(function(){var t=this,e=t._self._c;return t.deprecated.length?e("section",{staticClass:"k-lab-docs-section k-lab-docs-deprecated"},[e("k-lab-docs-deprecated",{attrs:{deprecated:t.deprecated}})],1):t._e()}),[]).exports,a={props:{description:String,since:String}};const n=t({mixins:[a]},(function(){var t,e,s,a=this,n=a._self._c;return(null==(t=a.description)?void 0:t.length)||(null==(e=a.since)?void 0:e.length)?n("section",{staticClass:"k-lab-docs-section"},[n("header",{staticClass:"k-lab-docs-desc-header"},[n("k-headline",{staticClass:"h3"},[a._v("Description")]),(null==(s=a.since)?void 0:s.length)?n("k-text",[a._v(" Since "),n("code",[a._v(a._s(a.since))])]):a._e()],1),n("k-box",{attrs:{theme:"text"}},[n("k-text",{attrs:{html:a.description}})],1)],1):a._e()}),[]).exports,c={props:{examples:{default:()=>[],type:Array}}};const r=t({mixins:[c]},(function(){var t=this,e=t._self._c;return t.examples.length?e("section",{staticClass:"k-lab-docs-section k-lab-docs-examples"},[e("k-headline",{staticClass:"h3"},[t._v("Examples")]),t._l(t.examples,(function(s,a){return e("k-code",{key:a,attrs:{language:"html"}},[t._v(t._s(s.content))])}))],2):t._e()}),[]).exports,l={props:{props:{default:()=>[],type:Array}}};const i=t({mixins:[l]},(function(){var t=this,e=t._self._c;return t.props.length?e("section",{staticClass:"k-lab-docs-section"},[e("k-headline",{staticClass:"h3"},[t._v("Props")]),e("div",{staticClass:"k-table"},[e("table",[t._m(0),e("tbody",t._l(t.props,(function(s){var a,n,c,r,l,i,o,d,p;return e("tr",{key:s.name},[e("td",[e("k-text",[e("code",[t._v(t._s(s.name))]),s.required?e("abbr",{staticClass:"k-lab-docs-required"},[t._v("✶")]):t._e(),(null==(a=s.since)?void 0:a.length)?e("div",{staticClass:"k-lab-docs-since"},[t._v(" since "+t._s(s.since)+" ")]):t._e()])],1),e("td",[e("k-lab-docs-types",{attrs:{types:null==(n=s.type)?void 0:n.split("|")}})],1),e("td",[s.default?e("k-text",[e("code",[t._v(t._s(s.default))])]):t._e()],1),e("td",{staticClass:"k-lab-docs-description"},[e("k-lab-docs-deprecated",{attrs:{deprecated:s.deprecated}}),(null==(c=s.description)?void 0:c.length)?e("k-text",{attrs:{html:s.description}}):t._e(),(null==(r=s.value)?void 0:r.length)||(null==(l=s.values)?void 0:l.length)||(null==(i=s.example)?void 0:i.length)?e("k-text",{staticClass:"k-lab-docs-prop-values"},[(null==(o=s.value)?void 0:o.length)?e("dl",[e("dt",[t._v("Value")]),e("dd",[e("code",[t._v(t._s(s.value))])])]):t._e(),(null==(d=s.values)?void 0:d.length)?e("dl",[e("dt",[t._v("Values")]),e("dd",t._l(s.values,(function(s){return e("code",{key:s},[t._v(" "+t._s(s.replaceAll("`",""))+" ")])})),0)]):t._e(),(null==(p=s.example)?void 0:p.length)?e("dl",[e("dt",[t._v("Example")]),e("dd",[e("code",[t._v(t._s(s.example))])])]):t._e()]):t._e()],1)])})),0)])])],1):t._e()}),[function(){var t=this,e=t._self._c;return e("thead",[e("th",{staticStyle:{width:"10rem"}},[t._v("Name")]),e("th",{staticStyle:{width:"10rem"}},[t._v("Type")]),e("th",{staticStyle:{width:"10rem"}},[t._v("Default")]),e("th",[t._v("Description")])])}]).exports,o={props:{slots:{default:()=>[],type:Array}},computed:{hasBindings(){return this.slots.filter((t=>t.bindings.length)).length}}};const d=t({mixins:[o]},(function(){var t=this,e=t._self._c;return t.slots.length?e("section",{staticClass:"k-lab-docs-section"},[e("k-headline",{staticClass:"h3"},[t._v("Slots")]),e("div",{staticClass:"k-table"},[e("table",[e("thead",[e("th",{staticStyle:{width:"10rem"}},[t._v("Slot")]),e("th",[t._v("Description")]),t.hasBindings?e("th",[t._v("Bindings")]):t._e()]),e("tbody",t._l(t.slots,(function(s){var a;return e("tr",{key:s.name},[e("td",{staticStyle:{width:"12rem"}},[e("k-text",[e("code",[t._v(t._s(s.name))]),(null==(a=s.since)?void 0:a.length)?e("div",{staticClass:"k-lab-docs-since"},[t._v(" since "+t._s(s.since)+" ")]):t._e()])],1),e("td",[e("k-lab-docs-deprecated",{attrs:{deprecated:s.deprecated}}),e("k-text",{attrs:{html:s.description}})],1),t.hasBindings?e("td",[e("k-lab-docs-params",{attrs:{params:s.bindings}})],1):t._e()])})),0)])])],1):t._e()}),[]).exports,p={props:{events:{default:()=>[],type:Array}},computed:{hasProperties(){return this.events.filter((t=>t.properties.length)).length}}};const _=t({mixins:[p]},(function(){var t=this,e=t._self._c;return t.events.length?e("section",{staticClass:"k-lab-docs-section"},[e("k-headline",{staticClass:"h3"},[t._v("Events")]),e("div",{staticClass:"k-table"},[e("table",[e("thead",[e("th",{staticStyle:{width:"10rem"}},[t._v("Event")]),e("th",[t._v("Description")]),t.hasProperties?e("th",[t._v("Properties")]):t._e()]),e("tbody",t._l(t.events,(function(s){var a;return e("tr",{key:s.name},[e("td",[e("k-text",[e("code",[t._v(t._s(s.name))]),(null==(a=s.since)?void 0:a.length)?e("div",{staticClass:"k-lab-docs-since"},[t._v(" since "+t._s(s.since)+" ")]):t._e()])],1),e("td",[e("k-lab-docs-deprecated",{attrs:{deprecated:s.deprecated}}),e("k-text",{attrs:{html:s.description}})],1),t.hasProperties?e("td",[e("k-lab-docs-params",{attrs:{params:s.properties}})],1):t._e()])})),0)])])],1):t._e()}),[]).exports,h={props:{methods:{default:()=>[],type:Array}}};const v=t({mixins:[h]},(function(){var t=this,e=t._self._c;return t.methods.length?e("section",{staticClass:"k-lab-docs-section"},[e("k-headline",{staticClass:"h3"},[t._v("Methods")]),e("div",{staticClass:"k-table"},[e("table",[t._m(0),e("tbody",t._l(t.methods,(function(s){var a;return e("tr",{key:s.name},[e("td",[e("k-text",[e("code",[t._v(t._s(s.name))]),(null==(a=s.since)?void 0:a.length)?e("div",{staticClass:"k-lab-docs-since"},[t._v(" since "+t._s(s.since)+" ")]):t._e()])],1),e("td",[e("k-lab-docs-deprecated",{attrs:{deprecated:s.deprecated}}),e("k-text",{attrs:{html:s.description}})],1),e("td",[e("k-lab-docs-params",{attrs:{params:s.params}})],1),e("td",[e("k-lab-docs-types",{attrs:{types:[s.returns]}})],1)])})),0)])])],1):t._e()}),[function(){var t=this,e=t._self._c;return e("thead",[e("th",{staticStyle:{width:"10rem"}},[t._v("Method")]),e("th",[t._v("Description")]),e("th",{staticStyle:{width:"16rem"}},[t._v("Params")]),e("th",{staticStyle:{width:"10rem"}},[t._v("Returns")])])}]).exports,k={props:{docBlock:String}};const u=t({mixins:[k]},(function(){var t,e=this,s=e._self._c;return(null==(t=e.docBlock)?void 0:t.length)?s("section",{staticClass:"k-lab-docs-section"},[s("header",[s("k-headline",{staticClass:"h3"},[e._v("Further information")])],1),s("k-box",{attrs:{theme:"text"}},[s("k-text",{attrs:{html:e.docBlock}})],1)],1):e._e()}),[]).exports;const m=t({props:{deprecated:{type:String}}},(function(){var t,e=this,s=e._self._c;return(null==(t=e.deprecated)?void 0:t.length)?s("k-box",{staticClass:"k-lab-docs-deprecated",attrs:{icon:"protected",theme:"warning"}},[s("k-text",{attrs:{html:"Deprecated: "+e.deprecated}})],1):e._e()}),[]).exports;const b=t({props:{params:{type:Array,default:()=>[]}}},(function(){var t=this,e=t._self._c;return t.params.length?e("ul",{staticClass:"k-labs-docs-params"},t._l(t.params,(function(s){return e("li",{key:s.name},[e("k-text",[e("code",[t._v(t._s(s.name))]),e("k-lab-docs-types",{attrs:{types:[s.type]}}),s.description.length?e("span",{domProps:{innerHTML:t._s(s.description)}}):t._e()],1)],1)})),0):t._e()}),[]).exports;const x=t({props:{types:{type:Array,default:()=>[]}}},(function(){var t=this,e=t._self._c;return e("k-text",{staticClass:"k-lab-docs-types"},t._l(t.types,(function(s){return e("code",{key:s,attrs:{"data-type":s}},[t._v(" "+t._s(s)+" ")])})),0)}),[]).exports;Vue.component("k-lab-docs-deprecated",m),Vue.component("k-lab-docs-params",b),Vue.component("k-lab-docs-types",x);const f=t({components:{"k-lab-docs-deprecated":s,"k-lab-docs-description":n,"k-lab-docs-examples":r,"k-lab-docs-props":i,"k-lab-docs-slots":d,"k-lab-docs-events":_,"k-lab-docs-methods":v,"k-lab-docs-docblock":u},mixins:[e,a,c,l,o,p,h,k],props:{component:String}},(function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-lab-docs"},[e("k-lab-docs-deprecated",{attrs:{deprecated:t.deprecated}}),e("k-lab-docs-description",{attrs:{description:t.description,since:t.since}}),e("k-lab-docs-examples",{attrs:{examples:t.examples}}),e("k-lab-docs-props",{attrs:{props:t.props}}),e("k-lab-docs-slots",{attrs:{slots:t.slots}}),e("k-lab-docs-events",{attrs:{events:t.events}}),e("k-lab-docs-methods",{attrs:{methods:t.methods}}),e("k-lab-docs-docblock",{attrs:{"doc-block":t.docBlock}})],1)}),[]).exports;export{f as D}; diff --git a/kirby/panel/dist/js/DocsView.min.js b/kirby/panel/dist/js/DocsView.min.js new file mode 100644 index 0000000..887e7dc --- /dev/null +++ b/kirby/panel/dist/js/DocsView.min.js @@ -0,0 +1 @@ +import{D as t}from"./Docs.min.js";import{n as s}from"./index.min.js";import"./vendor.min.js";const o=s({components:{"k-lab-docs":t},props:{component:String,docs:Object,lab:String},mounted(){},methods:{reloadDocs(){this.$panel.view.refresh()}}},(function(){var t=this,s=t._self._c;return s("k-panel-inside",{staticClass:"k-lab-docs-view"},[s("k-header",[t._v(" "+t._s(t.component)+" "),t.docs.github||t.lab?s("k-button-group",{attrs:{slot:"buttons"},slot:"buttons"},[t.lab?s("k-button",{attrs:{icon:"lab",text:"Lab examples",size:"sm",variant:"filled",link:"/lab/"+t.lab}}):t._e(),t.docs.github?s("k-button",{attrs:{icon:"github",size:"sm",variant:"filled",link:t.docs.github,target:"_blank"}}):t._e()],1):t._e()],1),s("k-lab-docs",t._b({},"k-lab-docs",t.docs,!1))],1)}),[]).exports;export{o as default}; diff --git a/kirby/panel/dist/js/Highlight.min.js b/kirby/panel/dist/js/Highlight.min.js new file mode 100644 index 0000000..76b89e9 --- /dev/null +++ b/kirby/panel/dist/js/Highlight.min.js @@ -0,0 +1 @@ +import{n as e}from"./index.min.js";import"./vendor.min.js";var t=function(e){var t=/(?:^|\s)lang(?:uage)?-([\w-]+)(?=\s|$)/i,a=0,n={},r={manual:e.Prism&&e.Prism.manual,disableWorkerMessageHandler:e.Prism&&e.Prism.disableWorkerMessageHandler,util:{encode:function e(t){return t instanceof i?new i(t.type,e(t.content),t.alias):Array.isArray(t)?t.map(e):t.replace(/&/g,"&").replace(/=c.reach);F+=w.value.length,w=w.next){var A=w.value;if(t.length>e.length)return;if(!(A instanceof i)){var $,_=1;if(y){if(!($=s(x,F,e,m))||$.index>=e.length)break;var z=$.index,S=$.index+$[0].length,j=F;for(j+=w.value.length;z>=j;)j+=(w=w.next).value.length;if(F=j-=w.value.length,w.value instanceof i)continue;for(var E=w;E!==t.tail&&(jc.reach&&(c.reach=L);var C=w.prev;if(O&&(C=u(t,C,O),F+=O.length),d(t,C,_),w=u(t,C,new i(g,h?r.tokenize(P,h):P,k,P)),q&&u(t,w,q),_>1){var T={cause:g+","+f,reach:L};l(e,t,a,w.prev,F,T),c&&T.reach>c.reach&&(c.reach=T.reach)}}}}}}function o(){var e={value:null,prev:null,next:null},t={value:null,prev:e,next:null};e.next=t,this.head=e,this.tail=t,this.length=0}function u(e,t,a){var n=t.next,r={value:a,prev:t,next:n};return t.next=r,n.prev=r,e.length++,r}function d(e,t,a){for(var n=t.next,r=0;r"+i.content+""},!e.document)return e.addEventListener?(r.disableWorkerMessageHandler||e.addEventListener("message",(function(t){var a=JSON.parse(t.data),n=a.language,i=a.code,s=a.immediateClose;e.postMessage(r.highlight(i,r.languages[n],n)),s&&e.close()}),!1),r):r;var c=r.util.currentScript();function g(){r.manual||r.highlightAll()}if(c&&(r.filename=c.src,c.hasAttribute("data-manual")&&(r.manual=!0)),!r.manual){var p=document.readyState;"loading"===p||"interactive"===p&&c&&c.defer?document.addEventListener("DOMContentLoaded",g):window.requestAnimationFrame?window.requestAnimationFrame(g):window.setTimeout(g,16)}return r}("undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{});"undefined"!=typeof module&&module.exports&&(module.exports=t),"undefined"!=typeof global&&(global.Prism=t),t.languages.markup={comment:{pattern://,greedy:!0},prolog:{pattern:/<\?[\s\S]+?\?>/,greedy:!0},doctype:{pattern:/"'[\]]|"[^"]*"|'[^']*')+(?:\[(?:[^<"'\]]|"[^"]*"|'[^']*'|<(?!!--)|)*\]\s*)?>/i,greedy:!0,inside:{"internal-subset":{pattern:/(^[^\[]*\[)[\s\S]+(?=\]>$)/,lookbehind:!0,greedy:!0,inside:null},string:{pattern:/"[^"]*"|'[^']*'/,greedy:!0},punctuation:/^$|[[\]]/,"doctype-tag":/^DOCTYPE/i,name:/[^\s<>'"]+/}},cdata:{pattern://i,greedy:!0},tag:{pattern:/<\/?(?!\d)[^\s>\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?\s*\/?>/,greedy:!0,inside:{tag:{pattern:/^<\/?[^\s>\/]+/,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"special-attr":[],"attr-value":{pattern:/=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+)/,inside:{punctuation:[{pattern:/^=/,alias:"attr-equals"},{pattern:/^(\s*)["']|["']$/,lookbehind:!0}]}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:[{pattern:/&[\da-z]{1,8};/i,alias:"named-entity"},/&#x?[\da-f]{1,8};/i]},t.languages.markup.tag.inside["attr-value"].inside.entity=t.languages.markup.entity,t.languages.markup.doctype.inside["internal-subset"].inside=t.languages.markup,t.hooks.add("wrap",(function(e){"entity"===e.type&&(e.attributes.title=e.content.replace(/&/,"&"))})),Object.defineProperty(t.languages.markup.tag,"addInlined",{value:function(e,a){var n={};n["language-"+a]={pattern:/(^$)/i,lookbehind:!0,inside:t.languages[a]},n.cdata=/^$/i;var r={"included-cdata":{pattern://i,inside:n}};r["language-"+a]={pattern:/[\s\S]+/,inside:t.languages[a]};var i={};i[e]={pattern:RegExp("(<__[^>]*>)(?:))*\\]\\]>|(?!)".replace(/__/g,(function(){return e})),"i"),lookbehind:!0,greedy:!0,inside:r},t.languages.insertBefore("markup","cdata",i)}}),Object.defineProperty(t.languages.markup.tag,"addAttribute",{value:function(e,a){t.languages.markup.tag.inside["special-attr"].push({pattern:RegExp("(^|[\"'\\s])(?:"+e+")\\s*=\\s*(?:\"[^\"]*\"|'[^']*'|[^\\s'\">=]+(?=[\\s>]))","i"),lookbehind:!0,inside:{"attr-name":/^[^\s=]+/,"attr-value":{pattern:/=[\s\S]+/,inside:{value:{pattern:/(^=\s*(["']|(?!["'])))\S[\s\S]*(?=\2$)/,lookbehind:!0,alias:[a,"language-"+a],inside:t.languages[a]},punctuation:[{pattern:/^=/,alias:"attr-equals"},/"|'/]}}}})}}),t.languages.html=t.languages.markup,t.languages.mathml=t.languages.markup,t.languages.svg=t.languages.markup,t.languages.xml=t.languages.extend("markup",{}),t.languages.ssml=t.languages.xml,t.languages.atom=t.languages.xml,t.languages.rss=t.languages.xml,function(e){var t=/(?:"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"|'(?:\\(?:\r\n|[\s\S])|[^'\\\r\n])*')/;e.languages.css={comment:/\/\*[\s\S]*?\*\//,atrule:{pattern:RegExp("@[\\w-](?:[^;{\\s\"']|\\s+(?!\\s)|"+t.source+")*?(?:;|(?=\\s*\\{))"),inside:{rule:/^@[\w-]+/,"selector-function-argument":{pattern:/(\bselector\s*\(\s*(?![\s)]))(?:[^()\s]|\s+(?![\s)])|\((?:[^()]|\([^()]*\))*\))+(?=\s*\))/,lookbehind:!0,alias:"selector"},keyword:{pattern:/(^|[^\w-])(?:and|not|only|or)(?![\w-])/,lookbehind:!0}}},url:{pattern:RegExp("\\burl\\((?:"+t.source+"|(?:[^\\\\\r\n()\"']|\\\\[^])*)\\)","i"),greedy:!0,inside:{function:/^url/i,punctuation:/^\(|\)$/,string:{pattern:RegExp("^"+t.source+"$"),alias:"url"}}},selector:{pattern:RegExp("(^|[{}\\s])[^{}\\s](?:[^{};\"'\\s]|\\s+(?![\\s{])|"+t.source+")*(?=\\s*\\{)"),lookbehind:!0},string:{pattern:t,greedy:!0},property:{pattern:/(^|[^-\w\xA0-\uFFFF])(?!\s)[-_a-z\xA0-\uFFFF](?:(?!\s)[-\w\xA0-\uFFFF])*(?=\s*:)/i,lookbehind:!0},important:/!important\b/i,function:{pattern:/(^|[^-a-z0-9])[-a-z0-9]+(?=\()/i,lookbehind:!0},punctuation:/[(){};:,]/},e.languages.css.atrule.inside.rest=e.languages.css;var a=e.languages.markup;a&&(a.tag.addInlined("style","css"),a.tag.addAttribute("style","css"))}(t),t.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,lookbehind:!0,greedy:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"class-name":{pattern:/(\b(?:class|extends|implements|instanceof|interface|new|trait)\s+|\bcatch\s+\()[\w.\\]+/i,lookbehind:!0,inside:{punctuation:/[.\\]/}},keyword:/\b(?:break|catch|continue|do|else|finally|for|function|if|in|instanceof|new|null|return|throw|try|while)\b/,boolean:/\b(?:false|true)\b/,function:/\b\w+(?=\()/,number:/\b0x[\da-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?/i,operator:/[<>]=?|[!=]=?=?|--?|\+\+?|&&?|\|\|?|[?*/~^%]/,punctuation:/[{}[\];(),.:]/},t.languages.javascript=t.languages.extend("clike",{"class-name":[t.languages.clike["class-name"],{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$A-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\.(?:constructor|prototype))/,lookbehind:!0}],keyword:[{pattern:/((?:^|\})\s*)catch\b/,lookbehind:!0},{pattern:/(^|[^.]|\.\.\.\s*)\b(?:as|assert(?=\s*\{)|async(?=\s*(?:function\b|\(|[$\w\xA0-\uFFFF]|$))|await|break|case|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally(?=\s*(?:\{|$))|for|from(?=\s*(?:['"]|$))|function|(?:get|set)(?=\s*(?:[#\[$\w\xA0-\uFFFF]|$))|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)\b/,lookbehind:!0}],function:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*(?:\.\s*(?:apply|bind|call)\s*)?\()/,number:{pattern:RegExp("(^|[^\\w$])(?:NaN|Infinity|0[bB][01]+(?:_[01]+)*n?|0[oO][0-7]+(?:_[0-7]+)*n?|0[xX][\\dA-Fa-f]+(?:_[\\dA-Fa-f]+)*n?|\\d+(?:_\\d+)*n|(?:\\d+(?:_\\d+)*(?:\\.(?:\\d+(?:_\\d+)*)?)?|\\.\\d+(?:_\\d+)*)(?:[Ee][+-]?\\d+(?:_\\d+)*)?)(?![\\w$])"),lookbehind:!0},operator:/--|\+\+|\*\*=?|=>|&&=?|\|\|=?|[!=]==|<<=?|>>>?=?|[-+*/%&|^!=<>]=?|\.{3}|\?\?=?|\?\.?|[~:]/}),t.languages.javascript["class-name"][0].pattern=/(\b(?:class|extends|implements|instanceof|interface|new)\s+)[\w.\\]+/,t.languages.insertBefore("javascript","keyword",{regex:{pattern:RegExp("((?:^|[^$\\w\\xA0-\\uFFFF.\"'\\])\\s]|\\b(?:return|yield))\\s*)/(?:(?:\\[(?:[^\\]\\\\\r\n]|\\\\.)*\\]|\\\\.|[^/\\\\\\[\r\n])+/[dgimyus]{0,7}|(?:\\[(?:[^[\\]\\\\\r\n]|\\\\.|\\[(?:[^[\\]\\\\\r\n]|\\\\.|\\[(?:[^[\\]\\\\\r\n]|\\\\.)*\\])*\\])*\\]|\\\\.|[^/\\\\\\[\r\n])+/[dgimyus]{0,7}v[dgimyus]{0,7})(?=(?:\\s|/\\*(?:[^*]|\\*(?!/))*\\*/)*(?:$|[\r\n,.;:})\\]]|//))"),lookbehind:!0,greedy:!0,inside:{"regex-source":{pattern:/^(\/)[\s\S]+(?=\/[a-z]*$)/,lookbehind:!0,alias:"language-regex",inside:t.languages.regex},"regex-delimiter":/^\/|\/$/,"regex-flags":/^[a-z]+$/}},"function-variable":{pattern:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*[=:]\s*(?:async\s*)?(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)\s*=>))/,alias:"function"},parameter:[{pattern:/(function(?:\s+(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)?\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\))/,lookbehind:!0,inside:t.languages.javascript},{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$a-z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*=>)/i,lookbehind:!0,inside:t.languages.javascript},{pattern:/(\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*=>)/,lookbehind:!0,inside:t.languages.javascript},{pattern:/((?:\b|\s|^)(?!(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)(?![$\w\xA0-\uFFFF]))(?:(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*\s*)\(\s*|\]\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*\{)/,lookbehind:!0,inside:t.languages.javascript}],constant:/\b[A-Z](?:[A-Z_]|\dx?)*\b/}),t.languages.insertBefore("javascript","string",{hashbang:{pattern:/^#!.*/,greedy:!0,alias:"comment"},"template-string":{pattern:/`(?:\\[\s\S]|\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}|(?!\$\{)[^\\`])*`/,greedy:!0,inside:{"template-punctuation":{pattern:/^`|`$/,alias:"string"},interpolation:{pattern:/((?:^|[^\\])(?:\\{2})*)\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}/,lookbehind:!0,inside:{"interpolation-punctuation":{pattern:/^\$\{|\}$/,alias:"punctuation"},rest:t.languages.javascript}},string:/[\s\S]+/}},"string-property":{pattern:/((?:^|[,{])[ \t]*)(["'])(?:\\(?:\r\n|[\s\S])|(?!\2)[^\\\r\n])*\2(?=\s*:)/m,lookbehind:!0,greedy:!0,alias:"property"}}),t.languages.insertBefore("javascript","operator",{"literal-property":{pattern:/((?:^|[,{])[ \t]*)(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*:)/m,lookbehind:!0,alias:"property"}}),t.languages.markup&&(t.languages.markup.tag.addInlined("script","javascript"),t.languages.markup.tag.addAttribute("on(?:abort|blur|change|click|composition(?:end|start|update)|dblclick|error|focus(?:in|out)?|key(?:down|up)|load|mouse(?:down|enter|leave|move|out|over|up)|reset|resize|scroll|select|slotchange|submit|unload|wheel)","javascript")),t.languages.js=t.languages.javascript,function(e){function t(e,t){return"___"+e.toUpperCase()+t+"___"}Object.defineProperties(e.languages["markup-templating"]={},{buildPlaceholders:{value:function(a,n,r,i){if(a.language===n){var s=a.tokenStack=[];a.code=a.code.replace(r,(function(e){if("function"==typeof i&&!i(e))return e;for(var r,l=s.length;-1!==a.code.indexOf(r=t(n,l));)++l;return s[l]=e,r})),a.grammar=e.languages.markup}}},tokenizePlaceholders:{value:function(a,n){if(a.language===n&&a.tokenStack){a.grammar=e.languages[n];var r=0,i=Object.keys(a.tokenStack);!function s(l){for(var o=0;o=i.length);o++){var u=l[o];if("string"==typeof u||u.content&&"string"==typeof u.content){var d=i[r],c=a.tokenStack[d],g="string"==typeof u?u:u.content,p=t(n,d),f=g.indexOf(p);if(f>-1){++r;var b=g.substring(0,f),h=new e.Token(n,e.tokenize(c,a.grammar),"language-"+n,c),m=g.substring(f+p.length),y=[];b&&y.push.apply(y,s([b])),y.push(h),m&&y.push.apply(y,s([m])),"string"==typeof u?l.splice.apply(l,[o,1].concat(y)):u.content=y}}else u.content&&s(u.content)}return l}(a.tokens)}}}})}(t),function(e){var t=/\/\*[\s\S]*?\*\/|\/\/.*|#(?!\[).*/,a=[{pattern:/\b(?:false|true)\b/i,alias:"boolean"},{pattern:/(::\s*)\b[a-z_]\w*\b(?!\s*\()/i,greedy:!0,lookbehind:!0},{pattern:/(\b(?:case|const)\s+)\b[a-z_]\w*(?=\s*[;=])/i,greedy:!0,lookbehind:!0},/\b(?:null)\b/i,/\b[A-Z_][A-Z0-9_]*\b(?!\s*\()/],n=/\b0b[01]+(?:_[01]+)*\b|\b0o[0-7]+(?:_[0-7]+)*\b|\b0x[\da-f]+(?:_[\da-f]+)*\b|(?:\b\d+(?:_\d+)*\.?(?:\d+(?:_\d+)*)?|\B\.\d+)(?:e[+-]?\d+)?/i,r=/|\?\?=?|\.{3}|\??->|[!=]=?=?|::|\*\*=?|--|\+\+|&&|\|\||<<|>>|[?~]|[/^|%*&<>.+-]=?/,i=/[{}\[\](),:;]/;e.languages.php={delimiter:{pattern:/\?>$|^<\?(?:php(?=\s)|=)?/i,alias:"important"},comment:t,variable:/\$+(?:\w+\b|(?=\{))/,package:{pattern:/(namespace\s+|use\s+(?:function\s+)?)(?:\\?\b[a-z_]\w*)+\b(?!\\)/i,lookbehind:!0,inside:{punctuation:/\\/}},"class-name-definition":{pattern:/(\b(?:class|enum|interface|trait)\s+)\b[a-z_]\w*(?!\\)\b/i,lookbehind:!0,alias:"class-name"},"function-definition":{pattern:/(\bfunction\s+)[a-z_]\w*(?=\s*\()/i,lookbehind:!0,alias:"function"},keyword:[{pattern:/(\(\s*)\b(?:array|bool|boolean|float|int|integer|object|string)\b(?=\s*\))/i,alias:"type-casting",greedy:!0,lookbehind:!0},{pattern:/([(,?]\s*)\b(?:array(?!\s*\()|bool|callable|(?:false|null)(?=\s*\|)|float|int|iterable|mixed|object|self|static|string)\b(?=\s*\$)/i,alias:"type-hint",greedy:!0,lookbehind:!0},{pattern:/(\)\s*:\s*(?:\?\s*)?)\b(?:array(?!\s*\()|bool|callable|(?:false|null)(?=\s*\|)|float|int|iterable|mixed|never|object|self|static|string|void)\b/i,alias:"return-type",greedy:!0,lookbehind:!0},{pattern:/\b(?:array(?!\s*\()|bool|float|int|iterable|mixed|object|string|void)\b/i,alias:"type-declaration",greedy:!0},{pattern:/(\|\s*)(?:false|null)\b|\b(?:false|null)(?=\s*\|)/i,alias:"type-declaration",greedy:!0,lookbehind:!0},{pattern:/\b(?:parent|self|static)(?=\s*::)/i,alias:"static-context",greedy:!0},{pattern:/(\byield\s+)from\b/i,lookbehind:!0},/\bclass\b/i,{pattern:/((?:^|[^\s>:]|(?:^|[^-])>|(?:^|[^:]):)\s*)\b(?:abstract|and|array|as|break|callable|case|catch|clone|const|continue|declare|default|die|do|echo|else|elseif|empty|enddeclare|endfor|endforeach|endif|endswitch|endwhile|enum|eval|exit|extends|final|finally|fn|for|foreach|function|global|goto|if|implements|include|include_once|instanceof|insteadof|interface|isset|list|match|namespace|never|new|or|parent|print|private|protected|public|readonly|require|require_once|return|self|static|switch|throw|trait|try|unset|use|var|while|xor|yield|__halt_compiler)\b/i,lookbehind:!0}],"argument-name":{pattern:/([(,]\s*)\b[a-z_]\w*(?=\s*:(?!:))/i,lookbehind:!0},"class-name":[{pattern:/(\b(?:extends|implements|instanceof|new(?!\s+self|\s+static))\s+|\bcatch\s*\()\b[a-z_]\w*(?!\\)\b/i,greedy:!0,lookbehind:!0},{pattern:/(\|\s*)\b[a-z_]\w*(?!\\)\b/i,greedy:!0,lookbehind:!0},{pattern:/\b[a-z_]\w*(?!\\)\b(?=\s*\|)/i,greedy:!0},{pattern:/(\|\s*)(?:\\?\b[a-z_]\w*)+\b/i,alias:"class-name-fully-qualified",greedy:!0,lookbehind:!0,inside:{punctuation:/\\/}},{pattern:/(?:\\?\b[a-z_]\w*)+\b(?=\s*\|)/i,alias:"class-name-fully-qualified",greedy:!0,inside:{punctuation:/\\/}},{pattern:/(\b(?:extends|implements|instanceof|new(?!\s+self\b|\s+static\b))\s+|\bcatch\s*\()(?:\\?\b[a-z_]\w*)+\b(?!\\)/i,alias:"class-name-fully-qualified",greedy:!0,lookbehind:!0,inside:{punctuation:/\\/}},{pattern:/\b[a-z_]\w*(?=\s*\$)/i,alias:"type-declaration",greedy:!0},{pattern:/(?:\\?\b[a-z_]\w*)+(?=\s*\$)/i,alias:["class-name-fully-qualified","type-declaration"],greedy:!0,inside:{punctuation:/\\/}},{pattern:/\b[a-z_]\w*(?=\s*::)/i,alias:"static-context",greedy:!0},{pattern:/(?:\\?\b[a-z_]\w*)+(?=\s*::)/i,alias:["class-name-fully-qualified","static-context"],greedy:!0,inside:{punctuation:/\\/}},{pattern:/([(,?]\s*)[a-z_]\w*(?=\s*\$)/i,alias:"type-hint",greedy:!0,lookbehind:!0},{pattern:/([(,?]\s*)(?:\\?\b[a-z_]\w*)+(?=\s*\$)/i,alias:["class-name-fully-qualified","type-hint"],greedy:!0,lookbehind:!0,inside:{punctuation:/\\/}},{pattern:/(\)\s*:\s*(?:\?\s*)?)\b[a-z_]\w*(?!\\)\b/i,alias:"return-type",greedy:!0,lookbehind:!0},{pattern:/(\)\s*:\s*(?:\?\s*)?)(?:\\?\b[a-z_]\w*)+\b(?!\\)/i,alias:["class-name-fully-qualified","return-type"],greedy:!0,lookbehind:!0,inside:{punctuation:/\\/}}],constant:a,function:{pattern:/(^|[^\\\w])\\?[a-z_](?:[\w\\]*\w)?(?=\s*\()/i,lookbehind:!0,inside:{punctuation:/\\/}},property:{pattern:/(->\s*)\w+/,lookbehind:!0},number:n,operator:r,punctuation:i};var s={pattern:/\{\$(?:\{(?:\{[^{}]+\}|[^{}]+)\}|[^{}])+\}|(^|[^\\{])\$+(?:\w+(?:\[[^\r\n\[\]]+\]|->\w+)?)/,lookbehind:!0,inside:e.languages.php},l=[{pattern:/<<<'([^']+)'[\r\n](?:.*[\r\n])*?\1;/,alias:"nowdoc-string",greedy:!0,inside:{delimiter:{pattern:/^<<<'[^']+'|[a-z_]\w*;$/i,alias:"symbol",inside:{punctuation:/^<<<'?|[';]$/}}}},{pattern:/<<<(?:"([^"]+)"[\r\n](?:.*[\r\n])*?\1;|([a-z_]\w*)[\r\n](?:.*[\r\n])*?\2;)/i,alias:"heredoc-string",greedy:!0,inside:{delimiter:{pattern:/^<<<(?:"[^"]+"|[a-z_]\w*)|[a-z_]\w*;$/i,alias:"symbol",inside:{punctuation:/^<<<"?|[";]$/}},interpolation:s}},{pattern:/`(?:\\[\s\S]|[^\\`])*`/,alias:"backtick-quoted-string",greedy:!0},{pattern:/'(?:\\[\s\S]|[^\\'])*'/,alias:"single-quoted-string",greedy:!0},{pattern:/"(?:\\[\s\S]|[^\\"])*"/,alias:"double-quoted-string",greedy:!0,inside:{interpolation:s}}];e.languages.insertBefore("php","variable",{string:l,attribute:{pattern:/#\[(?:[^"'\/#]|\/(?![*/])|\/\/.*$|#(?!\[).*$|\/\*(?:[^*]|\*(?!\/))*\*\/|"(?:\\[\s\S]|[^\\"])*"|'(?:\\[\s\S]|[^\\'])*')+\](?=\s*[a-z$#])/im,greedy:!0,inside:{"attribute-content":{pattern:/^(#\[)[\s\S]+(?=\]$)/,lookbehind:!0,inside:{comment:t,string:l,"attribute-class-name":[{pattern:/([^:]|^)\b[a-z_]\w*(?!\\)\b/i,alias:"class-name",greedy:!0,lookbehind:!0},{pattern:/([^:]|^)(?:\\?\b[a-z_]\w*)+/i,alias:["class-name","class-name-fully-qualified"],greedy:!0,lookbehind:!0,inside:{punctuation:/\\/}}],constant:a,number:n,operator:r,punctuation:i}},delimiter:{pattern:/^#\[|\]$/,alias:"punctuation"}}}}),e.hooks.add("before-tokenize",(function(t){/<\?/.test(t.code)&&e.languages["markup-templating"].buildPlaceholders(t,"php",/<\?(?:[^"'/#]|\/(?![*/])|("|')(?:\\[\s\S]|(?!\1)[^\\])*\1|(?:\/\/|#(?!\[))(?:[^?\n\r]|\?(?!>))*(?=$|\?>|[\r\n])|#\[|\/\*(?:[^*]|\*(?!\/))*(?:\*\/|$))*?(?:\?>|$)/g)})),e.hooks.add("after-tokenize",(function(t){e.languages["markup-templating"].tokenizePlaceholders(t,"php")}))}(t),function(e){var t=/[*&][^\s[\]{},]+/,a=/!(?:<[\w\-%#;/?:@&=+$,.!~*'()[\]]+>|(?:[a-zA-Z\d-]*!)?[\w\-%#;/?:@&=+$.~*'()]+)?/,n="(?:"+a.source+"(?:[ \t]+"+t.source+")?|"+t.source+"(?:[ \t]+"+a.source+")?)",r="(?:[^\\s\\x00-\\x08\\x0e-\\x1f!\"#%&'*,\\-:>?@[\\]`{|}\\x7f-\\x84\\x86-\\x9f\\ud800-\\udfff\\ufffe\\uffff]|[?:-])(?:[ \t]*(?:(?![#:])|:))*".replace(//g,(function(){return"[^\\s\\x00-\\x08\\x0e-\\x1f,[\\]{}\\x7f-\\x84\\x86-\\x9f\\ud800-\\udfff\\ufffe\\uffff]"})),i="\"(?:[^\"\\\\\r\n]|\\\\.)*\"|'(?:[^'\\\\\r\n]|\\\\.)*'";function s(e,t){t=(t||"").replace(/m/g,"")+"m";var a="([:\\-,[{]\\s*(?:\\s<>[ \t]+)?)(?:<>)(?=[ \t]*(?:$|,|\\]|\\}|(?:[\r\n]\\s*)?#))".replace(/<>/g,(function(){return n})).replace(/<>/g,(function(){return e}));return RegExp(a,t)}e.languages.yaml={scalar:{pattern:RegExp("([\\-:]\\s*(?:\\s<>[ \t]+)?[|>])[ \t]*(?:((?:\r?\n|\r)[ \t]+)\\S[^\r\n]*(?:\\2[^\r\n]+)*)".replace(/<>/g,(function(){return n}))),lookbehind:!0,alias:"string"},comment:/#.*/,key:{pattern:RegExp("((?:^|[:\\-,[{\r\n?])[ \t]*(?:<>[ \t]+)?)<>(?=\\s*:\\s)".replace(/<>/g,(function(){return n})).replace(/<>/g,(function(){return"(?:"+r+"|"+i+")"}))),lookbehind:!0,greedy:!0,alias:"atrule"},directive:{pattern:/(^[ \t]*)%.+/m,lookbehind:!0,alias:"important"},datetime:{pattern:s("\\d{4}-\\d\\d?-\\d\\d?(?:[tT]|[ \t]+)\\d\\d?:\\d{2}:\\d{2}(?:\\.\\d*)?(?:[ \t]*(?:Z|[-+]\\d\\d?(?::\\d{2})?))?|\\d{4}-\\d{2}-\\d{2}|\\d\\d?:\\d{2}(?::\\d{2}(?:\\.\\d*)?)?"),lookbehind:!0,alias:"number"},boolean:{pattern:s("false|true","i"),lookbehind:!0,alias:"important"},null:{pattern:s("null|~","i"),lookbehind:!0,alias:"important"},string:{pattern:s(i),lookbehind:!0,greedy:!0},number:{pattern:s("[+-]?(?:0x[\\da-f]+|0o[0-7]+|(?:\\d+(?:\\.\\d*)?|\\.\\d+)(?:e[+-]?\\d+)?|\\.inf|\\.nan)","i"),lookbehind:!0},tag:a,important:t,punctuation:/---|[:[\]{}\-,|>?]|\.\.\./},e.languages.yml=e.languages.yaml}(t),t.manual=!0;const a=e({mounted(){t.highlightAll(this.$el)},updated(){t.highlightAll(this.$el)},render(){return this.$scopedSlots.default({})}},null,null).exports;export{a as default}; diff --git a/kirby/panel/dist/js/IndexView.min.js b/kirby/panel/dist/js/IndexView.min.js new file mode 100644 index 0000000..e840b24 --- /dev/null +++ b/kirby/panel/dist/js/IndexView.min.js @@ -0,0 +1 @@ +import{n as e}from"./index.min.js";import"./vendor.min.js";const t=e({props:{categories:Array,info:String,tab:String}},(function(){var e=this,t=e._self._c;return t("k-panel-inside",{staticClass:"k-lab-index-view"},[t("k-header",[e._v("Lab")]),t("k-tabs",{attrs:{tab:e.tab,tabs:[{name:"examples",label:"Examples",link:"/lab"},{name:"docs",label:"Docs",link:"/lab/docs"}]}}),e.info?t("k-box",{attrs:{icon:"question",theme:"info",text:e.info,html:!0}}):e._e(),e._l(e.categories,(function(e){return t("k-section",{key:e.name,attrs:{headline:e.name}},[t("k-collection",{attrs:{items:e.examples,empty:{icon:e.icon,text:"Add examples to "+e.path}}})],1)}))],2)}),[]).exports;export{t as default}; diff --git a/kirby/panel/dist/js/PlaygroundView.min.js b/kirby/panel/dist/js/PlaygroundView.min.js new file mode 100644 index 0000000..50d4571 --- /dev/null +++ b/kirby/panel/dist/js/PlaygroundView.min.js @@ -0,0 +1 @@ +import{D as t}from"./Docs.min.js";import{n as e}from"./index.min.js";import"./vendor.min.js";const a=e({props:{docs:Object},emits:["cancel"],computed:{options(){const t=[{icon:"expand",link:"lab/docs/"+this.docs.component}];return this.docs.github&&t.unshift({icon:"github",link:this.docs.github,target:"_blank"}),t}}},(function(){var t=this,e=t._self._c;return e("k-drawer",t._b({ref:"drawer",staticClass:"k-form-drawer",attrs:{options:t.options},on:{submit:function(e){return t.$emit("cancel")}}},"k-drawer",t.$attrs,!1),[e("k-lab-docs",t._b({},"k-lab-docs",t.docs,!1))],1)}),[]).exports;const o=e({props:{code:{type:Boolean,default:!0},label:String,flex:Boolean},data:()=>({mode:"preview"}),computed:{component(){return window.UiExamples[this.label]}}},(function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-lab-example",attrs:{"data-flex":t.flex,tabindex:"0"}},[e("header",{staticClass:"k-lab-example-header"},[e("h3",{staticClass:"k-lab-example-label"},[t._v(t._s(t.label))]),t.code?e("k-button-group",{staticClass:"k-lab-example-inspector",attrs:{layout:"collapsed"}},[e("k-button",{attrs:{theme:"preview"===t.mode?"info":null,icon:"preview",size:"xs",title:"Preview"},on:{click:function(e){t.mode="preview"}}}),e("k-button",{attrs:{theme:"inspect"===t.mode?"info":null,icon:"code",size:"xs",title:"Vue code"},on:{click:function(e){t.mode="inspect"}}})],1):t._e()],1),"preview"===t.mode?e("div",{staticClass:"k-lab-example-canvas"},[t._t("default")],2):t._e(),"inspect"===t.mode?e("div",{staticClass:"k-lab-example-code"},[e("k-code",{attrs:{language:"html"}},[t._v(t._s(t.component))])],1):t._e()])}),[]).exports;const s=e({},(function(){return(0,this._self._c)("div",{staticClass:"k-lab-examples"},[this._t("default")],2)}),[]).exports;const n=e({methods:{submit(t){const e=Object.fromEntries(new FormData(t));this.$panel.dialog.open({component:"k-lab-output-dialog",props:{code:JSON.stringify(e,null,2)}})}}},(function(){var t=this,e=t._self._c;return e("form",{staticClass:"k-lab-form",on:{submit:function(e){return e.preventDefault(),t.submit(e.target)}}},[t._t("default"),e("footer",[e("k-button",{attrs:{type:"submit",icon:"check",theme:"positive",variant:"filled"}},[t._v(" Submit ")])],1)],2)}),[]).exports;const i=e({props:{code:String,language:{default:"js",type:String}},emits:["cancel"]},(function(){var t=this,e=t._self._c;return e("k-dialog",t._b({attrs:{size:"large","cancel-button":!1,"submit-button":!1},on:{cancel:function(e){return t.$emit("cancel")}}},"k-dialog",t.$attrs,!1),[e("k-code",{attrs:{language:t.language}},[t._v(t._s(t.code))])],1)}),[]).exports;const l=e({},(function(){var t=this._self._c;return t("div",{staticClass:"k-table"},[t("table",[t("tbody",[t("tr",[t("td",{staticClass:"k-table-cell",attrs:{"data-mobile":"true"}},[this._t("default")],2)])])])])}),[]).exports;Vue.component("k-lab-docs",t),Vue.component("k-lab-docs-drawer",a),Vue.component("k-lab-example",o),Vue.component("k-lab-examples",s),Vue.component("k-lab-form",n),Vue.component("k-lab-output-dialog",i),Vue.component("k-lab-table-cell",l);const r=e({props:{compiler:Boolean,docs:String,examples:[Object,Array],file:String,github:String,props:[Object,Array],styles:String,tab:String,tabs:{type:Array,default:()=>[]},template:String,title:String},data:()=>({component:null}),watch:{tab:{handler(){this.createComponent()},immediate:!0}},mounted(){this.$panel.view.path.replace(/lab\//,"")},methods:{async createComponent(){if(!this.file)return;const{default:t}=await import(this.$panel.url(this.file)+"?cache="+Date.now());t.template=this.template,this.component={...t},window.UiExamples=this.examples},openDocs(){this.$panel.drawer.open(`lab/docs/${this.docs}`)},async reloadComponent(){await this.$panel.view.refresh(),this.createComponent()},reloadDocs(){this.$panel.drawer.isOpen&&this.$panel.drawer.refresh()}}},(function(){var t=this,e=t._self._c;return e("k-panel-inside",{staticClass:"k-lab-playground-view",attrs:{"data-has-tabs":t.tabs.length>1}},[e("k-header",{scopedSlots:t._u([{key:"buttons",fn:function(){return[t.docs||t.github?e("k-button-group",[t.docs?e("k-button",{attrs:{text:t.docs,icon:"book",size:"sm",variant:"filled"},on:{click:t.openDocs}}):t._e(),t.github?e("k-button",{attrs:{link:t.github,icon:"github",size:"sm",target:"_blank",variant:"filled"}}):t._e()],1):t._e()]},proxy:!0}])},[t._v(" "+t._s(t.title)+" ")]),e("k-tabs",{attrs:{tab:t.tab,tabs:t.tabs}}),!1===t.compiler?e("k-box",{attrs:{theme:"info"}},[t._v(" The Vue template compiler must be enabled to show lab examples ")]):[t.component?e(t.component,t._b({tag:"component"},"component",t.props,!1)):t._e(),t.styles?e("style",{tag:"component",domProps:{innerHTML:t._s(t.styles)}}):t._e()]],2)}),[]).exports;export{r as default}; diff --git a/kirby/panel/dist/js/container-query-polyfill.modern.min.js b/kirby/panel/dist/js/container-query-polyfill.modern.min.js new file mode 100644 index 0000000..7053dd7 --- /dev/null +++ b/kirby/panel/dist/js/container-query-polyfill.modern.min.js @@ -0,0 +1 @@ +function e(){return e=Object.assign?Object.assign.bind():function(e){for(var t=1;t0?{type:2,value:n/r}:{type:1};case 6:return null!=n&&null!=r?{type:4,value:r>=n?"portrait":"landscape"}:{type:1}}}function n(e,t){switch(e.type){case 1:case 2:case 3:case 4:return i(e,t);case 5:{const n=t.sizeFeatures.get(e.feature);return null==n?{type:1}:n}case 6:return e.value}}function r(e){return{type:5,value:e}}function u(e,t,n){return r(function(e,t,n){switch(n){case 1:return e===t;case 2:return e>t;case 3:return e>=t;case 4:return ee*t))}return null}function c(e,t){switch(e.type){case 2:return 0===e.value?0:null;case 3:return l(e,t)}return null}function i(e,t){switch(e.type){case 4:return function(e,t){const o=n(e.left,t),s=n(e.right,t),l=e.operator;if(4===o.type&&4===s.type||5===o.type&&5===s.type)return i=o,a=s,1===l?r(i.value===a.value):{type:1};var i,a;if(3===o.type||3===s.type){const e=c(o,t),n=c(s,t);if(null!=e&&null!=n)return u(e,n,l)}else if(2===o.type&&2===s.type)return u(o.value,s.value,l);return{type:1}}(e,t);case 2:return function(e,t){const n=i(e.left,t);return 5!==n.type||!0!==n.value?n:i(e.right,t)}(e,t);case 3:return function(e,t){const n=i(e.left,t);return 5===n.type&&!0===n.value?n:i(e.right,t)}(e,t);case 1:{const n=i(e.value,t);return 5===n.type?{type:5,value:!n.value}:{type:1}}case 5:return a(n(e,t));case 6:return a(e.value)}}function a(e){switch(e.type){case 5:return e;case 2:case 3:return{type:5,value:e.value>0}}return{type:1}}const f=Array.from({length:4},(()=>Math.floor(256*Math.random()).toString(16))).join(""),p=S("container"),y=S("container-type"),h=S("container-name"),v=`data-cqs-${f}`,d=`data-cqc-${f}`,m=S("cqw"),w=S("cqh"),g=S("cqi"),b=S("cqb");function S(e){return`--cq-${e}-${f}`}const x=Symbol();function q(e,t){const n={value:t,errorIndices:[],index:-1,at(r){const u=n.index+r;return u>=e.length?t:e[u]},consume:e=>(n.index+=e,n.value=n.at(0),n.value),reconsume(){n.index-=1},error(){n.errorIndices.push(n.index)}};return n}function C(e){return q(e,{type:0})}function*$(e){const t=[];let n=!1;for(const b of e){const e=b.codePointAt(0);n&&10!==e&&(n=!1,t.push(10)),0===e||e>=55296&&e<=57343?t.push(65533):13===e?n=!0:t.push(e)}const r=q(t,-1),{at:u,consume:o,error:s,reconsume:l}=r;function c(){return String.fromCodePoint(r.value)}function i(){return{type:13,value:c()}}function a(){for(;z(u(1));)o(1)}function f(){for(;-1!==r.value;)if(o(1),42===u(0)&&47===u(1))return void o(1);s()}function p(){const[e,t]=function(){let e=0,t="",n=u(1);for(43!==n&&45!==n||(o(1),t+=c());k(u(1));)o(1),t+=c();if(46===u(1)&&k(u(2)))for(e=1,o(1),t+=c();k(u(1));)o(1),t+=c();if(n=u(1),69===n||101===n){const n=u(2);if(k(n))for(e=1,o(1),t+=c();k(u(1));)o(1),t+=c();else if((45===n||43===n)&&k(u(3)))for(e=1,o(1),t+=c(),o(1),t+=c();k(u(1));)o(1),t+=c()}return[t,e]}(),n=u(1);return d(n,u(1),u(2))?{type:15,value:e,flag:t,unit:w()}:37===n?(o(1),{type:16,value:e}):{type:17,value:e,flag:t}}function y(){const e=w();let t=u(1);if("url"===e.toLowerCase()&&40===t){for(o(1);z(u(1))&&z(u(2));)o(1);t=u(1);const n=u(2);return 34===t||39===t?{type:23,value:e}:!z(t)||34!==n&&39!==n?function(){let e="";for(a();;){const n=o(1);if(41===n)return{type:20,value:e};if(-1===n)return s(),{type:20,value:e};if(z(n)){a();const t=u(1);return 41===t||-1===t?(o(1),-1===n&&s(),{type:20,value:e}):(g(),{type:21})}if(34===n||39===n||40===n||(t=n)>=0&&t<=8||11===t||t>=14&&t<=31||127===t)return s(),g(),{type:21};if(92===n){if(!j(n,u(1)))return s(),{type:21};e+=v()}else e+=c()}var t}():{type:23,value:e}}return 40===t?(o(1),{type:23,value:e}):{type:24,value:e}}function h(e){let t="";for(;;){const n=o(1);if(-1===n||n===e)return-1===n&&s(),{type:2,value:t};if(E(n))return s(),l(),{type:3};if(92===n){const e=u(1);if(-1===e)continue;E(e)?o(1):t+=v()}else t+=c()}}function v(){const e=o(1);if(A(e)){const t=[e];for(let e=0;e<5;e++){const e=u(1);if(!A(e))break;t.push(e),o(1)}z(u(1))&&o(1);let n=parseInt(String.fromCodePoint(...t),16);return(0===n||n>=55296&&n<=57343||n>1114111)&&(n=65533),String.fromCodePoint(n)}return-1===e?(s(),String.fromCodePoint(65533)):c()}function d(e,t,n){return 45===e?L(t)||45===t||j(t,n):!!L(e)}function m(e,t,n){return 43===e||45===e?k(t)||46===t&&k(n):!(46!==e||!k(t))||!!k(e)}function w(){let e="";for(;;){const t=o(1);if(M(t))e+=c();else{if(!j(t,u(1)))return l(),e;e+=v()}}}function g(){for(;;){const e=o(1);if(-1===e)return;j(e,u(1))&&v()}}for(;;){const e=o(1);if(47===e&&42===u(1))o(2),f();else if(z(e))a(),yield{type:1};else if(34===e)yield h(e);else if(35===e){const e=u(1);M(e)||j(e,u(2))?yield{type:14,flag:d(u(1),u(2),u(3))?1:0,value:w()}:yield i()}else if(39===e)yield h(e);else if(40===e)yield{type:4};else if(41===e)yield{type:5};else if(43===e)m(e,u(1),u(2))?(l(),yield p()):yield i();else if(44===e)yield{type:6};else if(45===e){const t=u(1),n=u(2);m(e,t,n)?(l(),yield p()):45===t&&62===n?(o(2),yield{type:19}):d(e,t,n)?(l(),yield y()):yield i()}else if(46===e)m(e,u(1),u(2))?(l(),yield p()):yield i();else if(58===e)yield{type:7};else if(59===e)yield{type:8};else if(60===e)33===u(1)&&45===u(2)&&45===u(3)?yield{type:18}:yield i();else if(64===e)if(d(u(1),u(2),u(3))){const e=w();yield{type:22,value:e}}else yield i();else if(91===e)yield{type:9};else if(92===e)j(e,u(1))?(l(),yield y()):(s(),yield i());else if(93===e)yield{type:10};else if(123===e)yield{type:11};else if(125===e)yield{type:12};else if(k(e))l(),yield p();else if(L(e))l(),yield y();else{if(-1===e)return yield{type:0},r.errorIndices;yield{type:13,value:c()}}}}function k(e){return e>=48&&e<=57}function A(e){return k(e)||e>=65&&e<=70||e>=97&&e<=102}function E(e){return 10===e||13===e||12===e}function z(e){return E(e)||9===e||32===e}function L(e){return e>=65&&e<=90||e>=97&&e<=122||e>=128||95===e}function j(e,t){return 92===e&&!E(t)}function M(e){return L(e)||k(e)||45===e}const T={11:12,9:10,4:5};function P(t,n){const r=function(e,t){const n=[];for(;;)switch(e.consume(1).type){case 1:break;case 0:return{type:3,value:n};case 18:case 19:if(!1!==t){e.reconsume();const t=U(e);t!==x&&n.push(t)}break;case 22:e.reconsume(),n.push(F(e));break;default:{e.reconsume();const t=U(e);t!==x&&n.push(t);break}}}(C(t),!0===n);return e({},r,{value:r.value.map((t=>{return 26===t.type?0===(n=t).value.value.type?e({},n,{value:e({},n.value,{value:(r=n.value.value.value,function(e){const t=[],n=[];for(;;){const r=e.consume(1);switch(r.type){case 1:case 8:break;case 0:return{type:1,value:[...n,...t]};case 22:e.reconsume(),t.push(F(e));break;case 24:{const t=[r];let u=e.at(1);for(;8!==u.type&&0!==u.type;)t.push(I(e)),u=e.at(1);const o=R(C(t));o!==x&&n.push(o);break}case 13:if("&"===r.value){e.reconsume();const n=U(e);n!==x&&t.push(n);break}default:{e.error(),e.reconsume();let t=e.at(1);for(;8!==t.type&&0!==t.type;)I(e),t=e.at(1);break}}}}(C(r)))})}):n:t;var n,r}))})}function N(e){const t=C(e),n=[];for(;;){if(0===t.consume(1).type)return n;t.reconsume(),n.push(I(t))}}function O(e){for(;1===e.at(1).type;)e.consume(1)}function F(e){let t=e.consume(1);if(22!==t.type)throw new Error(`Unexpected type ${t.type}`);const n=t.value,r=[];for(;;)switch(t=e.consume(1),t.type){case 8:return{type:25,name:n,prelude:r,value:null};case 0:return e.error(),{type:25,name:n,prelude:r,value:null};case 11:return{type:25,name:n,prelude:r,value:Q(e)};case 28:if(11===t.source.type)return{type:25,name:n,prelude:r,value:t};default:e.reconsume(),r.push(I(e))}}function U(e){let t=e.value;const n=[];for(;;)switch(t=e.consume(1),t.type){case 0:return e.error(),x;case 11:return{type:26,prelude:n,value:Q(e)};case 28:if(11===t.source.type)return{type:26,prelude:n,value:t};default:e.reconsume(),n.push(I(e))}}function R(e){const t=e.consume(1);if(24!==t.type)throw new Error(`Unexpected type ${t.type}`);const n=t.value,r=[];let u=!1;if(O(e),7!==e.at(1).type)return e.error(),x;for(e.consume(1),O(e);0!==e.at(1).type;)r.push(I(e));const o=r[r.length-2],s=r[r.length-1];return o&&13===o.type&&"!"===o.value&&24===s.type&&"important"===s.value.toLowerCase()&&(u=!0,r.splice(r.length-2)),{type:29,name:n,value:r,important:u}}function I(e){const t=e.consume(1);switch(t.type){case 11:case 9:case 4:return Q(e);case 23:return function(e){let t=e.value;if(23!==t.type)throw new Error(`Unexpected type ${t.type}`);const n=t.value,r=[];for(;;)switch(t=e.consume(1),t.type){case 5:return{type:27,name:n,value:r};case 0:return e.error(),{type:27,name:n,value:r};default:e.reconsume(),r.push(I(e))}}(e);default:return t}}function Q(e){let t=e.value;const n=t,r=T[n.type];if(!r)throw new Error(`Unexpected type ${t.type}`);const u=[];for(;;)switch(t=e.consume(1),t.type){case r:return{type:28,source:n,value:{type:0,value:u}};case 0:return e.error(),{type:28,source:n,value:{type:0,value:u}};default:e.reconsume(),u.push(I(e))}}function H(e){return O(e),0===e.at(1).type}const V={11:["{","}"],9:["[","]"],4:["(",")"]};function D(e,t){switch(e.type){case 25:return`@${CSS.escape(e.name)} ${e.prelude.map((e=>D(e))).join("")}${e.value?D(e.value):";"}`;case 26:return`${e.prelude.map((e=>D(e))).join("")}${D(e.value)}`;case 28:{const[t,n]=V[e.source.type];return`${t}${W(e.value)}${n}`}case 27:return`${CSS.escape(e.name)}(${e.value.map((e=>D(e))).join("")})`;case 29:return`${CSS.escape(e.name)}:${e.value.map((e=>D(e))).join("")}${e.important?" !important":""}`;case 1:return" ";case 8:return";";case 7:return":";case 14:return"#"+CSS.escape(e.value);case 24:return CSS.escape(e.value);case 15:return e.value+CSS.escape(e.unit);case 13:case 17:return e.value;case 2:return`"${CSS.escape(e.value)}"`;case 6:return",";case 20:return"url("+CSS.escape(e.value)+")";case 22:return"@"+CSS.escape(e.value);case 16:return e.value+"%";default:throw new Error(`Unsupported token ${e.type}`)}}function W(e,t){return e.value.map((t=>{let n=D(t);return 29===t.type&&0!==e.type&&(n+=";"),n})).join("")}function _(e){return D(e)}function B(e){const t=e.at(1);return 13===t.type&&"="===t.value&&(e.consume(1),!0)}function G(e,t){const n=[];for(;;){const r=e.at(1);if(0===r.type||t&&7===r.type||13===r.type&&(">"===r.value||"<"===r.value||"="===r.value))break;n.push(e.consume(1))}return n}function Y(e){O(e);const t=e.consume(1);return 13!==t.type?x:">"===t.value?B(e)?3:2:"<"===t.value?B(e)?5:4:"="===t.value?1:x}function J(e){return 4===e||5===e}function K(e){return 2===e||3===e}function X(e,t,n){const r=function(e){O(e);const t=e.consume(1);return O(e),24!==t.type||0!==e.at(1).type?x:t.value}(C(e));if(r===x)return x;let u=r.toLowerCase();return u=n?n(u):u,t.has(u)?u:x}function Z(e){return{type:13,value:e}}function ee(e,t){return{type:29,name:e,value:t,important:!1}}function te(e){return{type:24,value:e}}function ne(e,t){return{type:27,name:e,value:t}}function re(e){return ne("var",[te(e)])}function ue(e,t){O(e);let n=!1,r=e.at(1);if(24===r.type){if("not"!==r.value.toLowerCase())return x;e.consume(1),O(e),n=!0}let u=function(e){const t=e.consume(1);switch(t.type){case 28:{if(4!==t.source.type)return x;const e=ue(C(t.value.value),null);return e!==x?e:{type:4,value:t}}case 27:return{type:4,value:t};default:return x}}(e);if(u===x)return x;u=n?{type:1,value:u}:u,O(e),r=e.at(1);const o=24===r.type?r.value.toLowerCase():null;if(null!==o){if(e.consume(1),O(e),"and"!==o&&"or"!==o||null!==t&&o!==t)return x;const n=ue(e,o);return n===x?x:{type:"and"===o?2:3,left:u,right:n}}return H(e)?u:x}function oe(e){return ue(e,null)}function se(e){switch(e.type){case 1:return[te("not"),{type:1},...se(e.value)];case 2:case 3:return[...se(e.left),{type:1},te(2===e.type?"and":"or"),{type:1},...se(e.right)];case 4:return[e.value]}}const le={width:1,height:2,"inline-size":3,"block-size":4,"aspect-ratio":5,orientation:6},ce=new Set(Object.keys(le)),ie=new Set(["none","and","not","or","normal","auto"]),ae=new Set(["initial","inherit","revert","revert-layer","unset"]),fe=new Set(["size","inline-size"]);function pe(e,t,n,r){const u=n();if(u===x)return x;let o=[u,null];O(e);const s=e.at(1);if(13===s.type){if(s.value!==t)return x;e.consume(1),O(e);const n=r();O(e),n!==x&&(o=[u,n])}return H(e)?o:x}function ye(e){const t=e.consume(1);return 17===t.type?parseInt(t.value):x}function he(e){const t=C(e);O(t);const n=t.consume(1);let r=x;switch(n.type){case 17:t.reconsume(),r=function(e){const t=pe(e,"/",(()=>ye(e)),(()=>ye(e)));return t===x?x:{type:2,value:t[0]/(null!==t[1]?t[1]:1)}}(t);break;case 15:r={type:3,value:parseInt(n.value),unit:n.unit.toLowerCase()};break;case 24:{const e=n.value.toLowerCase();switch(e){case"landscape":case"portrait":r={type:4,value:e}}}}return r===x?x:H(t)?{type:6,value:r}:x}function ve(e){return!we(e=e.toLowerCase())&&!ie.has(e)}function de(e,t){const n=[];for(;;){O(e);const r=e.at(1);if(24!==r.type||!t(r.value))return n;e.consume(1),n.push(r.value)}}function me(e){const t=[];for(;;){O(e);const n=e.at(1);if(24!==n.type)break;const r=n.value;if(!ve(r))break;e.consume(1),t.push(r)}return t}function we(e){return ae.has(e)}function ge(e){return e.map((e=>"cq-"+e))}function be(e){const t=de(e,(e=>we(e)));return 1===t.length?ge(t):x}function Se(e,t){const n=de(e,(e=>"none"===e));if(1===n.length)return ge(n);if(0!==n.length)return x;if(t){const t=be(e);if(t!==x)return t}const r=me(e);return r.length>0&&(!t||H(e))?r:x}function xe(e,t){if(t){const t=be(e);if(t!==x)return t}return function(e){const t=de(e,(e=>"normal"===e));if(1===t.length)return ge(t);if(0!==t.length)return x;const n=de(e,(e=>fe.has(e)));return n.length>0&&H(e)?n:x}(e)}function qe(e){const t=C(e),n=be(t);if(n!==x)return[n,n];const r=pe(t,"/",(()=>Se(t,!1)),(()=>xe(t,!1)));return r!==x&&H(t)?[r[0],r[1]||[]]:x}function Ce(e){const t=C(e),n=me(t);if(!n||n.length>1)return x;const r=oe(t);if(r===x)return x;const u={features:new Set},o=$e(r,u);return H(t)?{name:n.length>0?n[0]:null,condition:o,features:u.features}:x}function $e(e,t){switch(e.type){case 1:return{type:1,value:$e(e.value,t)};case 2:case 3:return{type:2===e.type?2:3,left:$e(e.left,t),right:$e(e.right,t)};case 4:if(28===e.value.type){const n=function(e,t){const n=function(e,t){const n=G(e,!0),r=e.at(1);if(0===r.type){const e=X(n,t);return e!==x&&t.has(e)?{type:1,feature:e}:x}if(7===r.type){e.consume(1);const r=G(e,!1);let u=1;const o=X(n,t,(e=>e.startsWith("min-")?(u=3,e.substring(4)):e.startsWith("max-")?(u=5,e.substring(4)):e));return o!==x?{type:2,feature:o,bounds:[null,[u,r]]}:x}const u=Y(e);if(u===x)return x;const o=G(e,!1);if(0===e.at(1).type){const e=X(n,t);if(e!==x)return{type:2,feature:e,bounds:[null,[u,o]]};const r=X(o,t);return r!==x?{type:2,feature:r,bounds:[[u,n],null]}:x}const s=Y(e);if(s===x||!(K(u)&&K(s)||J(u)&&J(s)))return x;const l=G(e,!1),c=X(o,t);return c!==x?{type:2,feature:c,bounds:[[u,n],[s,l]]}:x}(e,ce);if(n===x)return x;const r=le[n.feature];if(null==r)return x;if(t.features.add(r),1===n.type)return{type:5,feature:r};{const e={type:5,feature:r};let t=x;if(null!==n.bounds[0]){const r=he(n.bounds[0][1]);if(r===x)return x;t={type:4,operator:n.bounds[0][0],left:r,right:e}}if(null!==n.bounds[1]){const r=he(n.bounds[1][1]);if(r===x)return x;const u={type:4,operator:n.bounds[1][0],left:e,right:r};t=t!==x?{type:2,left:t,right:u}:u}return t}}(C(e.value.value.value),t);if(n!==x)return n}return{type:6,value:{type:1}}}}let ke=0;const Ae={cqw:m,cqh:w,cqi:g,cqb:b},Ee=CSS.supports("selector(:where(div))"),ze=":not(.container-query-polyfill)";N(Array.from($(ze)));const Le=document.createElement("div"),je=new Set(["before","after","first-line","first-letter"]);function Me(e,t){return ne("calc",[{type:17,flag:e.flag,value:e.value},Z("*"),t])}function Te(t){return t.map((t=>{switch(t.type){case 15:return function(e){const t=e.unit,n=Ae[t];return null!=n?Me(e,re(n)):"cqmin"===t||"cqmax"===t?Me(e,ne(e.unit.slice(2),[re(g),{type:6},re(b)])):e}(t);case 27:return e({},t,{value:Te(t.value)})}return t}))}function Pe(t){switch(t.name){case"container":return qe(t.value)?e({},t,{name:p}):t;case"container-name":return Se(C(t.value),!0)?e({},t,{name:h}):t;case"container-type":return null!=xe(C(t.value),!0)?e({},t,{name:y}):t}return e({},t,{value:Te(t.value)})}function Ne(t,n){return e({},t,{value:t.value.map((t=>{switch(t.type){case 25:return Qe(t,n);case 26:return r=t,(u=n).transformStyleRule(e({},r,{value:Ue(r.value,u)}));default:return t}var r,u}))})}function Oe(e){return 0===e.type||6===e.type}function Fe(e){for(let t=e.length-1;t>=0;t--)if(1!==e[t].type)return e.slice(0,t+1);return e}function Ue(t,n){return function(t,n){const r=[];let u=null,o=null;for(const e of t.value.value)switch(e.type){case 25:{const t=n?n(e):e;t&&r.push(t)}break;case 29:{const t=Pe(e);switch(t.name){case p:{const t=qe(e.value);t!==x&&(u=t[0],o=t[1]);break}case h:{const t=Se(C(e.value),!0);t!==x&&(u=t);break}case y:{const t=xe(C(e.value),!0);t!==x&&(o=t);break}default:r.push(t)}}}return u&&u.length>0&&r.push(ee(h,[te(u.join(" "))])),o&&o.length>0&&r.push(ee(y,[te(o.join(" "))])),e({},t,{value:{type:2,value:r}})}(t,(e=>Qe(e,n)))}function Re(t){if(1===t.type)return e({},t,{value:Re(t.value)});if(2===t.type||3===t.type)return e({},t,{left:Re(t.left),right:Re(t.right)});if(4===t.type&&28===t.value.type){const n=function(e){const t=C(e);return O(t),24!==t.at(1).type?x:R(t)||x}(t.value.value.value);if(n!==x)return e({},t,{value:e({},t.value,{value:{type:0,value:[Pe(n)]}})})}return t}function Ie(t,n){let r=oe(C(t.prelude));return r=r!==x?Re(r):x,e({},t,{prelude:r!==x?se(r):t.prelude,value:t.value?e({},t.value,{value:Ne(P(t.value.value.value),n)}):null})}function Qe(t,n){switch(t.name.toLocaleLowerCase()){case"media":case"layer":return u=n,e({},r=t,{value:r.value?e({},r.value,{value:Ne(P(r.value.value.value),u)}):null});case"keyframes":return function(t,n){let r=null;return t.value&&(r=e({},t.value,{value:{type:3,value:P(t.value.value.value).value.map((t=>{switch(t.type){case 26:return u=n,e({},r=t,{value:Ue(r.value,u)});case 25:return Qe(t,n)}var r,u}))}})),e({},t,{value:r})}(t,n);case"supports":return Ie(t,n);case"container":return function(t,n){if(t.value){const r=Ce(t.prelude);if(r!==x){const u={rule:r,selector:null,parent:n.parent,uid:"c"+ke++},o=new Set,s=[],l=Ne(P(t.value.value.value),{descriptors:n.descriptors,parent:u,transformStyleRule:t=>{const[n,r]=function(e,t){const n=C(e),r=[],u=[];for(;;){if(0===n.at(1).type)return[r,u];const c=Math.max(0,n.index);for(;o=n.at(1),l=n.at(2),!(Oe(o)||7===o.type&&(7===l.type||24===l.type&&je.has(l.value.toLowerCase())));)n.consume(1);const i=n.index+1,a=e.slice(c,i),f=a.length>0?Fe(a):[Z("*")];for(;!Oe(n.at(1));)n.consume(1);const p=e.slice(i,Math.max(0,n.index+1));let y=f,h=[{type:28,source:{type:9},value:{type:0,value:[te(p.length>0?v:d),Z("~"),Z("="),{type:2,value:t}]}}];if(Ee)h=[Z(":"),ne("where",h)];else{const e=f.map(_).join("");e.endsWith(ze)?y=N(Array.from($(e.substring(0,e.length-31)))):s.push({actual:e,expected:e+ze})}r.push(...f),u.push(...y),u.push(...h),u.push(...p),n.consume(1)}var o,l}(t.prelude,u.uid);if(s.length>0)return t;const l=n.map(_).join("");try{Le.matches(l),o.add(l)}catch(c){}return e({},t,{prelude:r})}}).value;if(s.length>0){const e=new Set,t=[];let n=0;for(const{actual:u}of s)n=Math.max(n,u.length);const r=Array.from({length:n},(()=>" ")).join("");for(const{actual:u,expected:o}of s)e.has(u)||(t.push(`${u}${r.substring(0,n-u.length)} => ${o}`),e.add(u));console.warn(`The :where() pseudo-class is not supported by this browser. To use the Container Query Polyfill, you must modify the selectors under your @container rules:\n\n${t.join("\n")}`)}return o.size>0&&(u.selector=Array.from(o).join(", ")),n.descriptors.push(u),{type:25,name:"media",prelude:[te("all")],value:e({},t.value,{value:{type:3,value:l}})}}}return t}(t,n)}var r,u;return t}class He{constructor(e){this.value=void 0,this.value=e}}function Ve(e,t){if(e===t)return!0;if(typeof e==typeof t&&null!==e&&null!==t&&"object"==typeof e){if(Array.isArray(e)){if(!Array.isArray(t)||t.length!==e.length)return!1;for(let n=0,r=e.length;nthis.styles.getPropertyValue(e)));this.context.viewportChanged({width:e.width,height:e.height})}}function nt(e){const t=new AbortController;return e(t.signal).catch((e=>{if(!(e instanceof DOMException&&"AbortError"===e.message))throw e})),t}function rt(e){let t=0;if(0===e.length)return t;if(e.startsWith("cq-")&&("normal"===(e=e.substring(3))||we(e)))return t;const n=e.split(" ");for(const r of n)switch(r){case"size":t|=3;break;case"inline-size":t|=1;break;default:return 0}return t}function ut(e){let t=0;return"none"!==e&&(t|=1,"contents"===e||"inline"===e||Je.test(e)||(t|=2)),t}function ot(e,t){return parseFloat(e(t))}function st(e,t){return t.reduce(((t,n)=>t+ot(e,n)),0)}function lt(e){let t=0,n=0;return"border-box"===e("box-sizing")&&(t=st(e,Ge),n=st(e,Ye)),{fontSize:ot(e,"font-size"),width:ot(e,"width")-t,height:ot(e,"height")-n}}function ct(e){return{containerType:rt(e(y).trim()),containerNames:(n=e(h).trim(),n.startsWith("cq-")&&("none"===(n=n.substring(3))||we(n))?new Set([]):new Set(0===n.length?[]:n.split(" "))),writingAxis:(t=e("writing-mode").trim(),Be.has(t)?1:0),displayFlags:ut(e("display").trim())};var t,n}function it(e,t,n){null!=n?n!=e.getPropertyValue(t)&&e.setProperty(t,n):e.removeProperty(t)}function at(e){const t=e[We];return null!=t?t:[]}function ft(e,t){e[We]=t}new Promise((e=>{})),window.CQPolyfill={version:"1.0.2"},"container"in document.documentElement.style||function(){function n(e){return e[De]||null}const r=document.documentElement;if(n(r))return;const u=document.createElement(`cq-polyfill-${f}`),o=document.createElement("style");new MutationObserver((e=>{for(const t of e){for(const e of t.removedNodes){const t=n(e);null==t||t.disconnect()}t.target.nodeType!==Node.DOCUMENT_NODE&&t.target.nodeType!==Node.DOCUMENT_FRAGMENT_NODE&&null===t.target.parentNode||"attributes"===t.type&&t.attributeName&&(t.attributeName===v||t.attributeName===d||t.target instanceof Element&&t.target.getAttribute(t.attributeName)===t.oldValue)||(k(t.target).mutate(),h())}})).observe(r,{childList:!0,subtree:!0,attributes:!0,attributeOldValue:!0});const s=new ResizeObserver((e=>{for(const t of e)k(t.target).resize();k(r).update(q())})),l=new Ke(r);async function c(e,{source:t,url:n,signal:u}){const o=function(e,t){try{const n=Array.from($(e));if(t)for(let e=0;ee};return{source:W(Ne(P(n,!0),r)),descriptors:r.descriptors}}catch(n){return console.warn("An error occurred while transpiling stylesheet: "+n),{source:e,descriptors:[]}}}(t,n?n.toString():void 0);let s=()=>{},l=()=>{};const c=k(r);let i=!1;return null!=u&&u.aborted||(l=()=>{if(!i){const{sheet:t}=e;null!=t&&(ft(t,o.descriptors),i=!0,s=()=>{ft(t),c.mutate(),h()},c.mutate(),h())}}),{source:o.source,dispose:s,refresh:l}}const a={cqw:null,cqh:null};function p({width:e,height:t}){a.cqw=e,a.cqh=t}function y(e,t,n){if(e instanceof Element&&t){let r="";for(const[n,u]of t.conditions){const t=n.value;null!=t.selector&&null!=u&&!(2&~u)&&e.matches(t.selector)&&(r.length>0&&(r+=" "),r+=t.uid)}r.length>0?e.setAttribute(n,r):e.removeAttribute(n)}}function h(){s.unobserve(r),s.observe(r)}const S=()=>{const e=[];for(const t of document.styleSheets)for(const n of at(t))e.push([new He(n),0]);return e},x=window.getComputedStyle(r),q=()=>{const t=e=>x.getPropertyValue(e),n=ct(t),r=lt(t);return{parentState:null,conditions:S(),context:e({},a,{fontSize:r.fontSize,rootFontSize:r.fontSize,writingAxis:n.writingAxis}),displayFlags:n.displayFlags,isQueryContainer:!1}},C=e=>e;function k(a){let f=n(a);if(!f){let h,S=null,x=!1;a===r?(h=l,S=C):a===u?(x=!0,h=new tt(u,{viewportChanged:p})):h=a===o?new et(o):a instanceof HTMLLinkElement?new Xe(a,{registerStyleSheet:t=>c(a,e({},t))}):a instanceof HTMLStyleElement?new Ze(a,{registerStyleSheet:t=>c(a,e({},t))}):new Ke(a);let q=Symbol();if(null==S&&a instanceof Element){const n=function(n){const r=window.getComputedStyle(n);return function(){let n=null;return(...u)=>{if(null==n||!Ve(n[0],u)){const o=(n=>{const{context:u,conditions:o}=n,s=e=>r.getPropertyValue(e),l=ct(s),c=e({},u,{writingAxis:l.writingAxis});let a=o,f=!1,p=l.displayFlags;!(1&n.displayFlags)&&(p=0);const{containerType:y,containerNames:h}=l;if(y>0){const e=y>0&&!(2&~p),n=new Map(o.map((e=>[e[0].value,e[1]])));if(a=[],f=!0,e){const e=lt(s);c.fontSize=e.fontSize;const r=function(e,t){const n={value:t.width},r={value:t.height};let u=n,o=r;if(1===e.writingAxis){const e=u;u=o,o=e}return!!(2&~e.containerType)&&(o.value=void 0),{width:n.value,height:r.value,inlineSize:u.value,blockSize:o.value}}(l,e),f={sizeFeatures:r,treeContext:c},p=e=>{const{rule:r}=e,u=r.name,o=null==u||h.has(u)?function(e,n){const r=new Map,u=n.sizeFeatures;for(const s of e.features){const e=t(s,u);if(1===e.type)return null;r.set(s,e)}const o=i(e.condition,{sizeFeatures:r,treeContext:n.treeContext});return 5===o.type?o.value:null}(r,f):null;var s;return null==o?1===((null!=(s=n.get(e))?s:0)&&1):!0===o},y=(e,t)=>{let n=e.get(t);if(null==n){const r=p(t);n=(r?1:0)|(!0!==r||null!=t.parent&&1&~y(e,t.parent)?0:2),e.set(t,n)}return n},v=new Map;for(const t of o)a.push([t[0],y(v,t[0].value)]);c.cqw=null!=r.width?r.width/100:u.cqw,c.cqh=null!=r.height?r.height/100:u.cqh}}return{parentState:new He(n),conditions:a,context:c,displayFlags:p,isQueryContainer:f}})(...u);null!=n&&Ve(n[1],o)||(n=[u,o])}return n[1]}}()}(a);S=e=>n(e,q)}const $=S||C;let A=null;const E=e=>{const t=A,n=$(e);return A=n,[A,A!==t]},z=a instanceof HTMLElement||a instanceof SVGElement?a.style:null;let L=!1;f={connect(){for(let e=a.firstChild;null!=e;e=e.nextSibling)k(e);h.connected()},disconnect(){a instanceof Element&&(s.unobserve(a),a.removeAttribute(v),a.removeAttribute(d)),z&&(z.removeProperty(g),z.removeProperty(b),z.removeProperty(m),z.removeProperty(w));for(let e=a.firstChild;null!=e;e=e.nextSibling){const t=n(e);null==t||t.disconnect()}h.disconnected(),delete a[De]},update(e){const[t,n]=E(e);if(n){if(y(a,e,d),y(a,t,v),a instanceof Element){const e=x||t.isQueryContainer;e&&!L?(s.observe(a),L=!0):!e&&L&&(s.unobserve(a),L=!1)}if(z){const n=t.context,r=n.writingAxis;let u=null,o=null,s=null,l=null;(r!==e.context.writingAxis||t.isQueryContainer)&&(u=`var(${0===r?m:w})`,o=`var(${1===r?m:w})`),e&&!t.isQueryContainer||(n.cqw&&(s=n.cqw+"px"),n.cqh&&(l=n.cqh+"px")),it(z,g,u),it(z,b,o),it(z,m,s),it(z,w,l)}h.updated()}for(let r=a.firstChild;null!=r;r=r.nextSibling)k(r).update(t)},resize(){q=Symbol()},mutate(){q=Symbol();for(let e=a.firstChild;null!=e;e=e.nextSibling)k(e).mutate()}},a[De]=f,f.connect()}return f}r.prepend(o,u),k(r),h()}(); diff --git a/kirby/panel/dist/js/index.js b/kirby/panel/dist/js/index.js deleted file mode 100644 index 62f6688..0000000 --- a/kirby/panel/dist/js/index.js +++ /dev/null @@ -1 +0,0 @@ -import{I as t,P as e,S as s,F as n,N as i,s as o,l as r,w as l,c as a,e as u,t as c,a as d,b as p,d as h,f as m,g as f,k as g,D as k,h as b,E as v,i as y,j as $,m as _,T as w,u as x,n as S,o as C,r as O,p as A,q as T,v as I,x as j,y as M,V as E,z as L,A as B,B as D,C as P,G as N}from"./vendor.js";const R=t=>({changeName:async(e,s,n)=>t.patch(e+"/files/"+s+"/name",{name:n}),delete:async(e,s)=>t.delete(e+"/files/"+s),async get(e,s,n){let i=await t.get(e+"/files/"+s,n);return!0===Array.isArray(i.content)&&(i.content={}),i},link(t,e,s){return"/"+this.url(t,e,s)},update:async(e,s,n)=>t.patch(e+"/files/"+s,n),url(t,e,s){let n=t+"/files/"+e;return s&&(n+="/"+s),n}}),F=t=>({async blueprint(e){return t.get("pages/"+this.id(e)+"/blueprint")},async blueprints(e,s){return t.get("pages/"+this.id(e)+"/blueprints",{section:s})},async changeSlug(e,s){return t.patch("pages/"+this.id(e)+"/slug",{slug:s})},async changeStatus(e,s,n){return t.patch("pages/"+this.id(e)+"/status",{status:s,position:n})},async changeTemplate(e,s){return t.patch("pages/"+this.id(e)+"/template",{template:s})},async changeTitle(e,s){return t.patch("pages/"+this.id(e)+"/title",{title:s})},async children(e,s){return t.post("pages/"+this.id(e)+"/children/search",s)},async create(e,s){return null===e||"/"===e?t.post("site/children",s):t.post("pages/"+this.id(e)+"/children",s)},async delete(e,s){return t.delete("pages/"+this.id(e),s)},async duplicate(e,s,n){return t.post("pages/"+this.id(e)+"/duplicate",{slug:s,children:n.children||!1,files:n.files||!1})},async get(e,s){let n=await t.get("pages/"+this.id(e),s);return!0===Array.isArray(n.content)&&(n.content={}),n},id:t=>t.replace(/\//g,"+"),async files(e,s){return t.post("pages/"+this.id(e)+"/files/search",s)},link(t){return"/"+this.url(t)},async preview(t){return(await this.get(this.id(t),{select:"previewUrl"})).previewUrl},async search(e,s){return e?t.post("pages/"+this.id(e)+"/children/search?select=id,title,hasChildren",s):t.post("site/children/search?select=id,title,hasChildren",s)},async update(e,s){return t.patch("pages/"+this.id(e),s)},url(t,e){let s=null===t?"pages":"pages/"+String(t).replace(/\//g,"+");return e&&(s+="/"+e),s}});function q(t){if(void 0!==t)return JSON.parse(JSON.stringify(t))}function Y(t,e){for(const s of Object.keys(e))e[s]instanceof Object&&Object.assign(e[s],Y(t[s]||{},e[s]));return Object.assign(t||{},e),t}function z(t){return Object.keys(t).reduce(((e,s)=>(e[s.toLowerCase()]=t[s],e)),{})}const H={clone:q,isEmpty:function(t){return null==t||""===t||("object"==typeof t&&0===Object.keys(t).length&&t.constructor===Object||void 0!==t.length&&0===t.length)},merge:Y,toLowerKeys:z};const U=t=>({running:0,async request(e,s,n=!1){s=Object.assign(s||{},{credentials:"same-origin",cache:"no-store",headers:{"content-type":"application/json",...z(s.headers??{})}}),t.methodOverwrite&&"GET"!==s.method&&"POST"!==s.method&&(s.headers["x-http-method-override"]=s.method,s.method="POST"),s=t.onPrepare(s);const i=e+"/"+JSON.stringify(s);t.onStart(i,n),this.running++;try{const n=await fetch([t.endpoint,e].join(t.endpoint.endsWith("/")||e.startsWith("/")?"":"/"),s),o=await async function(t){const e=await t.text();let s;try{s=JSON.parse(e)}catch(n){return window.panel.$vue.$api.onParserError({html:e}),!1}return s}(n);if(n.status<200||n.status>299)throw o;if("error"===o.status)throw o;let r=o;return o.data&&"model"===o.type&&(r=o.data),this.running--,t.onComplete(i),t.onSuccess(o),r}catch(o){if(this.running--,t.onComplete(i),!1!==t.onError(o))throw o;return{}}},async get(t,e,s,n=!1){return e&&(t+="?"+Object.keys(e).filter((t=>void 0!==e[t]&&null!==e[t])).map((t=>t+"="+e[t])).join("&")),this.request(t,Object.assign(s||{},{method:"GET"}),n)},async post(t,e,s,n="POST",i=!1){return this.request(t,Object.assign(s||{},{method:n,body:JSON.stringify(e)}),i)},async patch(t,e,s,n=!1){return this.post(t,e,s,"PATCH",n)},async delete(t,e,s,n=!1){return this.post(t,e,s,"DELETE",n)}}),V=t=>({blueprint:async e=>t.get("users/"+e+"/blueprint"),blueprints:async(e,s)=>t.get("users/"+e+"/blueprints",{section:s}),changeEmail:async(e,s)=>t.patch("users/"+e+"/email",{email:s}),changeLanguage:async(e,s)=>t.patch("users/"+e+"/language",{language:s}),changeName:async(e,s)=>t.patch("users/"+e+"/name",{name:s}),changePassword:async(e,s)=>t.patch("users/"+e+"/password",{password:s}),changeRole:async(e,s)=>t.patch("users/"+e+"/role",{role:s}),create:async e=>t.post("users",e),delete:async e=>t.delete("users/"+e),deleteAvatar:async e=>t.delete("users/"+e+"/avatar"),link(t,e){return"/"+this.url(t,e)},async list(e){return t.post(this.url(null,"search"),e)},get:async(e,s)=>t.get("users/"+e,s),async roles(e){return(await t.get(this.url(e,"roles"))).data.map((t=>({info:t.description||`(${window.panel.$t("role.description.placeholder")})`,text:t.title,value:t.name})))},search:async e=>t.post("users/search",e),update:async(e,s)=>t.patch("users/"+e,s),url(t,e){let s=t?"users/"+t:"users";return e&&(s+="/"+e),s}}),K={install(t,e){t.prototype.$api=t.$api=((t={})=>{const e={...{endpoint:"/api",methodOverwrite:!0,onPrepare:t=>t,onStart(){},onComplete(){},onSuccess(){},onParserError(){},onError(t){throw window.console.log(t.message),t}},...t.config||{}};let s={...e,...U(e),...t};return s.auth=(t=>({async login(e){const s={long:e.remember||!1,email:e.email,password:e.password};return t.post("auth/login",s)},logout:async()=>t.post("auth/logout"),ping:async()=>t.post("auth/ping"),user:async e=>t.get("auth",e),verifyCode:async e=>t.post("auth/code",{code:e})}))(s),s.files=R(s),s.languages=(t=>({create:async e=>t.post("languages",e),delete:async e=>t.delete("languages/"+e),get:async e=>t.get("languages/"+e),list:async()=>t.get("languages"),update:async(e,s)=>t.patch("languages/"+e,s)}))(s),s.pages=F(s),s.roles=(t=>({list:async e=>t.get("roles",e),get:async e=>t.get("roles/"+e)}))(s),s.system=(t=>({get:async(e={view:"panel"})=>t.get("system",e),install:async e=>(await t.post("system/install",e)).user,register:async e=>t.post("system/register",e)}))(s),s.site=(t=>({blueprint:async()=>t.get("site/blueprint"),blueprints:async()=>t.get("site/blueprints"),changeTitle:async e=>t.patch("site/title",{title:e}),children:async e=>t.post("site/children/search",e),get:async(e={view:"panel"})=>t.get("site",e),update:async e=>t.post("site",e)}))(s),s.translations=(t=>({list:async()=>t.get("translations"),get:async e=>t.get("translations/"+e)}))(s),s.users=V(s),s})({config:{endpoint:window.panel.$urls.api,onComplete:s=>{t.$api.requests=t.$api.requests.filter((t=>t!==s)),0===t.$api.requests.length&&e.dispatch("isLoading",!1)},onError:e=>{if(403===e.code&&("Unauthenticated"===e.message||"access.panel"===e.key))return t.prototype.$go("/logout"),!1;window.panel.$config.debug&&window.console.error(e)},onParserError:({html:t,silent:s})=>{e.dispatch("fatal",{html:t,silent:s})},onPrepare:t=>(window.panel.$language&&(t.headers["x-language"]=window.panel.$language.code),t.headers["x-csrf"]=window.panel.$system.csrf,t),onStart:(s,n=!1)=>{!1===n&&e.dispatch("isLoading",!0),t.$api.requests.push(s)},onSuccess:()=>{clearInterval(t.$api.ping),t.$api.ping=setInterval(t.$api.auth.ping,3e5)}},ping:null,requests:[]}),t.$api.ping=setInterval(t.$api.auth.ping,3e5)}},J={name:"Fiber",data:()=>({component:null,state:window.fiber,key:null}),created(){this.$fiber.init(this.state,{base:document.querySelector("base").href,headers:()=>({"X-CSRF":this.state.$system.csrf}),onFatal({text:t,options:e}){this.$store.dispatch("fatal",{html:t,silent:e.silent})},onFinish:()=>{0===this.$api.requests.length&&this.$store.dispatch("isLoading",!1)},onPushState:t=>{window.history.pushState(t,"",t.$url)},onReplaceState:t=>{window.history.replaceState(t,"",t.$url)},onStart:({silent:t})=>{!0!==t&&this.$store.dispatch("isLoading",!0)},onSwap:async(t,e)=>{e={navigate:!0,replace:!1,...e},this.setGlobals(t),this.setTitle(t),this.setTranslation(t),this.component=t.$view.component,this.state=t,this.key=t.$view.timestamp,!0===e.navigate&&this.navigate()},query:()=>{var t;return{language:null==(t=this.state.$language)?void 0:t.code}}}),window.addEventListener("popstate",this.$reload)},methods:{navigate(){this.$store.dispatch("navigate")},setGlobals(t){["$config","$direction","$language","$languages","$license","$menu","$multilang","$permissions","$searches","$system","$translation","$urls","$user","$view"].forEach((e=>{void 0!==t[e]?Vue.prototype[e]=window.panel[e]=t[e]:Vue.prototype[e]=t[e]=window.panel[e]}))},setTitle(t){t.$view.title?document.title=t.$view.title+" | "+t.$system.title:document.title=t.$system.title},setTranslation(t){t.$translation&&(document.documentElement.lang=t.$translation.code)}},render(t){if(this.component)return t(this.component,{key:this.key,props:this.state.$view.props})}};function G(t,e,s,n,i,o,r,l){var a,u="function"==typeof t?t.options:t;if(e&&(u.render=e,u.staticRenderFns=s,u._compiled=!0),n&&(u.functional=!0),o&&(u._scopeId="data-v-"+o),r?(a=function(t){(t=t||this.$vnode&&this.$vnode.ssrContext||this.parent&&this.parent.$vnode&&this.parent.$vnode.ssrContext)||"undefined"==typeof __VUE_SSR_CONTEXT__||(t=__VUE_SSR_CONTEXT__),i&&i.call(this,t),t&&t._registeredComponents&&t._registeredComponents.add(r)},u._ssrRegister=a):i&&(a=l?function(){i.call(this,(u.functional?this.parent:this).$root.$options.shadowRoot)}:i),a)if(u.functional){u._injectStyles=a;var c=u.render;u.render=function(t,e){return a.call(e),c(t,e)}}else{var d=u.beforeCreate;u.beforeCreate=d?[].concat(d,a):[a]}return{exports:t,options:u}}const W=G({props:{autofocus:{type:Boolean,default:!0},cancelButton:{type:[String,Boolean],default:!0},disabled:Boolean,icon:{type:String,default:"check"},size:{type:String,default:"default"},submitButton:{type:[String,Boolean],default:!0},theme:String,visible:Boolean},data:()=>({notification:null}),computed:{buttons(){let t=[];return this.cancelButton&&t.push({icon:"cancel",text:this.cancelButtonLabel,class:"k-dialog-button-cancel",click:this.cancel}),this.submitButtonConfig&&t.push({icon:this.icon,text:this.submitButtonLabel,theme:this.theme,class:"k-dialog-button-submit",click:this.submit,disabled:this.disabled}),t},cancelButtonLabel(){return!1!==this.cancelButton&&(!0===this.cancelButton||0===this.cancelButton.length?this.$t("cancel"):this.cancelButton)},submitButtonConfig(){return void 0!==this.$attrs.button?this.$attrs.button:void 0===this.submitButton||this.submitButton},submitButtonLabel(){return!0===this.submitButton||0===this.submitButton.length?this.$t("confirm"):this.submitButton}},created(){this.$events.$on("keydown.esc",this.close,!1)},destroyed(){this.$events.$off("keydown.esc",this.close,!1)},mounted(){this.visible&&this.$nextTick(this.open)},methods:{onOverlayClose(){this.notification=null,this.$emit("close"),this.$events.$off("keydown.esc",this.close),this.$store.dispatch("dialog",!1)},open(){this.$store.state.dialog||this.$store.dispatch("dialog",!0),this.notification=null,this.$refs.overlay.open(),this.$emit("open"),this.$events.$on("keydown.esc",this.close)},close(){this.$refs.overlay&&this.$refs.overlay.close()},cancel(){this.$emit("cancel"),this.close()},focus(){var t;if(null==(t=this.$refs.dialog)?void 0:t.querySelector){const t=this.$refs.dialog.querySelector(".k-dialog-button-cancel");"function"==typeof(null==t?void 0:t.focus)&&t.focus()}},error(t){this.notification={message:t,type:"error"}},submit(){this.$emit("submit")},success(t){this.notification={message:t,type:"success"}}}},(function(){var t=this,e=t._self._c;return e("k-overlay",{ref:"overlay",attrs:{autofocus:t.autofocus,centered:!0},on:{close:t.onOverlayClose,ready:function(e){return t.$emit("ready")}}},[e("div",{ref:"dialog",staticClass:"k-dialog",class:t.$vnode.data.staticClass,attrs:{"data-size":t.size},on:{mousedown:function(t){t.stopPropagation()}}},[t.notification?e("div",{staticClass:"k-dialog-notification",attrs:{"data-theme":t.notification.type}},[e("p",[t._v(t._s(t.notification.message))]),e("k-button",{attrs:{icon:"cancel"},on:{click:function(e){t.notification=null}}})],1):t._e(),e("div",{staticClass:"k-dialog-body scroll-y-auto"},[t._t("default")],2),t.$slots.footer||t.buttons.length?e("footer",{staticClass:"k-dialog-footer"},[t._t("footer",(function(){return[e("k-button-group",{attrs:{buttons:t.buttons}})]}))],2):t._e()])])}),[],!1,null,null,null,null).exports,X={props:{autofocus:{type:Boolean,default:!0},cancelButton:{type:[String,Boolean],default:!0},icon:String,submitButton:{type:[String,Boolean],default:!0},size:String,theme:String,visible:Boolean},methods:{close(){this.$refs.dialog.close(),this.$emit("close")},error(t){this.$refs.dialog.error(t)},open(){this.$refs.dialog.open(),this.$emit("open")},success(t){this.$refs.dialog.close(),t.route&&this.$go(t.route),t.message&&this.$store.dispatch("notification/success",t.message),t.event&&("string"==typeof t.event&&(t.event=[t.event]),t.event.forEach((e=>{this.$events.$emit(e,t)}))),!1!==Object.prototype.hasOwnProperty.call(t,"emit")&&!1===t.emit||this.$emit("success")}}};const Z=G({mixins:[X],props:{details:[Object,Array],message:String,size:{type:String,default:"medium"}},computed:{detailsList(){return Array.isArray(this.details)?this.details:Object.values(this.details||{})}}},(function(){var t=this,e=t._self._c;return e("k-dialog",{ref:"dialog",staticClass:"k-error-dialog",attrs:{"cancel-button":!1,size:t.size,visible:!0},on:{cancel:function(e){return t.$emit("cancel")},close:function(e){return t.$emit("close")},submit:function(e){return t.$refs.dialog.close()}}},[e("k-text",[t._v(t._s(t.message))]),t.detailsList.length?e("dl",{staticClass:"k-error-details"},[t._l(t.detailsList,(function(s,n){return[e("dt",{key:"detail-label-"+n},[t._v(" "+t._s(s.label)+" ")]),e("dd",{key:"detail-message-"+n},["object"==typeof s.message?[e("ul",t._l(s.message,(function(s,n){return e("li",{key:n},[t._v(" "+t._s(s)+" ")])})),0)]:[t._v(" "+t._s(s.message)+" ")]],2)]}))],2):t._e()],1)}),[],!1,null,null,null,null).exports;const Q=G({props:{code:Number,component:String,path:String,props:Object,referrer:String},data:()=>({isProcessing:!1}),methods:{close(){this.$refs.dialog.close()},onCancel(){"function"==typeof this.$store.state.dialog.cancel&&this.$store.state.dialog.cancel({dialog:this})},async onSubmit(t){if(!0===this.isProcessing)return!1;let e=null;this.isProcessing=!0;try{if("function"==typeof this.$store.state.dialog.submit)e=await this.$store.state.dialog.submit({dialog:this,value:t});else{if(!this.path)throw"The dialog needs a submit action or a dialog route path to be submitted";e=await this.$request(this.path,{body:t,method:"POST",type:"$dialog",headers:{"X-Fiber-Referrer":this.referrer}})}if(!1===e)return!1;this.$refs.dialog.success(e),e.dispatch&&Object.keys(e.dispatch).forEach((t=>{const s=e.dispatch[t];this.$store.dispatch(t,!0===Array.isArray(s)?[...s]:s)})),e.redirect?this.$go(e.redirect):this.$reload(e.reload||{})}catch(s){this.$refs.dialog.error(s)}finally{this.isProcessing=!1}}}},(function(){var t=this;return(0,t._self._c)(t.component,t._b({ref:"dialog",tag:"component",attrs:{disabled:t.isProcessing,visible:!0},on:{cancel:t.onCancel,submit:t.onSubmit}},"component",t.props,!1))}),[],!1,null,null,null,null).exports,tt=(t,e)=>{let s=null;return function(){clearTimeout(s),s=setTimeout((()=>t.apply(this,arguments)),e)}},et={data:()=>({models:[],issue:null,selected:{},options:{endpoint:null,max:null,multiple:!0,parent:null,selected:[],search:!0},search:null,pagination:{limit:20,page:1,total:0}}),computed:{checkedIcon(){return!0===this.multiple?"check":"circle-filled"},collection(){return{empty:this.emptyProps,items:this.items,link:!1,layout:"list",pagination:{details:!0,dropdown:!1,align:"center",...this.pagination},sortable:!1}},items(){return this.models.map(this.item)},multiple(){return!0===this.options.multiple&&1!==this.options.max}},watch:{search(){this.updateSearch()}},created(){this.updateSearch=tt(this.updateSearch,200)},methods:{async fetch(){const t={page:this.pagination.page,search:this.search,...this.fetchData||{}};try{const e=await this.$api.get(this.options.endpoint,t);this.models=e.data,this.pagination=e.pagination,this.onFetched&&this.onFetched(e)}catch(e){this.models=[],this.issue=e.message}},async open(t,e){this.pagination.page=0,this.search=null;let s=!0;Array.isArray(t)?(this.models=t,s=!1):(this.models=[],e=t),this.options={...this.options,...e},this.selected={},this.options.selected.forEach((t=>{Vue.set(this.selected,t,{id:t})})),s&&await this.fetch(),this.$refs.dialog.open()},paginate(t){this.pagination.page=t.page,this.pagination.limit=t.limit,this.fetch()},submit(){this.$emit("submit",Object.values(this.selected)),this.$refs.dialog.close()},isSelected(t){return void 0!==this.selected[t.id]},item:t=>t,toggle(t){!1!==this.options.multiple&&1!==this.options.max||(this.selected={}),!0!==this.isSelected(t)?this.options.max&&this.options.max<=Object.keys(this.selected).length||Vue.set(this.selected,t.id,t):this.$delete(this.selected,t.id)},toggleBtn(t){const e=this.isSelected(t);return{icon:e?this.checkedIcon:"circle-outline",tooltip:e?this.$t("remove"):this.$t("select"),theme:e?"positive":null}},updateSearch(){this.pagination.page=0,this.fetch()}}};const st=G({mixins:[et],computed:{emptyProps(){return{icon:"image",text:this.$t("dialog.files.empty")}}}},(function(){var t=this,e=t._self._c;return e("k-dialog",{ref:"dialog",staticClass:"k-files-dialog",attrs:{size:"medium"},on:{cancel:function(e){return t.$emit("cancel")},submit:t.submit}},[t.issue?[e("k-box",{attrs:{text:t.issue,theme:"negative"}})]:[t.options.search?e("k-input",{staticClass:"k-dialog-search",attrs:{autofocus:!0,placeholder:t.$t("search")+" …",value:t.search,type:"text",icon:"search"},on:{input:function(e){t.search=e}}}):t._e(),e("k-collection",t._b({on:{item:t.toggle,paginate:t.paginate},scopedSlots:t._u([{key:"options",fn:function({item:s}){return[e("k-button",t._b({on:{click:function(e){return t.toggle(s)}}},"k-button",t.toggleBtn(s),!1))]}}])},"k-collection",t.collection,!1))]],2)}),[],!1,null,null,null,null).exports;const nt=G({mixins:[X],props:{disabled:Boolean,fields:{type:[Array,Object],default:()=>[]},novalidate:{type:Boolean,default:!0},size:{type:String,default:"medium"},submitButton:{type:[String,Boolean],default:()=>window.panel.$t("save")},text:{type:String},theme:{type:String,default:"positive"},value:{type:Object,default:()=>({})}},data(){return{model:this.value}},computed:{hasFields(){return Object.keys(this.fields).length>0}},watch:{value(t){this.model=t}},methods:{onInput(t){this.model=t,this.$emit("input",t)},onSubmit(t){this.$emit("submit",t)}}},(function(){var t=this,e=t._self._c;return e("k-dialog",t._b({ref:"dialog",on:{cancel:function(e){return t.$emit("cancel")},close:function(e){return t.$emit("close")},ready:function(e){return t.$emit("ready")},submit:function(e){return t.$refs.form.submit()}}},"k-dialog",t.$props,!1),[t.text?[e("k-text",{attrs:{html:t.text}})]:t._e(),t.hasFields?e("k-form",{ref:"form",attrs:{value:t.model,fields:t.fields,novalidate:t.novalidate},on:{input:t.onInput,submit:t.onSubmit}}):e("k-box",{attrs:{theme:"negative"}},[t._v(" This form dialog has no fields ")])],2)}),[],!1,null,null,null,null).exports;const it=G({extends:nt,watch:{"model.name"(t){this.fields.code.disabled||this.onNameChanges(t)},"model.code"(t){this.fields.code.disabled||(this.model.code=this.$helper.slug(t,[this.$system.ascii]),this.onCodeChanges(this.model.code))}},methods:{onCodeChanges(t){if(!t)return this.model.locale=null;if(t.length>=2)if(-1!==t.indexOf("-")){let e=t.split("-"),s=[e[0],e[1].toUpperCase()];this.model.locale=s.join("_")}else{let e=this.$system.locales||[];(null==e?void 0:e[t])?this.model.locale=e[t]:this.model.locale=null}},onNameChanges(t){this.model.code=this.$helper.slug(t,[this.model.rules,this.$system.ascii]).substr(0,2)}}},null,null,!1,null,null,null,null).exports;const ot=G({mixins:[et],data(){const t=et.data();return{...t,model:{title:null,parent:null},options:{...t.options,parent:null}}},computed:{emptyProps(){return{icon:"page",text:this.$t("dialog.pages.empty")}},fetchData(){return{parent:this.options.parent}}},methods:{back(){this.options.parent=this.model.parent,this.pagination.page=1,this.fetch()},go(t){this.options.parent=t.id,this.pagination.page=1,this.fetch()},onFetched(t){this.model=t.model}}},(function(){var t=this,e=t._self._c;return e("k-dialog",{ref:"dialog",staticClass:"k-pages-dialog",attrs:{size:"medium"},on:{cancel:function(e){return t.$emit("cancel")},submit:t.submit}},[t.issue?[e("k-box",{attrs:{text:t.issue,theme:"negative"}})]:[t.model?e("header",{staticClass:"k-pages-dialog-navbar"},[e("k-button",{attrs:{disabled:!t.model.id,tooltip:t.$t("back"),icon:"angle-left"},on:{click:t.back}}),e("k-headline",[t._v(t._s(t.model.title))])],1):t._e(),t.options.search?e("k-input",{staticClass:"k-dialog-search",attrs:{autofocus:!0,placeholder:t.$t("search")+" …",value:t.search,type:"text",icon:"search"},on:{input:function(e){t.search=e}}}):t._e(),e("k-collection",t._b({on:{item:t.toggle,paginate:t.paginate},scopedSlots:t._u([{key:"options",fn:function({item:s}){return[e("k-button",t._b({on:{click:function(e){return t.toggle(s)}}},"k-button",t.toggleBtn(s),!1)),t.model?e("k-button",{attrs:{disabled:!s.hasChildren,tooltip:t.$t("open"),icon:"angle-right"},on:{click:function(e){return e.stopPropagation(),t.go(s)}}}):t._e()]}}])},"k-collection",t.collection,!1))]],2)}),[],!1,null,null,null,null).exports;const rt=G({mixins:[X],props:{disabled:Boolean,icon:{type:String,default:"trash"},submitButton:{type:[String,Boolean],default:()=>window.panel.$t("delete")},text:String,theme:{type:String,default:"negative"}}},(function(){var t=this;return(0,t._self._c)("k-text-dialog",t._g(t._b({ref:"dialog"},"k-text-dialog",t.$props,!1),t.$listeners),[t._t("default")],2)}),[],!1,null,null,null,null).exports;const lt=G({mixins:[X],props:{disabled:Boolean,text:String}},(function(){var t=this,e=t._self._c;return e("k-dialog",t._g(t._b({ref:"dialog"},"k-dialog",t.$props,!1),t.$listeners),[t._t("default",(function(){return[t.text?e("k-text",{attrs:{html:t.text}}):e("k-box",{attrs:{theme:"negative"}},[t._v(" This dialog does not define any text ")])]}))],2)}),[],!1,null,null,null,null).exports;const at=G({mixins:[et],computed:{emptyProps(){return{icon:"users",text:this.$t("dialog.users.empty")}}},methods:{item:t=>({...t,key:t.email,info:t.info!==t.text?t.info:null})}},(function(){var t=this,e=t._self._c;return e("k-dialog",{ref:"dialog",staticClass:"k-users-dialog",attrs:{size:"medium"},on:{cancel:function(e){return t.$emit("cancel")},submit:t.submit}},[t.issue?[e("k-box",{attrs:{text:t.issue,theme:"negative"}})]:[t.options.search?e("k-input",{staticClass:"k-dialog-search",attrs:{autofocus:!0,placeholder:t.$t("search")+" …",value:t.search,type:"text",icon:"search"},on:{input:function(e){t.search=e}}}):t._e(),e("k-collection",t._b({on:{item:t.toggle,paginate:t.paginate},scopedSlots:t._u([{key:"options",fn:function({item:s}){return[e("k-button",t._b({on:{click:function(e){return t.toggle(s)}}},"k-button",t.toggleBtn(s),!1))]}}])},"k-collection",t.collection,!1))]],2)}),[],!1,null,null,null,null).exports,ut={install(t){t.component("k-dialog",W),t.component("k-error-dialog",Z),t.component("k-fiber-dialog",Q),t.component("k-files-dialog",st),t.component("k-form-dialog",nt),t.component("k-language-dialog",it),t.component("k-pages-dialog",ot),t.component("k-remove-dialog",rt),t.component("k-text-dialog",lt),t.component("k-users-dialog",at)}};const ct=G({inheritAttrs:!1,props:{id:String,icon:String,tab:String,tabs:Object,title:String},data:()=>({click:!1}),computed:{breadcrumb(){return this.$store.state.drawers.open},hasTabs(){return this.tabs&&Object.keys(this.tabs).length>1},index(){return this.breadcrumb.findIndex((t=>t.id===this._uid))},nested(){return this.index>0}},watch:{index(){-1===this.index&&this.close()}},destroyed(){this.$store.dispatch("drawers/close",this._uid)},methods:{close(){this.$refs.overlay.close()},goTo(t){if(t===this._uid)return!0;this.$store.dispatch("drawers/goto",t)},mouseup(){!0===this.click&&this.close(),this.click=!1},mousedown(t=!1){this.click=t,!0===this.click&&this.$store.dispatch("drawers/close")},onClose(){this.$store.dispatch("drawers/close",this._uid),this.$emit("close")},onOpen(){this.$store.dispatch("drawers/open",{id:this._uid,icon:this.icon,title:this.title}),this.$emit("open")},open(){this.$refs.overlay.open()}}},(function(){var t=this,e=t._self._c;return e("k-overlay",{ref:"overlay",attrs:{dimmed:!1},on:{close:t.onClose,open:t.onOpen}},[e("div",{staticClass:"k-drawer",attrs:{"data-id":t.id,"data-nested":t.nested},on:{mousedown:function(e){return e.stopPropagation(),t.mousedown(!0)},mouseup:t.mouseup}},[e("div",{staticClass:"k-drawer-box",on:{mousedown:function(e){return e.stopPropagation(),t.mousedown(!1)}}},[e("header",{staticClass:"k-drawer-header"},[1===t.breadcrumb.length?e("h2",{staticClass:"k-drawer-title"},[e("k-icon",{attrs:{type:t.icon}}),t._v(" "+t._s(t.title)+" ")],1):e("ul",{staticClass:"k-drawer-breadcrumb"},t._l(t.breadcrumb,(function(s){return e("li",{key:s.id},[e("k-button",{attrs:{icon:s.icon,text:s.title},on:{click:function(e){return t.goTo(s.id)}}})],1)})),0),t.hasTabs?e("nav",{staticClass:"k-drawer-tabs"},t._l(t.tabs,(function(s){return e("k-button",{key:s.name,staticClass:"k-drawer-tab",attrs:{current:t.tab==s.name,text:s.label},on:{click:function(e){return e.stopPropagation(),t.$emit("tab",s.name)}}})})),1):t._e(),e("nav",{staticClass:"k-drawer-options"},[t._t("options"),e("k-button",{staticClass:"k-drawer-option",attrs:{icon:"check"},on:{click:t.close}})],2)]),e("div",{staticClass:"k-drawer-body scroll-y-auto"},[t._t("default")],2)])])])}),[],!1,null,null,null,null).exports;const dt=G({inheritAttrs:!1,props:{empty:{type:String,default:()=>"Missing field setup"},icon:String,id:String,tabs:Object,title:String,type:String,value:Object},data:()=>({tab:null}),computed:{fields(){const t=this.tab||null;return(this.tabs[t]||this.firstTab).fields||{}},firstTab(){return Object.values(this.tabs)[0]}},methods:{close(){this.$refs.drawer.close()},focus(t){var e;"function"==typeof(null==(e=this.$refs.form)?void 0:e.focus)&&this.$refs.form.focus(t)},open(t,e=!0){var s;this.$refs.drawer.open(),this.tab=t||this.firstTab.name,!0===e&&(e=null==(s=Object.values(this.fields).find((t=>!0===t.autofocus)))?void 0:s.name),setTimeout((()=>{this.focus(e)}),10)}}},(function(){var t=this,e=t._self._c;return e("k-drawer",{ref:"drawer",staticClass:"k-form-drawer",attrs:{id:t.id,icon:t.icon,tabs:t.tabs,tab:t.tab,title:t.title},on:{close:function(e){return t.$emit("close")},open:function(e){return t.$emit("open")},tab:function(e){t.tab=e}},scopedSlots:t._u([{key:"options",fn:function(){return[t._t("options")]},proxy:!0},{key:"default",fn:function(){return[0===Object.keys(t.fields).length?e("k-box",{attrs:{theme:"info"}},[t._v(" "+t._s(t.empty)+" ")]):e("k-form",{ref:"form",attrs:{autofocus:!0,fields:t.fields,value:t.$helper.clone(t.value)},on:{input:function(e){return t.$emit("input",e)},invalid:function(e){return t.$emit("invalid",e)}}})]},proxy:!0}],null,!0)})}),[],!1,null,null,null,null).exports,pt={install(t){t.component("k-drawer",ct),t.component("k-form-drawer",dt)}};const ht=G({props:{html:{type:Boolean,default:!1},limit:{type:Number,default:10},skip:{type:Array,default:()=>[]},options:Array,query:String},data:()=>({matches:[],selected:{text:null}}),methods:{close(){this.$refs.dropdown.close()},onSelect(t){this.$emit("select",t),this.$refs.dropdown.close()},search(t){if(t.length<1)return;const e=new RegExp(RegExp.escape(t),"ig");this.matches=this.options.filter((t=>!!t.text&&(-1===this.skip.indexOf(t.value)&&null!==t.text.match(e)))).slice(0,this.limit),this.$emit("search",t,this.matches),this.$refs.dropdown.open()}}},(function(){var t=this,e=t._self._c;return e("k-dropdown",{staticClass:"k-autocomplete"},[t._t("default"),e("k-dropdown-content",t._g({ref:"dropdown",attrs:{autofocus:!0}},t.$listeners),t._l(t.matches,(function(s,n){return e("k-dropdown-item",t._b({key:n,on:{mousedown:function(e){return t.onSelect(s)},keydown:[function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"tab",9,e.key,"Tab")?null:(e.preventDefault(),t.onSelect(s))},function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"enter",13,e.key,"Enter")?null:(e.preventDefault(),t.onSelect(s))},function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"left",37,e.key,["Left","ArrowLeft"])||"button"in e&&0!==e.button?null:(e.preventDefault(),t.close.apply(null,arguments))},function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"backspace",void 0,e.key,void 0)?null:(e.preventDefault(),t.close.apply(null,arguments))},function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"delete",[8,46],e.key,["Backspace","Delete","Del"])?null:(e.preventDefault(),t.close.apply(null,arguments))}]}},"k-dropdown-item",s,!1),[e("span",{domProps:{innerHTML:t._s(t.html?s.text:t.$esc(s.text))}})])})),1),t._v(" "+t._s(t.query)+" ")],2)}),[],!1,null,null,null,null).exports;const mt=G({props:{disabled:Boolean,max:String,min:String,value:String},data(){return this.data(this.value)},computed:{numberOfDays(){return this.toDate().daysInMonth()},firstWeekday(){const t=this.toDate().day();return t>0?t:7},weekdays(){return["mon","tue","wed","thu","fri","sat","sun"].map((t=>this.$t("days."+t)))},weeks(){const t=this.firstWeekday-1;return Math.ceil((this.numberOfDays+t)/7)},monthnames(){return["january","february","march","april","may","june","july","august","september","october","november","december"].map((t=>this.$t("months."+t)))},months(){var t=[];return this.monthnames.forEach(((e,s)=>{const n=this.toDate(1,s);t.push({value:s,text:e,disabled:n.isBefore(this.current.min,"month")||n.isAfter(this.current.max,"month")})})),t},years(){var t,e;const s=(null==(t=this.current.min)?void 0:t.get("year"))??this.current.year-20,n=(null==(e=this.current.max)?void 0:e.get("year"))??this.current.year+20;return this.toOptions(s,n)}},watch:{value(t){const e=this.data(t);this.dt=e.dt,this.current=e.current}},methods:{data(t){const e=this.$library.dayjs.iso(t),s=this.$library.dayjs();return{dt:e,current:{month:(e??s).month(),year:(e??s).year(),min:this.$library.dayjs.iso(this.min),max:this.$library.dayjs.iso(this.max)}}},days(t){let e=[];const s=7*(t-1)+1,n=s+7;for(let i=s;ithis.numberOfDays;e.push(s?"":t)}return e},isDisabled(t){const e=this.toDate(t);return this.disabled||e.isBefore(this.current.min,"day")||e.isAfter(this.current.max,"day")},isSelected(t){return this.toDate(t).isSame(this.dt,"day")},isToday(t){const e=this.$library.dayjs();return this.toDate(t).isSame(e,"day")},onInput(){var t;this.$emit("input",(null==(t=this.dt)?void 0:t.toISO("date"))||null)},onNext(){const t=this.toDate().add(1,"month");this.show(t)},onPrev(){const t=this.toDate().subtract(1,"month");this.show(t)},select(t){const e="today"===t?this.$library.dayjs().merge(this.toDate(),"time"):this.toDate(t);this.dt=e,this.show(e),this.onInput()},show(t){this.current.year=t.year(),this.current.month=t.month()},toDate(t=1,e=this.current.month){return this.$library.dayjs(`${this.current.year}-${e+1}-${t}`)},toOptions(t,e){for(var s=[],n=t;n<=e;n++)s.push({value:n,text:this.$helper.pad(n)});return s}}},(function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-calendar-input"},[e("nav",[e("k-button",{attrs:{icon:"angle-left"},on:{click:t.onPrev}}),e("span",{staticClass:"k-calendar-selects"},[e("k-select-input",{attrs:{options:t.months,disabled:t.disabled,required:!0,value:t.current.month},on:{input:function(e){t.current.month=Number(e)}}}),e("k-select-input",{attrs:{options:t.years,disabled:t.disabled,required:!0,value:t.current.year},on:{input:function(e){t.current.year=Number(e)}}})],1),e("k-button",{attrs:{icon:"angle-right"},on:{click:t.onNext}})],1),e("table",{staticClass:"k-calendar-table"},[e("thead",[e("tr",t._l(t.weekdays,(function(s){return e("th",{key:"weekday_"+s},[t._v(" "+t._s(s)+" ")])})),0)]),e("tbody",t._l(t.weeks,(function(s){return e("tr",{key:"week_"+s},t._l(t.days(s),(function(s,n){return e("td",{key:"day_"+n,staticClass:"k-calendar-day",attrs:{"aria-current":!!t.isToday(s)&&"date","aria-selected":!!t.isSelected(s)&&"date"}},[s?e("k-button",{attrs:{disabled:t.isDisabled(s),text:s},on:{click:function(e){return t.select(s)}}}):t._e()],1)})),0)})),0),e("tfoot",[e("tr",[e("td",{staticClass:"k-calendar-today",attrs:{colspan:"7"}},[e("k-button",{attrs:{text:t.$t("today")},on:{click:function(e){return t.select("today")}}})],1)])])])])}),[],!1,null,null,null,null).exports;const ft=G({props:{count:Number,min:Number,max:Number,required:{type:Boolean,default:!1}},computed:{valid(){return!1===this.required&&0===this.count||(!0!==this.required||0!==this.count)&&(!(this.min&&this.countthis.max))}}},(function(){var t=this,e=t._self._c;return e("span",{staticClass:"k-counter",attrs:{"data-invalid":!t.valid}},[e("span",[t._v(t._s(t.count))]),t.min&&t.max?e("span",{staticClass:"k-counter-rules"},[t._v("("+t._s(t.min)+"–"+t._s(t.max)+")")]):t.min?e("span",{staticClass:"k-counter-rules"},[t._v("≥ "+t._s(t.min))]):t.max?e("span",{staticClass:"k-counter-rules"},[t._v("≤ "+t._s(t.max))]):t._e()])}),[],!1,null,null,null,null).exports;const gt=G({props:{disabled:Boolean,config:Object,fields:{type:[Array,Object],default:()=>({})},novalidate:{type:Boolean,default:!1},value:{type:Object,default:()=>({})}},data(){return{errors:{},listeners:{...this.$listeners,submit:this.onSubmit}}},methods:{focus(t){var e,s;null==(s=null==(e=this.$refs.fields)?void 0:e.focus)||s.call(e,t)},onSubmit(){this.$emit("submit",this.value)},submit(){this.$refs.submitter.click()}}},(function(){var t=this,e=t._self._c;return e("form",{ref:"form",staticClass:"k-form",attrs:{method:"POST",autocomplete:"off",novalidate:""},on:{submit:function(e){return e.preventDefault(),t.onSubmit.apply(null,arguments)}}},[t._t("header"),t._t("default",(function(){return[e("k-fieldset",t._g({ref:"fields",attrs:{disabled:t.disabled,fields:t.fields,novalidate:t.novalidate},model:{value:t.value,callback:function(e){t.value=e},expression:"value"}},t.listeners))]})),t._t("footer"),e("input",{ref:"submitter",staticClass:"k-form-submitter",attrs:{type:"submit"}})],2)}),[],!1,null,null,null,null).exports;const kt=G({props:{lock:[Boolean,Object]},data:()=>({isRefreshing:null,isLocking:null}),computed:{api(){return[this.$view.path+"/lock",null,null,!0]},hasChanges(){return this.$store.getters["content/hasChanges"]()},isDisabled(){return!1===this.$store.state.content.status.enabled},isLocked(){return"lock"===this.lockState},isUnlocked(){return"unlock"===this.lockState},mode(){return null!==this.lockState?this.lockState:!0===this.hasChanges?"changes":null},lockState(){return this.supportsLocking&&this.lock?this.lock.state:null},supportsLocking(){return!1!==this.lock},theme(){return"lock"===this.mode?"negative":"unlock"===this.mode?"info":"notice"}},watch:{hasChanges:{handler(t,e){!0===this.supportsLocking&&!1===this.isLocked&&!1===this.isUnlocked&&(!0===t?(this.onLock(),this.isLocking=setInterval(this.onLock,3e4)):e&&(clearInterval(this.isLocking),this.onLock(!1)))},immediate:!0},isLocked(t){!1===t&&this.$events.$emit("model.reload")}},created(){this.supportsLocking&&(this.isRefreshing=setInterval(this.check,1e4)),this.$events.$on("keydown.cmd.s",this.onSave)},destroyed(){clearInterval(this.isRefreshing),clearInterval(this.isLocking),this.$events.$off("keydown.cmd.s",this.onSave)},methods:{async check(){const{lock:t}=await this.$api.get(...this.api);Vue.set(this.$view.props,"lock",t)},async onLock(t=!0){if(!0===t)try{await this.$api.patch(...this.api)}catch(e){clearInterval(this.isLocking),this.$store.dispatch("content/revert")}else clearInterval(this.isLocking),await this.$api.delete(...this.api)},onDownload(){let t="";const e=this.$store.getters["content/changes"]();Object.keys(e).forEach((s=>{const n=e[s];t+=s+": \n\n","object"==typeof n&&Object.keys(n).length||Array.isArray(n)&&n.length?t+=JSON.stringify(n,null,2):t+=n,t+="\n\n----\n\n"}));let s=document.createElement("a");s.setAttribute("href","data:text/plain;charset=utf-8,"+encodeURIComponent(t)),s.setAttribute("download",this.$view.path+".txt"),s.style.display="none",document.body.appendChild(s),s.click(),document.body.removeChild(s)},async onResolve(){await this.onUnlock(!1),this.$store.dispatch("content/revert")},onRevert(){this.$refs.revert.open()},async onSave(t){if(!t)return!1;t.preventDefault&&t.preventDefault();try{await this.$store.dispatch("content/save"),this.$events.$emit("model.update"),this.$store.dispatch("notification/success",":)")}catch(e){if(403===e.code)return;e.details&&Object.keys(e.details).length>0?this.$store.dispatch("notification/error",{message:this.$t("error.form.incomplete"),details:e.details}):this.$store.dispatch("notification/error",{message:this.$t("error.form.notSaved"),details:[{label:"Exception: "+e.exception,message:e.message}]})}},async onUnlock(t=!0){const e=[this.$view.path+"/unlock",null,null,!0];!0===t?await this.$api.patch(...e):await this.$api.delete(...e),this.$reload({silent:!0})},revert(){this.$store.dispatch("content/revert"),this.$refs.revert.close()}}},(function(){var t=this,e=t._self._c;return e("nav",{staticClass:"k-form-buttons",attrs:{"data-theme":t.theme}},["unlock"===t.mode?e("k-view",[e("p",{staticClass:"k-form-lock-info"},[t._v(" "+t._s(t.$t("lock.isUnlocked"))+" ")]),e("span",{staticClass:"k-form-lock-buttons"},[e("k-button",{staticClass:"k-form-button",attrs:{text:t.$t("download"),icon:"download"},on:{click:t.onDownload}}),e("k-button",{staticClass:"k-form-button",attrs:{text:t.$t("confirm"),icon:"check"},on:{click:t.onResolve}})],1)]):"lock"===t.mode?e("k-view",[e("p",{staticClass:"k-form-lock-info"},[e("k-icon",{attrs:{type:"lock"}}),e("span",{domProps:{innerHTML:t._s(t.$t("lock.isLocked",{email:t.$esc(t.lock.data.email)}))}})],1),t.lock.data.unlockable?e("k-button",{staticClass:"k-form-button",attrs:{text:t.$t("lock.unlock"),icon:"unlock"},on:{click:function(e){return t.onUnlock()}}}):e("k-icon",{staticClass:"k-form-lock-loader",attrs:{type:"loader"}})],1):"changes"===t.mode?e("k-view",[e("k-button",{staticClass:"k-form-button",attrs:{disabled:t.isDisabled,text:t.$t("revert"),icon:"undo"},on:{click:t.onRevert}}),e("k-button",{staticClass:"k-form-button",attrs:{disabled:t.isDisabled,text:t.$t("save"),icon:"check"},on:{click:t.onSave}})],1):t._e(),e("k-dialog",{ref:"revert",attrs:{"submit-button":t.$t("revert"),icon:"undo",theme:"negative"},on:{submit:t.revert}},[e("k-text",{attrs:{html:t.$t("revert.confirm")}})],1)],1)}),[],!1,null,null,null,null).exports;const bt=G({data:()=>({isOpen:!1,options:[]}),computed:{hasChanges(){return this.ids.length>0},ids(){return Object.keys(this.store).filter((t=>{var e;return Object.keys((null==(e=this.store[t])?void 0:e.changes)||{}).length>0}))},store(){return this.$store.state.content.models}},methods:{async toggle(){if(!1===this.$refs.list.isOpen)try{await this.$dropdown("changes",{method:"POST",body:{ids:this.ids}})((t=>{this.options=t}))}catch(t){return this.$store.dispatch("notification/success",this.$t("lock.unsaved.empty")),this.$store.dispatch("content/clear"),!1}this.$refs.list&&this.$refs.list.toggle()}}},(function(){var t=this,e=t._self._c;return t.hasChanges?e("k-dropdown",{staticClass:"k-form-indicator"},[e("k-button",{staticClass:"k-form-indicator-toggle k-topbar-button",attrs:{icon:"edit"},on:{click:t.toggle}}),e("k-dropdown-content",{ref:"list",attrs:{align:"right",theme:"light"}},[e("p",{staticClass:"k-form-indicator-info"},[t._v(t._s(t.$t("lock.unsaved"))+":")]),e("hr"),t._l(t.options,(function(s){return e("k-dropdown-item",t._b({key:s.id},"k-dropdown-item",s,!1),[t._v(" "+t._s(s.text)+" ")])}))],2)],1):t._e()}),[],!1,null,null,null,null).exports,vt={props:{after:String}},yt={props:{autofocus:Boolean}},$t={props:{before:String}},_t={props:{disabled:Boolean}},wt={props:{help:String}},xt={props:{id:{type:[Number,String],default(){return this._uid}}}},St={props:{invalid:Boolean}},Ct={props:{label:String}},Ot={props:{name:[Number,String]}},At={props:{required:Boolean}},Tt={mixins:[_t,wt,Ct,Ot,At],props:{counter:[Boolean,Object],endpoints:Object,input:[String,Number],translate:Boolean,type:String}};const It=G({mixins:[Tt],inheritAttrs:!1,computed:{labelText(){return this.label||" "}}},(function(){var t=this,e=t._self._c;return e("div",{class:"k-field k-field-name-"+t.name,attrs:{"data-disabled":t.disabled,"data-translate":t.translate},on:{focusin:function(e){return t.$emit("focus",e)},focusout:function(e){return t.$emit("blur",e)}}},[t._t("header",(function(){return[e("header",{staticClass:"k-field-header"},[t._t("label",(function(){return[e("label",{staticClass:"k-field-label",attrs:{for:t.input}},[t._v(" "+t._s(t.labelText)+" "),t.required?e("abbr",{attrs:{title:t.$t("field.required")}},[t._v("*")]):t._e()])]})),t._t("options"),t._t("counter",(function(){return[t.counter?e("k-counter",t._b({staticClass:"k-field-counter",attrs:{required:t.required}},"k-counter",t.counter,!1)):t._e()]}))],2)]})),t._t("default"),t._t("footer",(function(){return[t.help||t.$slots.help?e("footer",{staticClass:"k-field-footer"},[t._t("help",(function(){return[t.help?e("k-text",{staticClass:"k-field-help",attrs:{theme:"help",html:t.help}}):t._e()]}))],2):t._e()]}))],2)}),[],!1,null,null,null,null).exports;const jt=G({props:{config:Object,disabled:Boolean,fields:{type:[Array,Object],default:()=>[]},novalidate:{type:Boolean,default:!1},value:{type:Object,default:()=>({})}},data:()=>({errors:{}}),methods:{focus(t){if(t)return void(this.hasField(t)&&"function"==typeof this.$refs[t][0].focus&&this.$refs[t][0].focus());const e=Object.keys(this.$refs)[0];this.focus(e)},hasFieldType(t){return this.$helper.isComponent(`k-${t}-field`)},hasField(t){var e;return null==(e=this.$refs[t])?void 0:e[0]},onInvalid(t,e,s,n){this.errors[n]=e,this.$emit("invalid",this.errors)},onInput(t,e,s){const n={...this.value,[s]:t};this.$emit("input",n,e,s)},hasErrors(){return Object.keys(this.errors).length}}},(function(){var t=this,e=t._self._c;return e("fieldset",{staticClass:"k-fieldset"},[e("k-grid",[t._l(t.fields,(function(s,n){return[t.$helper.field.isVisible(s,t.value)?e("k-column",{key:s.signature,attrs:{width:s.width}},[e("k-error-boundary",[t.hasFieldType(s.type)?e("k-"+s.type+"-field",t._b({ref:n,refInFor:!0,tag:"component",attrs:{disabled:t.disabled||s.disabled,"form-data":t.value,name:n,novalidate:t.novalidate},on:{input:function(e){return t.$emit("input",t.value,s,n)},focus:function(e){return t.$emit("focus",e,s,n)},invalid:(e,i)=>t.onInvalid(e,i,s,n),submit:function(e){return t.$emit("submit",e,s,n)}},model:{value:t.value[n],callback:function(e){t.$set(t.value,n,e)},expression:"value[fieldName]"}},"component",s,!1)):e("k-box",{attrs:{theme:"negative"}},[e("k-text",{attrs:{size:"small"}},[t._v(" "+t._s(t.$t("error.field.type.missing",{name:n,type:s.type}))+" ")])],1)],1)],1):t._e()]}))],2)],1)}),[],!1,null,null,null,null).exports,Mt={mixins:[vt,$t,_t,St],props:{autofocus:Boolean,type:String,icon:[String,Boolean],theme:String,novalidate:{type:Boolean,default:!1},value:{type:[String,Boolean,Number,Object,Array],default:null}}};const Et=G({mixins:[Mt],inheritAttrs:!1,data(){return{isInvalid:this.invalid,listeners:{...this.$listeners,invalid:(t,e)=>{this.isInvalid=t,this.$emit("invalid",t,e)}}}},computed:{inputProps(){return{...this.$props,...this.$attrs}}},methods:{blur(t){(null==t?void 0:t.relatedTarget)&&!1===this.$el.contains(t.relatedTarget)&&this.trigger(null,"blur")},focus(t){this.trigger(t,"focus")},select(t){this.trigger(t,"select")},trigger(t,e){var s,n,i;if("INPUT"===(null==(s=null==t?void 0:t.target)?void 0:s.tagName)&&"function"==typeof(null==(n=null==t?void 0:t.target)?void 0:n[e]))return void t.target[e]();if("function"==typeof(null==(i=this.$refs.input)?void 0:i[e]))return void this.$refs.input[e]();const o=this.$el.querySelector("input, select, textarea");"function"==typeof(null==o?void 0:o[e])&&o[e]()}}},(function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-input",attrs:{"data-disabled":t.disabled,"data-invalid":!t.novalidate&&t.isInvalid,"data-theme":t.theme,"data-type":t.type}},[t.$slots.before||t.before?e("span",{staticClass:"k-input-before",on:{click:t.focus}},[t._t("before",(function(){return[t._v(t._s(t.before))]}))],2):t._e(),e("span",{staticClass:"k-input-element",on:{click:function(e){return e.stopPropagation(),t.focus.apply(null,arguments)}}},[t._t("default",(function(){return[e("k-"+t.type+"-input",t._g(t._b({ref:"input",tag:"component",attrs:{value:t.value}},"component",t.inputProps,!1),t.listeners))]}))],2),t.$slots.after||t.after?e("span",{staticClass:"k-input-after",on:{click:t.focus}},[t._t("after",(function(){return[t._v(t._s(t.after))]}))],2):t._e(),t.$slots.icon||t.icon?e("span",{staticClass:"k-input-icon",on:{click:t.focus}},[t._t("icon",(function(){return[e("k-icon",{attrs:{type:t.icon}})]}))],2):t._e()])}),[],!1,null,null,null,null).exports;const Lt=G({props:{methods:Array},data:()=>({currentForm:null,isLoading:!1,user:{email:"",password:"",remember:!1}}),computed:{canToggle(){return null!==this.codeMode&&!0===this.methods.includes("password")&&(!0===this.methods.includes("password-reset")||!0===this.methods.includes("code"))},codeMode(){return!0===this.methods.includes("password-reset")?"password-reset":!0===this.methods.includes("code")?"code":null},fields(){let t={email:{autofocus:!0,label:this.$t("email"),type:"email",required:!0,link:!1}};return"email-password"===this.form&&(t.password={label:this.$t("password"),type:"password",minLength:8,required:!0,autocomplete:"current-password",counter:!1}),t},form(){return this.currentForm?this.currentForm:"password"===this.methods[0]?"email-password":"email"},isResetForm(){return"password-reset"===this.codeMode&&"email"===this.form},toggleText(){return this.$t("login.toggleText."+this.codeMode+"."+this.formOpposite(this.form))}},methods:{formOpposite:t=>"email-password"===t?"email":"email-password",async login(){this.$emit("error",null),this.isLoading=!0;let t=Object.assign({},this.user);"email"===this.currentForm&&(t.password=null),!0===this.isResetForm&&(t.remember=!1);try{await this.$api.auth.login(t),this.$reload({globals:["$system","$translation"]})}catch(e){this.$emit("error",e)}finally{this.isLoading=!1}},toggleForm(){this.currentForm=this.formOpposite(this.form),this.$refs.fieldset.focus("email")}}},(function(){var t=this,e=t._self._c;return e("form",{staticClass:"k-login-form",on:{submit:function(e){return e.preventDefault(),t.login.apply(null,arguments)}}},[e("div",{staticClass:"k-login-fields"},[!0===t.canToggle?e("button",{staticClass:"k-login-toggler",attrs:{type:"button"},on:{click:t.toggleForm}},[t._v(" "+t._s(t.toggleText)+" ")]):t._e(),e("k-fieldset",{ref:"fieldset",attrs:{novalidate:!0,fields:t.fields,value:t.user},on:{input:function(e){t.user=e}}})],1),e("div",{staticClass:"k-login-buttons"},[!1===t.isResetForm?e("span",{staticClass:"k-login-checkbox"},[e("k-checkbox-input",{attrs:{value:t.user.remember,label:t.$t("login.remember")},on:{input:function(e){t.user.remember=e}}})],1):t._e(),e("k-button",{staticClass:"k-login-button",attrs:{icon:"check",type:"submit"}},[t._v(" "+t._s(t.$t("login"+(t.isResetForm?".reset":"")))+" "),t.isLoading?[t._v(" … ")]:t._e()],2)],1)])}),[],!1,null,null,null,null).exports;const Bt=G({props:{methods:Array,pending:Object},data:()=>({code:"",isLoadingBack:!1,isLoadingLogin:!1}),computed:{mode(){return!0===this.methods.includes("password-reset")?"password-reset":"login"}},methods:{async back(){this.isLoadingBack=!0,this.$go("/logout")},async login(){this.$emit("error",null),this.isLoadingLogin=!0;try{await this.$api.auth.verifyCode(this.code),this.$store.dispatch("notification/success",this.$t("welcome")),"password-reset"===this.mode?this.$go("reset-password"):this.$reload()}catch(t){this.$emit("error",t)}finally{this.isLoadingLogin=!1}}}},(function(){var t=this,e=t._self._c;return e("form",{staticClass:"k-login-form k-login-code-form",on:{submit:function(e){return e.preventDefault(),t.login.apply(null,arguments)}}},[e("k-user-info",{attrs:{user:t.pending.email}}),e("k-text-field",{attrs:{autofocus:!0,counter:!1,help:t.$t("login.code.text."+t.pending.challenge),label:t.$t("login.code.label."+t.mode),novalidate:!0,placeholder:t.$t("login.code.placeholder."+t.pending.challenge),required:!0,value:t.code,autocomplete:"one-time-code",icon:"unlock",name:"code"},on:{input:function(e){t.code=e}}}),e("div",{staticClass:"k-login-buttons"},[e("k-button",{staticClass:"k-login-button k-login-back-button",attrs:{icon:"angle-left"},on:{click:t.back}},[t._v(" "+t._s(t.$t("back"))+" "),t.isLoadingBack?[t._v(" … ")]:t._e()],2),e("k-button",{staticClass:"k-login-button",attrs:{icon:"check",type:"submit"}},[t._v(" "+t._s(t.$t("login"+("password-reset"===t.mode?".reset":"")))+" "),t.isLoadingLogin?[t._v(" … ")]:t._e()],2)],1)],1)}),[],!1,null,null,null,null).exports;const Dt=G({props:{display:{type:String,default:"HH:mm"},value:String},computed:{day(){return this.formatTimes([6,7,8,9,10,11,"-",12,13,14,15,16,17])},night(){return this.formatTimes([18,19,20,21,22,23,"-",0,1,2,3,4,5])}},methods:{formatTimes(t){return t.map((t=>{if("-"===t)return t;const e=this.$library.dayjs(t+":00","H:mm");return{display:e.format(this.display),select:e.toISO("time")}}))},select(t){this.$emit("input",t)}}},(function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-times"},[e("div",{staticClass:"k-times-slot"},[e("k-icon",{attrs:{type:"sun"}}),e("ul",t._l(t.day,(function(s){return e("li",{key:s.select},["-"===s?e("hr"):e("k-button",{on:{click:function(e){return t.select(s.select)}}},[t._v(t._s(s.display))])],1)})),0)],1),e("div",{staticClass:"k-times-slot"},[e("k-icon",{attrs:{type:"moon"}}),e("ul",t._l(t.night,(function(s){return e("li",{key:s.select},["-"===s?e("hr"):e("k-button",{on:{click:function(e){return t.select(s.select)}}},[t._v(t._s(s.display))])],1)})),0)],1)])}),[],!1,null,null,null,null).exports;const Pt=G({props:{accept:{type:String,default:"*"},attributes:{type:Object},max:{type:Number},method:{type:String,default:"POST"},multiple:{type:Boolean,default:!0},url:{type:String}},data(){return{options:this.$props,completed:{},errors:[],files:[],total:0}},computed:{limit(){return!1===this.options.multiple?1:this.options.max}},methods:{open(t){this.params(t),setTimeout((()=>{this.$refs.input.click()}),1)},params(t){this.options=Object.assign({},this.$props,t)},select(t){this.upload(t.target.files)},drop(t,e){this.params(e),this.upload(t)},upload(t){this.$refs.dialog.open(),this.files=[...t],this.completed={},this.errors=[],this.hasErrors=!1,this.limit&&(this.files=this.files.slice(0,this.limit)),this.total=this.files.length,this.files.forEach((t=>{var e,s;this.$helper.upload(t,{url:this.options.url,attributes:this.options.attributes,method:this.options.method,headers:{"X-CSRF":window.panel.$system.csrf},progress:(t,e,s)=>{var n,i;null==(i=null==(n=this.$refs[e.name])?void 0:n[0])||i.set(s)},success:(t,e,s)=>{this.complete(e,s.data)},error:(t,e,s)=>{this.errors.push({file:e,message:s.message}),this.complete(e,s.data)}}),void 0!==(null==(s=null==(e=this.options)?void 0:e.attributes)?void 0:s.sort)&&this.options.attributes.sort++}))},complete(t,e){if(this.completed[t.name]=e,Object.keys(this.completed).length==this.total){if(this.$refs.input.value="",this.errors.length>0)return this.$forceUpdate(),void this.$emit("error",this.files);setTimeout((()=>{this.$refs.dialog.close(),this.$emit("success",this.files,Object.values(this.completed))}),250)}}}},(function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-upload"},[e("input",{ref:"input",attrs:{accept:t.options.accept,multiple:t.options.multiple,"aria-hidden":"true",type:"file",tabindex:"-1"},on:{change:t.select,click:function(t){t.stopPropagation()}}}),e("k-dialog",{ref:"dialog",staticClass:"k-upload-dialog",attrs:{"cancel-button":!1,"submit-button":!1,size:"medium"},scopedSlots:t._u([{key:"footer",fn:function(){return[t.errors.length>0?[e("k-button-group",{attrs:{buttons:[{icon:"check",text:t.$t("confirm"),click:()=>t.$refs.dialog.close()}]}})]:t._e()]},proxy:!0}])},[t.errors.length>0?[e("k-headline",[t._v(t._s(t.$t("upload.errors")))]),e("ul",{staticClass:"k-upload-error-list"},t._l(t.errors,(function(s,n){return e("li",{key:"error-"+n},[e("p",{staticClass:"k-upload-error-filename"},[t._v(" "+t._s(s.file.name)+" ")]),e("p",{staticClass:"k-upload-error-message"},[t._v(" "+t._s(s.message)+" ")])])})),0)]:[e("k-headline",[t._v(t._s(t.$t("upload.progress")))]),e("ul",{staticClass:"k-upload-list"},t._l(t.files,(function(s,n){return e("li",{key:"file-"+n},[e("k-progress",{ref:s.name,refInFor:!0}),e("p",{staticClass:"k-upload-list-filename"},[t._v(" "+t._s(s.name)+" ")]),e("p",[t._v(t._s(t.errors[s.name]))])],1)})),0)]],2)],1)}),[],!1,null,null,null,null).exports;const Nt=t=>({$from:e})=>((t,e)=>{for(let s=t.depth;s>0;s--){const n=t.node(s);if(e(n))return{pos:s>0?t.before(s):0,start:t.start(s),depth:s,node:n}}})(e,t),Rt=t=>e=>{if((t=>t instanceof i)(e)){const{node:s,$from:n}=e;if(((t,e)=>Array.isArray(t)&&t.indexOf(e.type)>-1||e.type===t)(t,s))return{node:s,pos:n.pos,depth:n.depth}}},Ft=(t,e,s={})=>{const n=Rt(e)(t.selection)||Nt((t=>t.type===e))(t.selection);return Object.keys(s).length&&n?n.node.hasMarkup(e,{...n.node.attrs,...s}):!!n};function qt(t=null,e=null){if(!t||!e)return!1;const s=t.parent.childAfter(t.parentOffset);if(!s.node)return!1;const n=s.node.marks.find((t=>t.type===e));if(!n)return!1;let i=t.index(),o=t.start()+s.offset,r=i+1,l=o+s.node.nodeSize;for(;i>0&&n.isInSet(t.parent.child(i-1).marks);)i-=1,o-=t.parent.child(i).nodeSize;for(;r{i=[...i,...t.marks]}));const o=i.find((t=>t.type.name===e.name));return o?o.attrs:{}},getNodeAttrs:function(t,e){const{from:s,to:n}=t.selection;let i=[];t.doc.nodesBetween(s,n,(t=>{i=[...i,t]}));const o=i.reverse().find((t=>t.type.name===e.name));return o?o.attrs:{}},markInputRule:function(e,s,n){return new t(e,((t,e,i,o)=>{const r=n instanceof Function?n(e):n,{tr:l}=t,a=e.length-1;let u=o,c=i;if(e[a]){const n=i+e[0].indexOf(e[a-1]),r=n+e[a-1].length-1,d=n+e[a-1].lastIndexOf(e[a]),p=d+e[a].length,h=function(t,e,s){let n=[];return s.doc.nodesBetween(t,e,((t,e)=>{n=[...n,...t.marks.map((s=>({start:e,end:e+t.nodeSize,mark:s})))]})),n}(i,o,t).filter((t=>{const{excluded:e}=t.mark.type;return e.find((t=>t.name===s.name))})).filter((t=>t.end>n));if(h.length)return!1;pn&&l.delete(n,d),c=n,u=c+e[a].length}return l.addMark(c,u,s.create(r)),l.removeStoredMark(s),l}))},markIsActive:function(t,e){const{from:s,$from:n,to:i,empty:o}=t.selection;return o?!!e.isInSet(t.storedMarks||n.marks()):!!t.doc.rangeHasMark(s,i,e)},markPasteRule:function(t,i,o){const r=(e,s)=>{const l=[];return e.forEach((e=>{var n;if(e.isText){const{text:r,marks:a}=e;let u,c=0;const d=!!a.filter((t=>"link"===t.type.name))[0];for(;!d&&null!==(u=t.exec(r));)if((null==(n=null==s?void 0:s.type)?void 0:n.allowsMarkType(i))&&u[1]){const t=u.index,s=t+u[0].length,n=t+u[0].indexOf(u[1]),r=n+u[1].length,a=o instanceof Function?o(u):o;t>0&&l.push(e.cut(c,t)),l.push(e.cut(n,r).mark(i.create(a).addToSet(e.marks))),c=s}cnew s(r(t.content),t.openStart,t.openEnd)}})},minMax:function(t=0,e=0,s=0){return Math.min(Math.max(parseInt(t,10),e),s)},nodeIsActive:Ft,nodeInputRule:function(e,s,n){return new t(e,((t,e,i,o)=>{const r=n instanceof Function?n(e):n,{tr:l}=t;return e[0]&&l.replaceWith(i-1,o,s.create(r)),l}))},pasteRule:function(t,i,o){const r=e=>{const s=[];return e.forEach((e=>{if(e.isText){const{text:n}=e;let r,l=0;do{if(r=t.exec(n),r){const t=r.index,n=t+r[0].length,a=o instanceof Function?o(r[0]):o;t>0&&s.push(e.cut(l,t)),s.push(e.cut(t,n).mark(i.create(a).addToSet(e.marks))),l=n}}while(r);lnew s(r(t.content),t.openStart,t.openEnd)}})},removeMark:function(t){return(e,s)=>{const{tr:n,selection:i}=e;let{from:o,to:r}=i;const{$from:l,empty:a}=i;if(a){const e=qt(l,t);o=e.from,r=e.to}return n.removeMark(o,r,t),s(n)}},toggleBlockType:function(t,e,s={}){return(n,i,r)=>Ft(n,t,s)?o(e)(n,i,r):o(t,s)(n,i,r)},toggleList:function(t,e){return(s,n,i)=>{const{schema:o,selection:a}=s,{$from:u,$to:c}=a,d=u.blockRange(c);if(!d)return!1;const p=Nt((t=>Yt(t,o)))(a);if(d.depth>=1&&p&&d.depth-p.depth<=1){if(p.node.type===t)return r(e)(s,n,i);if(Yt(p.node,o)&&t.validContent(p.node.content)){const{tr:e}=s;return e.setNodeMarkup(p.pos,t),n&&n(e),!1}}return l(t)(s,n,i)}},updateMark:function(t,e){return(s,n)=>{const{tr:i,selection:o,doc:r}=s,{ranges:l,empty:a}=o;if(a){const{from:s,to:n}=qt(o.$from,t);r.rangeHasMark(s,n,t)&&i.removeMark(s,n,t),i.addMark(s,n,t.create(e))}else l.forEach((s=>{const{$to:n,$from:o}=s;r.rangeHasMark(o.pos,n.pos,t)&&i.removeMark(o.pos,n.pos,t),i.addMark(o.pos,n.pos,t.create(e))}));return n(i)}}};class Ht{emit(t,...e){this._callbacks=this._callbacks||{};const s=this._callbacks[t];return s&&s.forEach((t=>t.apply(this,e))),this}off(t,e){if(arguments.length){const s=this._callbacks?this._callbacks[t]:null;s&&(e?this._callbacks[t]=s.filter((t=>t!==e)):delete this._callbacks[t])}else this._callbacks={};return this}on(t,e){return this._callbacks=this._callbacks||{},this._callbacks[t]=this._callbacks[t]||[],this._callbacks[t].push(e),this}}class Ut{constructor(t=[],e){t.forEach((t=>{t.bindEditor(e),t.init()})),this.extensions=t}commands({schema:t,view:e}){return this.extensions.filter((t=>t.commands)).reduce(((s,n)=>{const{name:i,type:o}=n,r={},l=n.commands({schema:t,utils:zt,...["node","mark"].includes(o)?{type:t[`${o}s`][i]}:{}}),a=(t,s)=>{r[t]=t=>{if("function"!=typeof s||!e.editable)return!1;e.focus();const n=s(t);return"function"==typeof n?n(e.state,e.dispatch,e):n}};return"object"==typeof l?Object.entries(l).forEach((([t,e])=>{a(t,e)})):a(i,l),{...s,...r}}),{})}buttons(t="mark"){const e={};return this.extensions.filter((e=>e.type===t)).filter((t=>t.button)).forEach((t=>{Array.isArray(t.button)?t.button.forEach((t=>{e[t.id||t.name]=t})):e[t.name]=t.button})),e}getAllowedExtensions(t){return t instanceof Array||!t?t instanceof Array?this.extensions.filter((e=>!t.includes(e.name))):this.extensions:[]}getFromExtensions(t,e,s=this.extensions){return s.filter((t=>["extension"].includes(t.type))).filter((e=>e[t])).map((s=>s[t]({...e,utils:zt})))}getFromNodesAndMarks(t,e,s=this.extensions){return s.filter((t=>["node","mark"].includes(t.type))).filter((e=>e[t])).map((s=>s[t]({...e,type:e.schema[`${s.type}s`][s.name],utils:zt})))}inputRules({schema:t,excludedExtensions:e}){const s=this.getAllowedExtensions(e);return[...this.getFromExtensions("inputRules",{schema:t},s),...this.getFromNodesAndMarks("inputRules",{schema:t},s)].reduce(((t,e)=>[...t,...e]),[])}keymaps({schema:t}){return[...this.getFromExtensions("keys",{schema:t}),...this.getFromNodesAndMarks("keys",{schema:t})].map((t=>g(t)))}get marks(){return this.extensions.filter((t=>"mark"===t.type)).reduce(((t,{name:e,schema:s})=>({...t,[e]:s})),{})}get nodes(){return this.extensions.filter((t=>"node"===t.type)).reduce(((t,{name:e,schema:s})=>({...t,[e]:s})),{})}get options(){const{view:t}=this;return this.extensions.reduce(((e,s)=>({...e,[s.name]:new Proxy(s.options,{set(e,s,n){const i=e[s]!==n;return Object.assign(e,{[s]:n}),i&&t.updateState(t.state),!0}})})),{})}pasteRules({schema:t,excludedExtensions:e}){const s=this.getAllowedExtensions(e);return[...this.getFromExtensions("pasteRules",{schema:t},s),...this.getFromNodesAndMarks("pasteRules",{schema:t},s)].reduce(((t,e)=>[...t,...e]),[])}plugins({schema:t}){return[...this.getFromExtensions("plugins",{schema:t}),...this.getFromNodesAndMarks("plugins",{schema:t})].reduce(((t,e)=>[...t,...e]),[]).map((t=>t instanceof e?t:new e(t)))}}class Vt{constructor(t={}){this.options={...this.defaults,...t}}init(){return null}bindEditor(t=null){this.editor=t}get name(){return null}get type(){return"extension"}get defaults(){return{}}plugins(){return[]}inputRules(){return[]}pasteRules(){return[]}keys(){return{}}}class Kt extends Vt{constructor(t={}){super(t)}get type(){return"node"}get schema(){return null}commands(){return{}}}class Jt extends Kt{get defaults(){return{inline:!1}}get name(){return"doc"}get schema(){return{content:this.options.inline?"inline*":"block+"}}}class Gt extends Kt{get button(){return{id:this.name,icon:"paragraph",label:window.panel.$t("toolbar.button.paragraph"),name:this.name}}commands({utils:t,type:e}){return{paragraph:()=>t.setBlockType(e)}}get schema(){return{content:"inline*",group:"block",draggable:!1,parseDOM:[{tag:"p"}],toDOM:()=>["p",0]}}get name(){return"paragraph"}}let Wt=class extends Kt{get name(){return"text"}get schema(){return{group:"inline"}}};class Xt extends Ht{constructor(t={}){super(),this.defaults={autofocus:!1,content:"",disableInputRules:!1,disablePasteRules:!1,editable:!0,element:null,extensions:[],emptyDocument:{type:"doc",content:[]},events:{},inline:!1,parseOptions:{},topNode:"doc",useBuiltInExtensions:!0},this.init(t)}blur(){this.view.dom.blur()}get builtInExtensions(){return this.options.useBuiltInExtensions?[new Jt({inline:this.options.inline}),new Wt,new Gt]:[]}buttons(t){return this.extensions.buttons(t)}clearContent(t=!1){this.setContent(this.options.emptyDocument,t)}command(t,...e){this.commands[t]&&this.commands[t](...e)}createCommands(){return this.extensions.commands({schema:this.schema,view:this.view})}createDocument(t,e=this.options.parseOptions){if(null===t)return this.schema.nodeFromJSON(this.options.emptyDocument);if("object"==typeof t)try{return this.schema.nodeFromJSON(t)}catch(s){return window.console.warn("Invalid content.","Passed value:",t,"Error:",s),this.schema.nodeFromJSON(this.options.emptyDocument)}if("string"==typeof t){const s=`
${t}
`,n=(new window.DOMParser).parseFromString(s,"text/html").body.firstElementChild;return k.fromSchema(this.schema).parse(n,e)}return!1}createEvents(){const t=this.options.events||{};return Object.entries(t).forEach((([t,e])=>{this.on(t,e)})),t}createExtensions(){return new Ut([...this.builtInExtensions,...this.options.extensions],this)}createFocusEvents(){const t=(t,e,s=!0)=>{this.focused=s,this.emit(s?"focus":"blur",{event:e,state:t.state,view:t});const n=this.state.tr.setMeta("focused",s);this.view.dispatch(n)};return new e({props:{attributes:{tabindex:0},handleDOMEvents:{focus:(e,s)=>{t(e,s,!0)},blur:(e,s)=>{t(e,s,!1)}}}})}createInputRules(){return this.extensions.inputRules({schema:this.schema,excludedExtensions:this.options.disableInputRules})}createKeymaps(){return this.extensions.keymaps({schema:this.schema})}createMarks(){return this.extensions.marks}createNodes(){return this.extensions.nodes}createPasteRules(){return this.extensions.pasteRules({schema:this.schema,excludedExtensions:this.options.disablePasteRules})}createPlugins(){return this.extensions.plugins({schema:this.schema})}createSchema(){return new b({topNode:this.options.topNode,nodes:this.nodes,marks:this.marks})}createState(){return v.create({schema:this.schema,doc:this.createDocument(this.options.content),plugins:[...this.plugins,y({rules:this.inputRules}),...this.pasteRules,...this.keymaps,g({Backspace:x}),g(S),this.createFocusEvents()]})}createView(){return new $(this.element,{dispatchTransaction:this.dispatchTransaction.bind(this),editable:()=>this.options.editable,handlePaste:(t,e)=>{if("function"==typeof this.events.paste){const t=e.clipboardData.getData("text/html"),s=e.clipboardData.getData("text/plain");if(!0===this.events.paste(e,t,s))return!0}},handleDrop:(...t)=>{this.emit("drop",...t)},state:this.createState()})}destroy(){this.view&&this.view.destroy()}dispatchTransaction(t){const e=this.state,s=this.state.apply(t);this.view.updateState(s),this.selection={from:this.state.selection.from,to:this.state.selection.to},this.setActiveNodesAndMarks();const n={editor:this,getHTML:this.getHTML.bind(this),getJSON:this.getJSON.bind(this),state:this.state,transaction:t};this.emit("transaction",n),!t.docChanged&&t.getMeta("preventUpdate")||this.emit("update",n);const{from:i,to:o}=this.state.selection,r=!e||!e.selection.eq(s.selection);this.emit(s.selection.empty?"deselect":"select",{...n,from:i,hasChanged:r,to:o})}focus(t=null){if(this.view.focused&&null===t||!1===t)return;const{from:e,to:s}=this.selectionAtPosition(t);this.setSelection(e,s),setTimeout((()=>this.view.focus()),10)}getHTML(){const t=document.createElement("div"),e=_.fromSchema(this.schema).serializeFragment(this.state.doc.content);return t.appendChild(e),this.options.inline&&t.querySelector("p")?t.querySelector("p").innerHTML:t.innerHTML}getJSON(){return this.state.doc.toJSON()}getMarkAttrs(t=null){return this.activeMarkAttrs[t]}getSchemaJSON(){return JSON.parse(JSON.stringify({nodes:this.nodes,marks:this.marks}))}init(t={}){this.options={...this.defaults,...t},this.element=this.options.element,this.focused=!1,this.selection={from:0,to:0},this.events=this.createEvents(),this.extensions=this.createExtensions(),this.nodes=this.createNodes(),this.marks=this.createMarks(),this.schema=this.createSchema(),this.keymaps=this.createKeymaps(),this.inputRules=this.createInputRules(),this.pasteRules=this.createPasteRules(),this.plugins=this.createPlugins(),this.view=this.createView(),this.commands=this.createCommands(),this.setActiveNodesAndMarks(),!1!==this.options.autofocus&&this.focus(this.options.autofocus),this.emit("init",{view:this.view,state:this.state}),this.extensions.view=this.view,this.setContent(this.options.content,!0)}isEditable(){return this.options.editable}isEmpty(){if(this.state)return 0===this.state.doc.textContent.length}get isActive(){return Object.entries({...this.activeMarks,...this.activeNodes}).reduce(((t,[e,s])=>({...t,[e]:(t={})=>s(t)})),{})}removeMark(t){if(this.schema.marks[t])return zt.removeMark(this.schema.marks[t])(this.state,this.view.dispatch)}selectionAtPosition(t=null){if(this.selection&&null===t)return this.selection;if("start"===t||!0===t)return{from:0,to:0};if("end"===t){const{doc:t}=this.state;return{from:t.content.size,to:t.content.size}}return{from:t,to:t}}setActiveNodesAndMarks(){this.activeMarks=Object.values(this.schema.marks).filter((t=>zt.markIsActive(this.state,t))).map((t=>t.name)),this.activeMarkAttrs=Object.entries(this.schema.marks).reduce(((t,[e,s])=>({...t,[e]:zt.getMarkAttrs(this.state,s)})),{}),this.activeNodes=Object.values(this.schema.nodes).filter((t=>zt.nodeIsActive(this.state,t))).map((t=>t.name)),this.activeNodeAttrs=Object.entries(this.schema.nodes).reduce(((t,[e,s])=>({...t,[e]:zt.getNodeAttrs(this.state,s)})),{})}setContent(t={},e=!1,s){const{doc:n,tr:i}=this.state,o=this.createDocument(t,s),r=i.replaceWith(0,n.content.size,o).setMeta("preventUpdate",!e);this.view.dispatch(r)}setSelection(t=0,e=0){const{doc:s,tr:n}=this.state,i=zt.minMax(t,0,s.content.size),o=zt.minMax(e,0,s.content.size),r=w.create(s,i,o),l=n.setSelection(r);this.view.dispatch(l)}get state(){return this.view?this.view.state:null}toggleMark(t){if(this.schema.marks[t])return zt.toggleMark(this.schema.marks[t])(this.state,this.view.dispatch)}updateMark(t,e){if(this.schema.marks[t])return zt.updateMark(this.schema.marks[t],e)(this.state,this.view.dispatch)}}const Zt=G({data:()=>({link:{href:null,title:null,target:!1}}),computed:{fields(){return{href:{label:this.$t("url"),type:"text",icon:"url"},title:{label:this.$t("title"),type:"text",icon:"title"},target:{label:this.$t("open.newWindow"),type:"toggle",text:[this.$t("no"),this.$t("yes")]}}}},methods:{open(t){this.link={title:null,target:!1,...t},this.link.target=Boolean(this.link.target),this.$refs.dialog.open()},submit(){this.$emit("submit",{...this.link,target:this.link.target?"_blank":null}),this.$refs.dialog.close()}}},(function(){var t=this;return(0,t._self._c)("k-form-dialog",{ref:"dialog",attrs:{fields:t.fields,"submit-button":t.$t("confirm"),value:t.link,size:"medium"},on:{close:function(e){return t.$emit("close")},input:function(e){t.link=e},submit:t.submit}})}),[],!1,null,null,null,null).exports;const Qt=G({data:()=>({email:{email:null,title:null}}),computed:{fields(){return{href:{label:this.$t("email"),type:"email",icon:"email"},title:{label:this.$t("title"),type:"text",icon:"title"}}}},methods:{open(t){this.email={title:null,...t},this.$refs.dialog.open()},submit(){this.$emit("submit",this.email),this.$refs.dialog.close()}}},(function(){var t=this;return(0,t._self._c)("k-form-dialog",{ref:"dialog",attrs:{fields:t.fields,"submit-button":t.$t("confirm"),value:t.email,size:"medium"},on:{input:function(e){t.email=e},close:function(e){return t.$emit("close")},submit:t.submit}})}),[],!1,null,null,null,null).exports;class te extends Vt{constructor(t={}){super(t)}command(){return()=>{}}remove(){this.editor.removeMark(this.name)}get schema(){return null}get type(){return"mark"}toggle(){return this.editor.toggleMark(this.name)}update(t){this.editor.updateMark(this.name,t)}}let ee=class extends te{get button(){return{icon:"url",label:window.panel.$t("toolbar.button.link")}}commands(){return{link:()=>{this.editor.emit("link",this.editor)},insertLink:(t={})=>{if(t.href)return this.update(t)},removeLink:()=>this.remove(),toggleLink:(t={})=>{var e;(null==(e=t.href)?void 0:e.length)>0?this.editor.command("insertLink",t):this.editor.command("removeLink")}}}get defaults(){return{target:null}}get name(){return"link"}pasteRules({type:t,utils:e}){return[e.pasteRule(/https?:\/\/(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z]{2,}\b([-a-zA-Z0-9@:%_+.~#?&//=,]*)/gi,t,(t=>({href:t})))]}plugins(){return[{props:{handleClick:(t,e,s)=>{const n=this.editor.getMarkAttrs("link");n.href&&!0===s.altKey&&s.target instanceof HTMLAnchorElement&&(s.stopPropagation(),window.open(n.href,n.target))}}}]}get schema(){return{attrs:{href:{default:null},target:{default:null},title:{default:null}},inclusive:!1,parseDOM:[{tag:"a[href]:not([href^='mailto:'])",getAttrs:t=>({href:t.getAttribute("href"),target:t.getAttribute("target"),title:t.getAttribute("title")})}],toDOM:t=>["a",{...t.attrs,rel:"noreferrer"},0]}}},se=class extends te{get button(){return{icon:"code",label:window.panel.$t("toolbar.button.code")}}commands(){return()=>this.toggle()}inputRules({type:t,utils:e}){return[e.markInputRule(/(?:`)([^`]+)(?:`)$/,t)]}keys(){return{"Mod-`":()=>this.toggle()}}get name(){return"code"}pasteRules({type:t,utils:e}){return[e.markPasteRule(/(?:`)([^`]+)(?:`)/g,t)]}get schema(){return{excludes:"_",parseDOM:[{tag:"code"}],toDOM:()=>["code",0]}}};class ne extends te{get button(){return{icon:"bold",label:window.panel.$t("toolbar.button.bold")}}commands(){return()=>this.toggle()}inputRules({type:t,utils:e}){return[e.markInputRule(/(?:\*\*|__)([^*_]+)(?:\*\*|__)$/,t)]}keys(){return{"Mod-b":()=>this.toggle()}}get name(){return"bold"}pasteRules({type:t,utils:e}){return[e.markPasteRule(/(?:\*\*|__)([^*_]+)(?:\*\*|__)/g,t)]}get schema(){return{parseDOM:[{tag:"strong"},{tag:"b",getAttrs:t=>"normal"!==t.style.fontWeight&&null},{style:"font-weight",getAttrs:t=>/^(bold(er)?|[5-9]\d{2,})$/.test(t)&&null}],toDOM:()=>["strong",0]}}}class ie extends te{get button(){return{icon:"italic",label:window.panel.$t("toolbar.button.italic")}}commands(){return()=>this.toggle()}inputRules({type:t,utils:e}){return[e.markInputRule(/(?:^|\s)((?:\*)((?:[^*]+))(?:\*))$/,t),e.markInputRule(/(?:^|\s)((?:_)((?:[^_]+))(?:_))$/,t)]}keys(){return{"Mod-i":()=>this.toggle()}}get name(){return"italic"}pasteRules({type:t,utils:e}){return[e.markPasteRule(/_([^_]+)_/g,t),e.markPasteRule(/\*([^*]+)\*/g,t)]}get schema(){return{parseDOM:[{tag:"i"},{tag:"em"},{style:"font-style=italic"}],toDOM:()=>["em",0]}}}class oe extends te{get button(){return{icon:"email",label:"Email"}}commands(){return{email:()=>{this.editor.emit("email")},insertEmail:(t={})=>{if(t.href)return this.update(t)},removeEmail:()=>this.remove(),toggleEmail:(t={})=>{var e;(null==(e=t.href)?void 0:e.length)>0?this.editor.command("insertEmail",t):this.editor.command("removeEmail")}}}get defaults(){return{target:null}}get name(){return"email"}pasteRules({type:t,utils:e}){return[e.pasteRule(/^[\w-.]+@([\w-]+\.)+[\w-]{2,4}$/gi,t,(t=>({href:t})))]}plugins(){return[{props:{handleClick:(t,e,s)=>{const n=this.editor.getMarkAttrs("email");n.href&&!0===s.altKey&&s.target instanceof HTMLAnchorElement&&(s.stopPropagation(),window.open(n.href))}}}]}get schema(){return{attrs:{href:{default:null},title:{default:null}},inclusive:!1,parseDOM:[{tag:"a[href^='mailto:']",getAttrs:t=>({href:t.getAttribute("href").replace("mailto:",""),title:t.getAttribute("title")})}],toDOM:t=>["a",{...t.attrs,href:"mailto:"+t.attrs.href},0]}}}class re extends te{get button(){return{icon:"strikethrough",label:window.panel.$t("toolbar.button.strike")}}commands(){return()=>this.toggle()}inputRules({type:t,utils:e}){return[e.markInputRule(/~([^~]+)~$/,t)]}keys(){return{"Mod-d":()=>this.toggle()}}get name(){return"strike"}pasteRules({type:t,utils:e}){return[e.markPasteRule(/~([^~]+)~/g,t)]}get schema(){return{parseDOM:[{tag:"s"},{tag:"del"},{tag:"strike"},{style:"text-decoration",getAttrs:t=>"line-through"===t}],toDOM:()=>["s",0]}}}class le extends te{get button(){return{icon:"underline",label:window.panel.$t("toolbar.button.underline")}}commands(){return()=>this.toggle()}keys(){return{"Mod-u":()=>this.toggle()}}get name(){return"underline"}get schema(){return{parseDOM:[{tag:"u"},{style:"text-decoration",getAttrs:t=>"underline"===t}],toDOM:()=>["u",0]}}}class ae extends Kt{get button(){return{id:this.name,icon:"list-bullet",label:window.panel.$t("toolbar.button.ul"),name:this.name,when:["listItem","bulletList","orderedList"]}}commands({type:t,schema:e,utils:s}){return()=>s.toggleList(t,e.nodes.listItem)}inputRules({type:t,utils:e}){return[e.wrappingInputRule(/^\s*([-+*])\s$/,t)]}keys({type:t,schema:e,utils:s}){return{"Shift-Ctrl-8":s.toggleList(t,e.nodes.listItem)}}get name(){return"bulletList"}get schema(){return{content:"listItem+",group:"block",parseDOM:[{tag:"ul"}],toDOM:()=>["ul",0]}}}class ue extends Kt{commands({utils:t,type:e}){return()=>this.createHardBreak(t,e)}createHardBreak(t,e){return t.chainCommands(t.exitCode,((t,s)=>(s(t.tr.replaceSelectionWith(e.create()).scrollIntoView()),!0)))}get defaults(){return{enter:!1,text:!1}}keys({utils:t,type:e}){const s=this.createHardBreak(t,e);let n={"Mod-Enter":s,"Shift-Enter":s};return this.options.enter&&(n.Enter=s),n}get name(){return"hardBreak"}get schema(){return{inline:!0,group:"inline",selectable:!1,parseDOM:[{tag:"br"}],toDOM:()=>["br"]}}}let ce=class extends Kt{get button(){return this.options.levels.map((t=>({id:`h${t}`,command:`h${t}`,icon:`h${t}`,label:window.panel.$t("toolbar.button.heading."+t),attrs:{level:t},name:this.name,when:["heading","paragraph"]})))}commands({type:t,schema:e,utils:s}){let n={toggleHeading:n=>s.toggleBlockType(t,e.nodes.paragraph,n)};return this.options.levels.forEach((i=>{n[`h${i}`]=()=>s.toggleBlockType(t,e.nodes.paragraph,{level:i})})),n}get defaults(){return{levels:[1,2,3,4,5,6]}}inputRules({type:t,utils:e}){return this.options.levels.map((s=>e.textblockTypeInputRule(new RegExp(`^(#{1,${s}})\\s$`),t,(()=>({level:s})))))}keys({type:t,utils:e}){return this.options.levels.reduce(((s,n)=>({...s,[`Shift-Ctrl-${n}`]:e.setBlockType(t,{level:n})})),{})}get name(){return"heading"}get schema(){return{attrs:{level:{default:1}},content:"inline*",group:"block",defining:!0,draggable:!1,parseDOM:this.options.levels.map((t=>({tag:`h${t}`,attrs:{level:t}}))),toDOM:t=>[`h${t.attrs.level}`,0]}}};class de extends Kt{commands({type:t}){return()=>(e,s)=>s(e.tr.replaceSelectionWith(t.create()))}inputRules({type:t,utils:e}){return[e.nodeInputRule(/^(?:---|___\s|\*\*\*\s)$/,t)]}get name(){return"horizontalRule"}get schema(){return{group:"block",parseDOM:[{tag:"hr"}],toDOM:()=>["hr"]}}}class pe extends Kt{keys({type:t,utils:e}){return{Enter:e.splitListItem(t),"Shift-Tab":e.liftListItem(t),Tab:e.sinkListItem(t)}}get name(){return"listItem"}get schema(){return{content:"paragraph block*",defining:!0,draggable:!1,parseDOM:[{tag:"li"}],toDOM:()=>["li",0]}}}class he extends Kt{get button(){return{id:this.name,icon:"list-numbers",label:window.panel.$t("toolbar.button.ol"),name:this.name,when:["listItem","bulletList","orderedList"]}}commands({type:t,schema:e,utils:s}){return()=>s.toggleList(t,e.nodes.listItem)}inputRules({type:t,utils:e}){return[e.wrappingInputRule(/^(\d+)\.\s$/,t,(t=>({order:+t[1]})),((t,e)=>e.childCount+e.attrs.order===+t[1]))]}keys({type:t,schema:e,utils:s}){return{"Shift-Ctrl-9":s.toggleList(t,e.nodes.listItem)}}get name(){return"orderedList"}get schema(){return{attrs:{order:{default:1}},content:"listItem+",group:"block",parseDOM:[{tag:"ol",getAttrs:t=>({order:t.hasAttribute("start")?+t.getAttribute("start"):1})}],toDOM:t=>1===t.attrs.order?["ol",0]:["ol",{start:t.attrs.order},0]}}}class me extends Vt{commands(){return{undo:()=>C,redo:()=>O,undoDepth:()=>A,redoDepth:()=>T}}get defaults(){return{depth:"",newGroupDelay:""}}keys(){return{"Mod-z":C,"Mod-y":O,"Shift-Mod-z":O,"Mod-я":C,"Shift-Mod-я":O}}get name(){return"history"}plugins(){return[I({depth:this.options.depth,newGroupDelay:this.options.newGroupDelay})]}}class fe extends Vt{commands(){return{insertHtml:t=>(e,s)=>{let n=document.createElement("div");n.innerHTML=t.trim();const i=k.fromSchema(e.schema).parse(n);s(e.tr.replaceSelectionWith(i).scrollIntoView())}}}}let ge=class extends Vt{constructor(t={}){super(t)}close(){this.visible=!1,this.emit()}emit(){this.editor.emit("toolbar",{marks:this.marks,nodes:this.nodes,nodeAttrs:this.nodeAttrs,position:this.position,visible:this.visible})}init(){this.position={left:0,bottom:0},this.visible=!1,this.editor.on("blur",(()=>{this.close()})),this.editor.on("deselect",(()=>{this.close()})),this.editor.on("select",(({hasChanged:t})=>{!1!==t?this.open():this.emit()}))}get marks(){return this.editor.activeMarks}get nodes(){return this.editor.activeNodes}get nodeAttrs(){return this.editor.activeNodeAttrs}open(){this.visible=!0,this.reposition(),this.emit()}reposition(){const{from:t,to:e}=this.editor.selection,s=this.editor.view.coordsAtPos(t),n=this.editor.view.coordsAtPos(e,!0),i=this.editor.element.getBoundingClientRect();let o=(s.left+n.left)/2-i.left,r=Math.round(i.bottom-s.top);return this.position={bottom:r,left:o}}get type(){return"toolbar"}};const ke=G({props:{activeMarks:{type:Array,default:()=>[]},activeNodes:{type:Array,default:()=>[]},activeNodeAttrs:{type:[Array,Object],default:()=>[]},editor:{type:Object,required:!0},marks:{type:Array},isParagraphNodeHidden:{type:Boolean,default:!1}},computed:{activeButton(){return Object.values(this.nodeButtons).find((t=>this.isButtonActive(t)))||!1},hasVisibleButtons(){const t=Object.keys(this.nodeButtons);return t.length>1||1===t.length&&!1===t.includes("paragraph")},markButtons(){return this.buttons("mark")},nodeButtons(){let t=this.buttons("node");return!0===this.isParagraphNodeHidden&&t.paragraph&&delete t.paragraph,t}},methods:{buttons(t){const e=this.editor.buttons(t);let s=this.sorting;!1!==s&&!1!==Array.isArray(s)||(s=Object.keys(e));let n={};return s.forEach((t=>{e[t]&&(n[t]=e[t])})),n},command(t,...e){this.$emit("command",t,...e)},isButtonActive(t){if("paragraph"===t.name)return 1===this.activeNodes.length&&this.activeNodes.includes(t.name);let e=!0;if(t.attrs){const s=Object.values(this.activeNodeAttrs).find((e=>JSON.stringify(e)===JSON.stringify(t.attrs)));e=Boolean(s||!1)}return!0===e&&this.activeNodes.includes(t.name)},isButtonCurrent(t){return!!this.activeButton&&this.activeButton.id===t.id},isButtonDisabled(t){var e;if(null==(e=this.activeButton)?void 0:e.when){return!1===this.activeButton.when.includes(t.name)}return!1},needDividerAfterNode(t){let e=["paragraph"],s=Object.keys(this.nodeButtons);return(s.includes("bulletList")||s.includes("orderedList"))&&e.push("h6"),e.includes(t.id)}}},(function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-writer-toolbar"},[t.hasVisibleButtons?e("k-dropdown",{nativeOn:{mousedown:function(t){t.preventDefault()}}},[e("k-button",{class:{"k-writer-toolbar-button k-writer-toolbar-nodes":!0,"k-writer-toolbar-button-active":!!t.activeButton},attrs:{icon:t.activeButton.icon||"title"},on:{click:function(e){return t.$refs.nodes.toggle()}}}),e("k-dropdown-content",{ref:"nodes"},[t._l(t.nodeButtons,(function(s,n){return[e("k-dropdown-item",{key:n,attrs:{current:t.isButtonCurrent(s),disabled:t.isButtonDisabled(s),icon:s.icon},on:{click:function(e){return t.command(s.command||n)}}},[t._v(" "+t._s(s.label)+" ")]),t.needDividerAfterNode(s)?e("hr",{key:n+"-divider"}):t._e()]}))],2)],1):t._e(),t._l(t.markButtons,(function(s,n){return e("k-button",{key:n,class:{"k-writer-toolbar-button":!0,"k-writer-toolbar-button-active":t.activeMarks.includes(n)},attrs:{icon:s.icon,tooltip:s.label},on:{mousedown:function(e){return e.preventDefault(),t.command(s.command||n)}}})}))],2)}),[],!1,null,null,null,null).exports,be={props:{autofocus:Boolean,breaks:Boolean,code:Boolean,disabled:Boolean,emptyDocument:{type:Object,default:()=>({type:"doc",content:[]})},headings:[Array,Boolean],inline:{type:Boolean,default:!1},marks:{type:[Array,Boolean],default:!0},nodes:{type:[Array,Boolean],default:()=>["heading","bulletList","orderedList"]},paste:{type:Function,default:()=>()=>!1},placeholder:String,spellcheck:Boolean,extensions:Array,value:{type:String,default:""}}};const ve=G({components:{"k-writer-email-dialog":Qt,"k-writer-link-dialog":Zt,"k-writer-toolbar":ke},mixins:[be],data(){return{editor:null,json:{},html:this.value,isEmpty:!0,toolbar:!1}},computed:{isParagraphNodeHidden(){return!0===Array.isArray(this.nodes)&&3!==this.nodes.length&&!1===this.nodes.includes("paragraph")}},watch:{value(t,e){t!==e&&t!==this.html&&(this.html=t,this.editor.setContent(this.html))}},mounted(){this.editor=new Xt({autofocus:this.autofocus,content:this.value,editable:!this.disabled,element:this.$el,emptyDocument:this.emptyDocument,events:{link:t=>{this.$refs.linkDialog.open(t.getMarkAttrs("link"))},email:()=>{this.$refs.emailDialog.open(this.editor.getMarkAttrs("email"))},paste:this.paste,toolbar:t=>{this.toolbar=t,this.toolbar.visible&&this.$nextTick((()=>{this.onToolbarOpen()}))},update:t=>{if(!this.editor)return;const e=JSON.stringify(this.editor.getJSON());e!==JSON.stringify(this.json)&&(this.json=e,this.isEmpty=t.editor.isEmpty(),this.html=t.editor.getHTML(),this.isEmpty&&(0===t.editor.activeNodes.length||t.editor.activeNodes.includes("paragraph"))&&(this.html=""),this.$emit("input",this.html))}},extensions:[...this.createMarks(),...this.createNodes(),new me,new fe,new ge,...this.extensions||[]],inline:this.inline}),this.isEmpty=this.editor.isEmpty(),this.json=this.editor.getJSON()},beforeDestroy(){this.editor.destroy()},methods:{filterExtensions(t,e,s){!1===e?e=[]:!0!==e&&!1!==Array.isArray(e)||(e=Object.keys(t));let n=[];return e.forEach((e=>{t[e]&&n.push(t[e])})),"function"==typeof s&&(n=s(e,n)),n},command(t,...e){this.editor.command(t,...e)},createMarks(){return this.filterExtensions({link:new ee,bold:new ne,italic:new ie,strike:new re,underline:new le,code:new se,email:new oe},this.marks)},createNodes(){const t=new ue({text:!0,enter:this.inline});return!0===this.inline?[t]:this.filterExtensions({bulletList:new ae,orderedList:new he,heading:new ce,horizontalRule:new de,listItem:new pe},this.nodes,((e,s)=>((e.includes("bulletList")||e.includes("orderedList"))&&s.push(new pe),s.push(t),s)))},getHTML(){return this.editor.getHTML()},focus(){this.editor.focus()},onToolbarOpen(){if(this.$refs.toolbar){const t=this.$el.clientWidth,e=this.$refs.toolbar.$el.clientWidth;let s=this.toolbar.position.left;s-e/2<0&&(s=s+(e/2-s)-20),s+e/2>t&&(s=s-(s+e/2-t)+20),s!==this.toolbar.position.left&&(this.$refs.toolbar.$el.style.left=s+"px")}}}},(function(){var t=this,e=t._self._c;return e("div",{directives:[{name:"direction",rawName:"v-direction"}],ref:"editor",staticClass:"k-writer",attrs:{"data-empty":t.isEmpty,"data-placeholder":t.placeholder,spellcheck:t.spellcheck}},[t.editor?[t.toolbar.visible?e("k-writer-toolbar",{ref:"toolbar",style:{bottom:t.toolbar.position.bottom+"px","inset-inline-start":t.toolbar.position.left+"px"},attrs:{editor:t.editor,"active-marks":t.toolbar.marks,"active-nodes":t.toolbar.nodes,"active-node-attrs":t.toolbar.nodeAttrs,"is-paragraph-node-hidden":t.isParagraphNodeHidden},on:{command:function(e){return t.editor.command(e)}}}):t._e(),e("k-writer-link-dialog",{ref:"linkDialog",on:{close:function(e){return t.editor.focus()},submit:function(e){return t.editor.command("toggleLink",e)}}}),e("k-writer-email-dialog",{ref:"emailDialog",on:{close:function(e){return t.editor.focus()},submit:function(e){return t.editor.command("toggleEmail",e)}}})]:t._e()],2)}),[],!1,null,null,null,null).exports;const ye=G({},(function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-login-alert",on:{click:function(e){return t.$emit("click")}}},[e("span",[t._t("default")],2),e("k-icon",{attrs:{type:"alert"}})],1)}),[],!1,null,null,null,null).exports;const $e=G({props:{fields:Object,index:[Number,String],total:Number,value:Object},mounted(){this.$store.dispatch("content/disable"),this.$events.$on("keydown.cmd.s",this.onSubmit),this.$events.$on("keydown.esc",this.onDiscard)},destroyed(){this.$events.$off("keydown.cmd.s",this.onSubmit),this.$events.$off("keydown.esc",this.onDiscard),this.$store.dispatch("content/enable")},methods:{focus(t){this.$refs.form.focus(t)},onDiscard(){this.$emit("discard")},onInput(t){this.$emit("input",t)},onSubmit(){this.$emit("submit")}}},(function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-structure-form"},[e("div",{staticClass:"k-structure-backdrop",on:{click:t.onDiscard}}),e("section",[e("k-form",{ref:"form",staticClass:"k-structure-form-fields",attrs:{value:t.value,fields:t.fields},on:{input:t.onInput,submit:t.onSubmit}}),e("footer",{staticClass:"k-structure-form-buttons"},[e("k-button",{staticClass:"k-structure-form-cancel-button",attrs:{text:t.$t("cancel"),icon:"cancel"},on:{click:function(e){return t.$emit("close")}}}),"new"!==t.index?e("k-pagination",{attrs:{dropdown:!1,total:t.total,limit:1,page:t.index+1,details:!0},on:{paginate:function(e){return t.$emit("paginate",e)}}}):t._e(),e("k-button",{staticClass:"k-structure-form-submit-button",attrs:{text:t.$t("new"!==t.index?"confirm":"add"),icon:"check"},on:{click:t.onSubmit}})],1)],1)])}),[],!1,null,null,null,null).exports,_e=function(t){this.command("insert",((e,s)=>{let n=[];return s.split("\n").forEach(((e,s)=>{let i="ol"===t?s+1+".":"-";n.push(i+" "+e)})),n.join("\n")}))};const we=G({layout:["headlines","bold","italic","|","link","email","file","|","code","ul","ol"],props:{buttons:{type:[Boolean,Array],default:!0},uploads:[Boolean,Object,Array]},data(){let t={},e={},s=[],n=this.commands();return!1===this.buttons?t:(Array.isArray(this.buttons)&&(s=this.buttons),!0!==Array.isArray(this.buttons)&&(s=this.$options.layout),s.forEach(((s,i)=>{if("|"===s)t["divider-"+i]={divider:!0};else if(n[s]){let i=n[s];t[s]=i,i.shortcut&&(e[i.shortcut]=s)}})),{layout:t,shortcuts:e})},methods:{command(t,e){"function"==typeof t?t.apply(this):this.$emit("command",t,e)},close(){Object.keys(this.$refs).forEach((t=>{const e=this.$refs[t][0];"function"==typeof(null==e?void 0:e.close)&&e.close()}))},fileCommandSetup(){let t={label:this.$t("toolbar.button.file"),icon:"attachment"};return!1===this.uploads?t.command="selectFile":t.dropdown={select:{label:this.$t("toolbar.button.file.select"),icon:"check",command:"selectFile"},upload:{label:this.$t("toolbar.button.file.upload"),icon:"upload",command:"uploadFile"}},t},commands(){return{headlines:{label:this.$t("toolbar.button.headings"),icon:"title",dropdown:{h1:{label:this.$t("toolbar.button.heading.1"),icon:"title",command:"prepend",args:"#"},h2:{label:this.$t("toolbar.button.heading.2"),icon:"title",command:"prepend",args:"##"},h3:{label:this.$t("toolbar.button.heading.3"),icon:"title",command:"prepend",args:"###"}}},bold:{label:this.$t("toolbar.button.bold"),icon:"bold",command:"wrap",args:"**",shortcut:"b"},italic:{label:this.$t("toolbar.button.italic"),icon:"italic",command:"wrap",args:"*",shortcut:"i"},link:{label:this.$t("toolbar.button.link"),icon:"url",shortcut:"k",command:"dialog",args:"link"},email:{label:this.$t("toolbar.button.email"),icon:"email",shortcut:"e",command:"dialog",args:"email"},file:this.fileCommandSetup(),code:{label:this.$t("toolbar.button.code"),icon:"code",command:"wrap",args:"`"},ul:{label:this.$t("toolbar.button.ul"),icon:"list-bullet",command(){return _e.apply(this,["ul"])}},ol:{label:this.$t("toolbar.button.ol"),icon:"list-numbers",command(){return _e.apply(this,["ol"])}}}},shortcut(t,e){if(this.shortcuts[t]){const s=this.layout[this.shortcuts[t]];if(!s)return!1;e.preventDefault(),this.command(s.command,s.args)}}}},(function(){var t=this,e=t._self._c;return e("nav",{staticClass:"k-toolbar"},[e("div",{staticClass:"k-toolbar-wrapper"},[e("div",{staticClass:"k-toolbar-buttons"},[t._l(t.layout,(function(s,n){return[s.divider?[e("span",{key:n,staticClass:"k-toolbar-divider"})]:s.dropdown?[e("k-dropdown",{key:n},[e("k-button",{key:n,staticClass:"k-toolbar-button",attrs:{icon:s.icon,tooltip:s.label,tabindex:"-1"},on:{click:function(e){t.$refs[n+"-dropdown"][0].toggle()}}}),e("k-dropdown-content",{ref:n+"-dropdown",refInFor:!0},t._l(s.dropdown,(function(s,n){return e("k-dropdown-item",{key:n,attrs:{icon:s.icon},on:{click:function(e){return t.command(s.command,s.args)}}},[t._v(" "+t._s(s.label)+" ")])})),1)],1)]:[e("k-button",{key:n,staticClass:"k-toolbar-button",attrs:{icon:s.icon,tooltip:s.label,tabindex:"-1"},on:{click:function(e){return t.command(s.command,s.args)}}})]]}))],2)])])}),[],!1,null,null,null,null).exports;const xe=G({data(){return{value:{email:null,text:null},fields:{email:{label:this.$t("email"),type:"email"},text:{label:this.$t("link.text"),type:"text"}}}},computed:{kirbytext(){return this.$config.kirbytext}},methods:{open(t,e){this.value.text=e,this.$refs.dialog.open()},cancel(){this.$emit("cancel")},createKirbytext(){var t;const e=this.value.email||"";return(null==(t=this.value.text)?void 0:t.length)>0?`(email: ${e} text: ${this.value.text})`:`(email: ${e})`},createMarkdown(){var t;const e=this.value.email||"";return(null==(t=this.value.text)?void 0:t.length)>0?`[${this.value.text}](mailto:${e})`:`<${e}>`},submit(){this.$emit("submit",this.kirbytext?this.createKirbytext():this.createMarkdown()),this.value={email:null,text:null},this.$refs.dialog.close()}}},(function(){var t=this,e=t._self._c;return e("k-dialog",{ref:"dialog",attrs:{"submit-button":t.$t("insert")},on:{close:t.cancel,submit:function(e){return t.$refs.form.submit()}}},[e("k-form",{ref:"form",attrs:{fields:t.fields,value:t.value},on:{input:function(e){t.value=e},submit:t.submit}})],1)}),[],!1,null,null,null,null).exports;const Se=G({data(){return{value:{url:null,text:null},fields:{url:{label:this.$t("link"),type:"text",placeholder:this.$t("url.placeholder"),icon:"url"},text:{label:this.$t("link.text"),type:"text"}}}},computed:{kirbytext(){return this.$config.kirbytext}},methods:{open(t,e){this.value.text=e,this.$refs.dialog.open()},cancel(){this.$emit("cancel")},createKirbytext(){return this.value.text.length>0?`(link: ${this.value.url} text: ${this.value.text})`:`(link: ${this.value.url})`},createMarkdown(){return this.value.text.length>0?`[${this.value.text}](${this.value.url})`:`<${this.value.url}>`},submit(){this.$emit("submit",this.kirbytext?this.createKirbytext():this.createMarkdown()),this.value={url:null,text:null},this.$refs.dialog.close()}}},(function(){var t=this,e=t._self._c;return e("k-dialog",{ref:"dialog",attrs:{"submit-button":t.$t("insert")},on:{close:t.cancel,submit:function(e){return t.$refs.form.submit()}}},[e("k-form",{ref:"form",attrs:{fields:t.fields,value:t.value},on:{input:function(e){t.value=e},submit:t.submit}})],1)}),[],!1,null,null,null,null).exports;const Ce=G({mixins:[yt,_t,xt,Ct,At],inheritAttrs:!1,props:{value:Boolean},watch:{value(){this.onInvalid()}},mounted(){this.onInvalid(),this.$props.autofocus&&this.focus()},methods:{focus(){this.$refs.input.focus()},onChange(t){this.$emit("input",t)},onInvalid(){this.$emit("invalid",this.$v.$invalid,this.$v)},select(){this.focus()}},validations(){return{value:{required:!this.required||j.required}}}},(function(){var t=this,e=t._self._c;return e("label",{staticClass:"k-checkbox-input",on:{click:function(t){t.stopPropagation()}}},[e("input",{ref:"input",staticClass:"k-checkbox-input-native input-hidden",attrs:{id:t.id,disabled:t.disabled,type:"checkbox"},domProps:{checked:t.value},on:{change:function(e){return t.onChange(e.target.checked)}}}),e("span",{staticClass:"k-checkbox-input-icon",attrs:{"aria-hidden":"true"}},[e("svg",{attrs:{width:"12",height:"10",viewBox:"0 0 12 10",xmlns:"http://www.w3.org/2000/svg"}},[e("path",{attrs:{d:"M1 5l3.3 3L11 1","stroke-width":"2",fill:"none","fill-rule":"evenodd"}})])]),e("span",{staticClass:"k-checkbox-input-label",domProps:{innerHTML:t._s(t.label)}})])}),[],!1,null,null,null,null).exports,Oe={mixins:[yt,_t,xt,At],props:{columns:Number,max:Number,min:Number,options:Array,value:{type:[Array,Object],default:()=>[]}}};const Ae=G({mixins:[Oe],inheritAttrs:!1,data(){return{selected:this.toArray(this.value)}},watch:{value(t){this.selected=this.toArray(t)},selected(){this.onInvalid()}},mounted(){this.onInvalid(),this.$props.autofocus&&this.focus()},methods:{focus(){var t;null==(t=this.$el.querySelector("input"))||t.focus()},onInput(t,e){if(!0===e)this.selected.push(t);else{const e=this.selected.indexOf(t);-1!==e&&this.selected.splice(e,1)}this.$emit("input",this.selected)},onInvalid(){this.$emit("invalid",this.$v.$invalid,this.$v)},select(){this.focus()},toArray:t=>!0===Array.isArray(t)?t:"string"==typeof t?String(t).split(","):"object"==typeof t?Object.values(t):void 0},validations(){return{selected:{required:!this.required||j.required,min:!this.min||j.minLength(this.min),max:!this.max||j.maxLength(this.max)}}}},(function(){var t=this,e=t._self._c;return e("ul",{staticClass:"k-checkboxes-input",style:"--columns:"+t.columns},[t.options.length?t._l(t.options,(function(s,n){return e("li",{key:n},[e("k-checkbox-input",{attrs:{id:t.id+"-"+n,label:s.text,value:-1!==t.selected.indexOf(s.value)},on:{input:function(e){return t.onInput(s.value,e)}}})],1)})):e("k-box",{attrs:{theme:"info"}},[t._v(t._s(t.$t("options.none")))])],2)}),[],!1,null,null,null,null).exports,Te={mixins:[yt,_t,xt,At],props:{display:{type:String,default:"DD.MM.YYYY"},max:String,min:String,step:{type:Object,default:()=>({size:1,unit:"day"})},type:{type:String,default:"date"},value:String}};const Ie=G({mixins:[Te],inheritAttrs:!1,data:()=>({dt:null,formatted:null}),computed:{inputType:()=>"date",pattern(){return this.$library.dayjs.pattern(this.display)},rounding(){return{...this.$options.props.step.default(),...this.step}}},watch:{value:{handler(t,e){if(t!==e){const e=this.toDatetime(t);this.commit(e)}},immediate:!0}},created(){this.$events.$on("keydown.cmd.s",this.onBlur)},destroyed(){this.$events.$off("keydown.cmd.s",this.onBlur)},methods:{alter(t){let e=this.parse()||this.round(this.$library.dayjs()),s=this.rounding.unit,n=this.rounding.size;const i=this.selection();null!==i&&("meridiem"===i.unit?(t="pm"===e.format("a")?"subtract":"add",s="hour",n=12):(s=i.unit,s!==this.rounding.unit&&(n=1))),e=e[t](n,s).round(this.rounding.unit,this.rounding.size),this.commit(e),this.emit(e),this.$nextTick((()=>this.select(i)))},commit(t){this.dt=t,this.formatted=this.pattern.format(t),this.$emit("invalid",this.$v.$invalid,this.$v)},emit(t){this.$emit("input",this.toISO(t))},focus(){this.$refs.input.focus()},onArrowDown(){this.alter("subtract")},onArrowUp(){this.alter("add")},onBlur(){const t=this.parse();this.commit(t),this.emit(t)},onEnter(){this.onBlur(),this.$nextTick((()=>this.$emit("submit")))},onInput(t){const e=this.parse(),s=this.pattern.format(e);if(!t||s==t)return this.commit(e),this.emit(e)},onTab(t){""!=this.$refs.input.value&&(this.onBlur(),this.$nextTick((()=>{const e=this.selection();if(this.$refs.input&&e.start===this.$refs.input.selectionStart&&e.end===this.$refs.input.selectionEnd-1)if(t.shiftKey){if(0===e.index)return;this.selectPrev(e.index)}else{if(e.index===this.pattern.parts.length-1)return;this.selectNext(e.index)}else{if(this.$refs.input&&this.$refs.input.selectionStart==e.end+1&&e.index==this.pattern.parts.length-1)return;if(this.$refs.input&&this.$refs.input.selectionEnd-1>e.end){const t=this.pattern.at(this.$refs.input.selectionEnd,this.$refs.input.selectionEnd);this.select(this.pattern.parts[t.index])}else this.select(this.pattern.parts[e.index])}t.preventDefault()})))},parse(){let t=this.$refs.input.value;return t=this.$library.dayjs.interpret(t,this.inputType),this.round(t)},round(t){return(null==t?void 0:t.round(this.rounding.unit,this.rounding.size))||null},select(t){var e;t||(t=this.selection()),null==(e=this.$refs.input)||e.setSelectionRange(t.start,t.end+1)},selectFirst(){this.select(this.pattern.parts[0])},selectLast(){this.select(this.pattern.parts[this.pattern.parts.length-1])},selectNext(t){this.select(this.pattern.parts[t+1])},selectPrev(t){this.select(this.pattern.parts[t-1])},selection(){return this.pattern.at(this.$refs.input.selectionStart,this.$refs.input.selectionEnd)},toDatetime(t){return this.round(this.$library.dayjs.iso(t,this.inputType))},toISO(t){return(null==t?void 0:t.toISO(this.inputType))||null}},validations(){return{value:{min:!this.dt||!this.min||(()=>this.dt.validate(this.min,"min",this.rounding.unit)),max:!this.dt||!this.max||(()=>this.dt.validate(this.max,"max",this.rounding.unit)),required:!this.required||(()=>!!this.dt)}}}},(function(){var t=this;return(0,t._self._c)("input",{directives:[{name:"direction",rawName:"v-direction"}],ref:"input",class:`k-text-input k-${t.type}-input`,attrs:{id:t.id,autofocus:t.autofocus,disabled:t.disabled,placeholder:t.display,required:t.required,autocomplete:"off",spellcheck:"false",type:"text"},domProps:{value:t.formatted},on:{blur:t.onBlur,focus:function(e){return t.$emit("focus")},input:function(e){return t.onInput(e.target.value)},keydown:[function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"down",40,e.key,["Down","ArrowDown"])?null:(e.stopPropagation(),e.preventDefault(),t.onArrowDown.apply(null,arguments))},function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"up",38,e.key,["Up","ArrowUp"])?null:(e.stopPropagation(),e.preventDefault(),t.onArrowUp.apply(null,arguments))},function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"enter",13,e.key,"Enter")?null:(e.stopPropagation(),e.preventDefault(),t.onEnter.apply(null,arguments))},function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"tab",9,e.key,"Tab")?null:t.onTab.apply(null,arguments)}]}})}),[],!1,null,null,null,null).exports,je={mixins:[yt,_t,xt,Ot,At],props:{autocomplete:{type:[Boolean,String],default:"off"},maxlength:Number,minlength:Number,pattern:String,placeholder:String,preselect:Boolean,spellcheck:{type:[Boolean,String],default:"off"},type:{type:String,default:"text"},value:String}};const Me=G({mixins:[je],inheritAttrs:!1,data(){return{listeners:{...this.$listeners,input:t=>this.onInput(t.target.value)}}},watch:{value(){this.onInvalid()}},mounted(){this.onInvalid(),this.$props.autofocus&&this.focus(),this.$props.preselect&&this.select()},methods:{focus(){this.$refs.input.focus()},onInput(t){this.$emit("input",t)},onInvalid(){this.$emit("invalid",this.$v.$invalid,this.$v)},select(){this.$refs.input.select()}},validations(){return{value:{required:!this.required||j.required,minLength:!this.minlength||j.minLength(this.minlength),maxLength:!this.maxlength||j.maxLength(this.maxlength),email:"email"!==this.type||j.email,url:"url"!==this.type||j.url,pattern:!this.pattern||(t=>!this.required&&!t||!this.$refs.input.validity.patternMismatch)}}}},(function(){var t=this;return(0,t._self._c)("input",t._g(t._b({directives:[{name:"direction",rawName:"v-direction"}],ref:"input",staticClass:"k-text-input"},"input",{autocomplete:t.autocomplete,autofocus:t.autofocus,disabled:t.disabled,id:t.id,minlength:t.minlength,name:t.name,pattern:t.pattern,placeholder:t.placeholder,required:t.required,spellcheck:t.spellcheck,type:t.type,value:t.value},!1),t.listeners))}),[],!1,null,null,null,null).exports,Ee={mixins:[je],props:{autocomplete:{type:String,default:"email"},placeholder:{type:String,default:()=>window.panel.$t("email.placeholder")},type:{type:String,default:"email"}}};const Le=G({extends:Me,mixins:[Ee]},null,null,!1,null,null,null,null).exports;class Be extends Jt{get schema(){return{content:"bulletList|orderedList"}}}const De=G({mixins:[_t],inheritAttrs:!1,props:{autofocus:Boolean,marks:{type:[Array,Boolean],default:!0},spellcheck:Boolean,value:String},data(){return{list:this.value,html:this.value}},computed:{extensions:()=>[new Be({inline:!0})]},watch:{value(t){t!==this.html&&(this.list=t,this.html=t)}},methods:{focus(){this.$refs.input.focus()},onInput(t){let e=(new DOMParser).parseFromString(t,"text/html").querySelector("ul, ol");e&&0!==e.textContent.trim().length?(this.list=t,this.html=t.replace(/(

|<\/p>)/gi,""),this.$emit("input",this.html)):this.$emit("input",this.list="")}}},(function(){var t=this;return(0,t._self._c)("k-writer",t._b({ref:"input",staticClass:"k-list-input",attrs:{extensions:t.extensions,nodes:["bulletList","orderedList"],value:t.list},on:{input:t.onInput}},"k-writer",t.$props,!1))}),[],!1,null,null,null,null).exports,Pe={mixins:[_t,xt,At],props:{max:Number,min:Number,layout:String,options:Array,search:[Object,Boolean],separator:{type:String,default:","},sort:Boolean,value:{type:Array,required:!0,default:()=>[]}}};const Ne=G({mixins:[Pe],inheritAttrs:!1,data(){return{state:this.value,q:null,limit:!0,scrollTop:0}},computed:{draggable(){return this.state.length>1&&!this.sort},dragOptions(){return{disabled:!this.draggable,draggable:".k-tag",delay:1}},emptyLabel(){return this.q?this.$t("search.results.none"):this.$t("options.none")},filtered(){var t;return(null==(t=this.q)?void 0:t.length)>=(this.search.min||0)?this.options.filter((t=>this.isFiltered(t))).map((t=>({...t,text:this.toHighlightedString(t.text)}))):this.options},more(){return!this.max||this.state.lengththis.options.find((e=>e.value===t))));if(!1===this.sort)return t;const e=t=>this.options.findIndex((e=>e.value===t.value));return t.sort(((t,s)=>e(t)-e(s)))},visible(){return this.limit?this.filtered.slice(0,this.search.display||this.filtered.length):this.filtered}},watch:{value(t){this.state=t,this.onInvalid()}},mounted(){this.onInvalid(),this.$events.$on("click",this.close),this.$events.$on("keydown.cmd.s",this.close)},destroyed(){this.$events.$off("click",this.close),this.$events.$off("keydown.cmd.s",this.close)},methods:{add(t){!0===this.more&&(this.state.push(t.value),this.onInput())},blur(){this.close()},close(){var t;!0===(null==(t=this.$refs.dropdown)?void 0:t.isOpen)&&(this.$refs.dropdown.close(),this.limit=!0)},focus(){var t;null==(t=this.$refs.dropdown)||t.open()},index(t){return this.state.findIndex((e=>e===t.value))},isFiltered(t){return String(t.text).match(this.regex)||String(t.value).match(this.regex)},isSelected(t){return-1!==this.index(t)},navigate(t){var e,s,n;"prev"===t&&(t="previous"),null==(n=null==(s=null==(e=document.activeElement)?void 0:e[t+"Sibling"])?void 0:s.focus)||n.call(s)},onClose(){var t;!1===(null==(t=this.$refs.dropdown)?void 0:t.isOpen)&&(document.activeElement===this.$parent.$el&&(this.q=null),this.$parent.$el.focus())},onEscape(){this.q?this.q=null:this.close()},onInput(){this.$emit("input",this.state)},onInvalid(){this.$emit("invalid",this.$v.$invalid,this.$v)},onOpen(){this.$nextTick((()=>{var t,e,s;null==(e=null==(t=this.$refs.search)?void 0:t.focus)||e.call(t),(null==(s=this.$refs.dropdown)?void 0:s.$el)&&(this.$refs.dropdown.$el.querySelector(".k-multiselect-options").scrollTop=this.scrollTop)}))},remove(t){const e=this.index(t);this.state.splice(e,1),this.onInput()},select(t){this.scrollTop=this.$refs.dropdown.$el.querySelector(".k-multiselect-options").scrollTop,this.isSelected(t)?this.remove(t):this.add(t)},toHighlightedString(t){return(t=this.$helper.string.stripHTML(t)).replace(this.regex,"$1")}},validations(){return{state:{required:!this.required||j.required,minLength:!this.min||j.minLength(this.min),maxLength:!this.max||j.maxLength(this.max)}}}},(function(){var t=this,e=t._self._c;return e("k-draggable",{staticClass:"k-multiselect-input",attrs:{list:t.state,options:t.dragOptions,"data-layout":t.layout,element:"k-dropdown"},on:{end:t.onInput},nativeOn:{click:function(e){return t.$refs.dropdown.toggle.apply(null,arguments)}},scopedSlots:t._u([{key:"footer",fn:function(){return[e("k-dropdown-content",{ref:"dropdown",on:{open:t.onOpen,close:t.onClose},nativeOn:{keydown:function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"esc",27,e.key,["Esc","Escape"])?null:(e.stopPropagation(),t.close.apply(null,arguments))}}},[t.search?e("k-dropdown-item",{staticClass:"k-multiselect-search",attrs:{icon:"search"}},[e("input",{ref:"search",attrs:{placeholder:t.search.min?t.$t("search.min",{min:t.search.min}):t.$t("search")+" …"},domProps:{value:t.q},on:{input:function(e){t.q=e.target.value},keydown:function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"esc",27,e.key,["Esc","Escape"])?null:(e.stopPropagation(),t.onEscape.apply(null,arguments))}}})]):t._e(),e("div",{staticClass:"k-multiselect-options scroll-y-auto"},[t._l(t.visible,(function(s){return e("k-dropdown-item",{key:s.value,class:{"k-multiselect-option":!0,selected:t.isSelected(s),disabled:!t.more},attrs:{icon:t.isSelected(s)?"check":"circle-outline"},on:{click:function(e){return e.preventDefault(),t.select(s)}},nativeOn:{keydown:[function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"enter",13,e.key,"Enter")?null:(e.preventDefault(),e.stopPropagation(),t.select(s))},function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"space",32,e.key,[" ","Spacebar"])?null:(e.preventDefault(),e.stopPropagation(),t.select(s))}]}},[e("span",{domProps:{innerHTML:t._s(s.text)}})])})),0===t.filtered.length?e("k-dropdown-item",{staticClass:"k-multiselect-option",attrs:{disabled:!0}},[t._v(" "+t._s(t.emptyLabel)+" ")]):t._e()],2),t.visible.lengththis.onInput(t.target.value),blur:this.onBlur}}},watch:{value(t){this.number=t},number:{immediate:!0,handler(){this.onInvalid()}}},mounted(){this.$props.autofocus&&this.focus(),this.$props.preselect&&this.select()},methods:{decimals(){const t=Number(this.step||0);return Math.floor(t)===t?0:-1!==t.toString().indexOf("e")?parseInt(t.toFixed(16).split(".")[1].split("").reverse().join("")).toString().length:t.toString().split(".")[1].length||0},format(t){if(isNaN(t)||""===t)return"";const e=this.decimals();return t=e?parseFloat(t).toFixed(e):Number.isInteger(this.step)?parseInt(t):parseFloat(t)},clean(){this.number=this.format(this.number)},emit(t){t=parseFloat(t),isNaN(t)&&(t=""),t!==this.value&&this.$emit("input",t)},focus(){this.$refs.input.focus()},onInvalid(){this.$emit("invalid",this.$v.$invalid,this.$v)},onInput(t){this.number=t,this.emit(t)},onBlur(){this.clean(),this.emit(this.number)},select(){this.$refs.input.select()}},validations(){return{value:{required:!this.required||j.required,min:!this.min||j.minValue(this.min),max:!this.max||j.maxValue(this.max)}}}},(function(){var t=this;return(0,t._self._c)("input",t._g(t._b({ref:"input",staticClass:"k-number-input",attrs:{step:t.stepNumber,type:"number"},domProps:{value:t.number},on:{keydown:[function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"s",void 0,e.key,void 0)?null:e.ctrlKey?t.clean.apply(null,arguments):null},function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"s",void 0,e.key,void 0)?null:e.metaKey?t.clean.apply(null,arguments):null}]}},"input",{autofocus:t.autofocus,disabled:t.disabled,id:t.id,max:t.max,min:t.min,name:t.name,placeholder:t.placeholder,required:t.required},!1),t.listeners))}),[],!1,null,null,null,null).exports,qe={mixins:[je],props:{autocomplete:{type:String,default:"new-password"},type:{type:String,default:"password"}}};const Ye=G({extends:Me,mixins:[qe]},null,null,!1,null,null,null,null).exports,ze={mixins:[yt,_t,xt,At],props:{columns:Number,options:Array,value:[String,Number,Boolean]}};const He=G({mixins:[ze],inheritAttrs:!1,watch:{value(){this.onInvalid()}},mounted(){this.onInvalid(),this.$props.autofocus&&this.focus()},methods:{focus(){var t;null==(t=this.$el.querySelector("input"))||t.focus()},onInput(t){this.$emit("input",t)},onInvalid(){this.$emit("invalid",this.$v.$invalid,this.$v)},select(){this.focus()}},validations(){return{value:{required:!this.required||j.required}}}},(function(){var t=this,e=t._self._c;return e("ul",{staticClass:"k-radio-input",style:"--columns:"+t.columns},[t.options.length?t._l(t.options,(function(s,n){return e("li",{key:n},[e("input",{staticClass:"k-radio-input-native",attrs:{id:t.id+"-"+n,name:t.id,type:"radio"},domProps:{value:s.value,checked:t.value===s.value},on:{change:function(e){return t.onInput(s.value)}}}),s.info?e("label",{attrs:{for:t.id+"-"+n}},[e("span",{staticClass:"k-radio-input-text",domProps:{innerHTML:t._s(s.text)}}),e("span",{staticClass:"k-radio-input-info",domProps:{innerHTML:t._s(s.info)}})]):e("label",{attrs:{for:t.id+"-"+n},domProps:{innerHTML:t._s(s.text)}}),s.icon?e("k-icon",{attrs:{type:s.icon}}):t._e()],1)})):e("k-box",{attrs:{theme:"info"}},[t._v(t._s(t.$t("options.none")))])],2)}),[],!1,null,null,null,null).exports,Ue={mixins:[yt,_t,xt,Ot,At],props:{default:[Number,String],max:{type:Number,default:100},min:{type:Number,default:0},step:{type:Number,default:1},tooltip:{type:[Boolean,Object],default:()=>({before:null,after:null})},value:[Number,String]}};const Ve=G({mixins:[Ue],inheritAttrs:!1,data(){return{listeners:{...this.$listeners,input:t=>this.onInput(t.target.value)}}},computed:{baseline(){return this.min<0?0:this.min},label(){return this.required||this.value||0===this.value?this.format(this.position):"–"},position(){return this.value||0===this.value?this.value:this.default||this.baseline}},watch:{position(){this.onInvalid()}},mounted(){this.onInvalid(),this.$props.autofocus&&this.focus()},methods:{focus(){this.$refs.input.focus()},format(t){const e=document.lang?document.lang.replace("_","-"):"en",s=this.step.toString().split("."),n=s.length>1?s[1].length:0;return new Intl.NumberFormat(e,{minimumFractionDigits:n}).format(t)},onInvalid(){this.$emit("invalid",this.$v.$invalid,this.$v)},onInput(t){this.$emit("input",t)}},validations(){return{position:{required:!this.required||j.required,min:!this.min||j.minValue(this.min),max:!this.max||j.maxValue(this.max)}}}},(function(){var t=this,e=t._self._c;return e("label",{staticClass:"k-range-input"},[e("input",t._g(t._b({ref:"input",staticClass:"k-range-input-native",style:`--min: ${t.min}; --max: ${t.max}; --value: ${t.position}`,attrs:{type:"range"},domProps:{value:t.position}},"input",{autofocus:t.autofocus,disabled:t.disabled,id:t.id,max:t.max,min:t.min,name:t.name,required:t.required,step:t.step},!1),t.listeners)),t.tooltip?e("span",{staticClass:"k-range-input-tooltip"},[t.tooltip.before?e("span",{staticClass:"k-range-input-tooltip-before"},[t._v(t._s(t.tooltip.before))]):t._e(),e("span",{staticClass:"k-range-input-tooltip-text"},[t._v(t._s(t.label))]),t.tooltip.after?e("span",{staticClass:"k-range-input-tooltip-after"},[t._v(t._s(t.tooltip.after))]):t._e()]):t._e()])}),[],!1,null,null,null,null).exports,Ke={mixins:[yt,_t,xt,Ot,At],props:{ariaLabel:String,default:String,empty:{type:[Boolean,String],default:!0},placeholder:String,options:Array,value:{type:[String,Number,Boolean],default:""}}};const Je=G({mixins:[Ke],inheritAttrs:!1,data(){return{selected:this.value,listeners:{...this.$listeners,click:t=>this.onClick(t),change:t=>this.onInput(t.target.value),input:()=>{}}}},computed:{emptyOption(){return this.placeholder||"—"},hasEmptyOption(){return!1!==this.empty&&!(this.required&&this.default)},label(){const t=this.text(this.selected);return""===this.selected||null===this.selected||null===t?this.emptyOption:t}},watch:{value(t){this.selected=t,this.onInvalid()}},mounted(){this.onInvalid(),this.$props.autofocus&&this.focus()},methods:{focus(){this.$refs.input.focus()},onClick(t){t.stopPropagation(),this.$emit("click",t)},onInvalid(){this.$emit("invalid",this.$v.$invalid,this.$v)},onInput(t){this.selected=t,this.$emit("input",this.selected)},select(){this.focus()},text(t){let e=null;return this.options.forEach((s=>{s.value==t&&(e=s.text)})),e}},validations(){return{selected:{required:!this.required||j.required}}}},(function(){var t=this,e=t._self._c;return e("span",{staticClass:"k-select-input",attrs:{"data-disabled":t.disabled,"data-empty":""===t.selected}},[e("select",t._g({ref:"input",staticClass:"k-select-input-native",attrs:{id:t.id,autofocus:t.autofocus,"aria-label":t.ariaLabel,disabled:t.disabled,name:t.name,required:t.required},domProps:{value:t.selected}},t.listeners),[t.hasEmptyOption?e("option",{attrs:{disabled:t.required,value:""}},[t._v(" "+t._s(t.emptyOption)+" ")]):t._e(),t._l(t.options,(function(s){return e("option",{key:s.value,attrs:{disabled:s.disabled},domProps:{value:s.value}},[t._v(" "+t._s(s.text)+" ")])}))],2),t._v(" "+t._s(t.label)+" ")])}),[],!1,null,null,null,null).exports,Ge={mixins:[je],props:{allow:{type:String,default:""},formData:{type:Object,default:()=>({})},sync:{type:String}}};const We=G({extends:Me,mixins:[Ge],data(){return{slug:this.sluggify(this.value),slugs:this.$language?this.$language.rules:this.$system.slugs,syncValue:null}},watch:{formData:{handler(t){return!this.disabled&&(!(!this.sync||void 0===t[this.sync])&&(t[this.sync]!=this.syncValue&&(this.syncValue=t[this.sync],void this.onInput(this.sluggify(this.syncValue)))))},deep:!0,immediate:!0},value(t){(t=this.sluggify(t))!==this.slug&&(this.slug=t,this.$emit("input",this.slug))}},methods:{sluggify(t){return this.$helper.slug(t,[this.slugs,this.$system.ascii],this.allow)},onInput(t){this.slug=this.sluggify(t),this.$emit("input",this.slug)}}},(function(){var t=this;return(0,t._self._c)("input",t._g(t._b({directives:[{name:"direction",rawName:"v-direction"}],ref:"input",staticClass:"k-text-input",attrs:{autocomplete:"off",spellcheck:"false",type:"text"},domProps:{value:t.slug}},"input",{autofocus:t.autofocus,disabled:t.disabled,id:t.id,minlength:t.minlength,name:t.name,pattern:t.pattern,placeholder:t.placeholder,required:t.required},!1),t.listeners))}),[],!1,null,null,null,null).exports,Xe={mixins:[yt,_t,xt,Ot,At],props:{accept:{type:String,default:"all"},icon:{type:[String,Boolean],default:"tag"},layout:String,max:Number,min:Number,options:{type:Array,default:()=>[]},separator:{type:String,default:","},value:{type:Array,default:()=>[]}}};const Ze=G({mixins:[Xe],inheritAttrs:!1,data(){return{tags:this.toValues(this.value),selected:null,newTag:null}},computed:{dragOptions(){return{delay:1,disabled:!this.draggable,draggable:".k-tag"}},draggable(){return this.tags.length>1},skip(){return this.tags.map((t=>t.value))}},watch:{value(t){this.tags=this.toValues(t),this.onInvalid()}},mounted(){this.onInvalid(),this.$props.autofocus&&this.focus()},methods:{addString(t,e=!0){if(!t)return;if(0===(t=t.trim()).length)return;if(!0===t.includes(this.separator)){for(const e of t.split(this.separator))this.addString(e);return}const s=this.toValue(t);s&&this.addTag(s,e)},addTag(t,e=!0){this.addTagToIndex(t),this.$refs.autocomplete.close(),e&&this.$refs.input.focus()},addTagToIndex(t){if("options"===this.accept){if(!this.options.find((e=>e.value===t.value)))return}-1===this.index(t)&&(!this.max||this.tags.length=this.tags.length)return;break;case"first":e=0;break;case"last":e=this.tags.length-1;break;default:e=t}let n=this.tags[e];if(n){let t=this.$refs[n.value];if(null==t?void 0:t[0])return{ref:t[0],tag:n,index:e}}return!1},index(t){return this.tags.findIndex((e=>e.value===t.value))},navigate(t){var e=this.get(t);e?(e.ref.focus(),this.selectTag(e.tag)):"next"===t&&(this.$refs.input.focus(),this.selectTag(null))},onBack(t){0===t.target.selectionStart&&t.target.selectionStart===t.target.selectionEnd&&0!==this.tags.length&&(this.$refs.autocomplete.close(),this.navigate("last"),t.preventDefault())},onBlur(t){var e;let s=t.relatedTarget||t.explicitOriginalTarget;(null==(e=this.$refs.autocomplete.$el)?void 0:e.contains(s))||this.addString(this.$refs.input.value,!1)},onEnter(t){if(!this.newTag||0===this.newTag.length)return!0;t.preventDefault(),this.addString(this.newTag)},onInput(){const t=this.tags.map((t=>t.value));this.$emit("input",t)},onInvalid(){this.$emit("invalid",this.$v.$invalid,this.$v)},onSubmit(t){t.preventDefault(),t.stopImmediatePropagation(),this.onBlur(t),this.$emit("submit",t)},onTab(t){var e;(null==(e=this.newTag)?void 0:e.length)>0&&(t.preventDefault(),this.addString(this.newTag))},onType(t){this.newTag=t,this.$refs.autocomplete.search(t)},remove(t){const e=this.get("prev"),s=this.get("next");this.tags.splice(this.index(t),1),this.onInput(),e?(this.selectTag(e.tag),e.ref.focus()):s?this.selectTag(s.tag):(this.selectTag(null),this.$refs.input.focus())},select(){this.focus()},selectTag(t){this.selected=t},toValue(t){const e=this.options.find((e=>e.value===t));return"options"===this.accept?e:e||("string"==typeof t&&(t={value:t}),{value:t.value,text:this.$helper.string.escapeHTML(t.text??t.value)})},toValues(t){return"object"==typeof t&&(t=Object.values(t)),!1===Array.isArray(t)?[]:t.map(this.toValue).filter((t=>t))}},validations(){return{tags:{required:!this.required||j.required,minLength:!this.min||j.minLength(this.min),maxLength:!this.max||j.maxLength(this.max)}}}},(function(){var t=this,e=t._self._c;return e("k-draggable",{directives:[{name:"direction",rawName:"v-direction"}],staticClass:"k-tags-input",attrs:{list:t.tags,options:t.dragOptions,"data-layout":t.layout},on:{end:t.onInput},scopedSlots:t._u([{key:"footer",fn:function(){return[e("span",{staticClass:"k-tags-input-element"},[e("k-autocomplete",{ref:"autocomplete",attrs:{html:!0,options:t.options,skip:t.skip},on:{select:t.addTag,leave:function(e){return t.$refs.input.focus()}}},[e("input",{ref:"input",attrs:{id:t.id,autofocus:t.autofocus,disabled:t.disabled||t.max&&t.tags.length>=t.max,name:t.name,autocomplete:"off",type:"text"},domProps:{value:t.newTag},on:{input:function(e){return t.onType(e.target.value)},blur:t.onBlur,keydown:[function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"s",void 0,e.key,void 0)?null:e.metaKey?t.onSubmit.apply(null,arguments):null},function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"left",37,e.key,["Left","ArrowLeft"])||"button"in e&&0!==e.button||e.ctrlKey||e.shiftKey||e.altKey||e.metaKey?null:t.onBack.apply(null,arguments)},function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"enter",13,e.key,"Enter")||e.ctrlKey||e.shiftKey||e.altKey||e.metaKey?null:t.onEnter.apply(null,arguments)},function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"tab",9,e.key,"Tab")||e.ctrlKey||e.shiftKey||e.altKey||e.metaKey?null:t.onTab.apply(null,arguments)},function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"backspace",void 0,e.key,void 0)||e.ctrlKey||e.shiftKey||e.altKey||e.metaKey?null:t.onBack.apply(null,arguments)}]}})])],1)]},proxy:!0}])},t._l(t.tags,(function(s){return e("k-tag",{key:s.value,ref:s.value,refInFor:!0,attrs:{removable:!t.disabled,name:"tag"},on:{remove:function(e){return t.remove(s)}},nativeOn:{click:function(t){t.stopPropagation()},blur:function(e){return t.selectTag(null)},focus:function(e){return t.selectTag(s)},keydown:[function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"left",37,e.key,["Left","ArrowLeft"])||"button"in e&&0!==e.button?null:t.navigate("prev")},function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"right",39,e.key,["Right","ArrowRight"])||"button"in e&&2!==e.button?null:t.navigate("next")}],dblclick:function(e){return t.edit(s)}}},[e("span",{domProps:{innerHTML:t._s(s.text)}})])})),1)}),[],!1,null,null,null,null).exports,Qe={mixins:[je],props:{autocomplete:{type:String,default:"tel"},type:{type:String,default:"tel"}}};const ts=G({extends:Me,mixins:[Qe]},null,null,!1,null,null,null,null).exports,es={mixins:[yt,_t,xt,Ot,At],props:{buttons:{type:[Boolean,Array],default:!0},endpoints:Object,font:String,maxlength:Number,minlength:Number,placeholder:String,preselect:Boolean,size:String,spellcheck:{type:[Boolean,String],default:"off"},theme:String,uploads:[Boolean,Object,Array],value:String}};const ss=G({mixins:[es],inheritAttrs:!1,data:()=>({over:!1}),watch:{value(){this.onInvalid(),this.$nextTick((()=>{this.resize()}))}},mounted(){this.$nextTick((()=>{this.$library.autosize(this.$refs.input)})),this.onInvalid(),this.$props.autofocus&&this.focus(),this.$props.preselect&&this.select()},methods:{cancel(){this.$refs.input.focus()},dialog(t){if(!this.$refs[t+"Dialog"])throw"Invalid toolbar dialog";this.$refs[t+"Dialog"].open(this.$refs.input,this.selection())},focus(){this.$refs.input.focus()},insert(t){const e=this.$refs.input,s=e.value;setTimeout((()=>{if(e.focus(),document.execCommand("insertText",!1,t),e.value===s){const s=e.value.slice(0,e.selectionStart)+t+e.value.slice(e.selectionEnd);e.value=s,this.$emit("input",s)}})),this.resize()},insertFile(t){(null==t?void 0:t.length)>0&&this.insert(t.map((t=>t.dragText)).join("\n\n"))},insertUpload(t,e){this.insert(e.map((t=>t.dragText)).join("\n\n")),this.$events.$emit("model.update")},onClick(){this.$refs.toolbar&&this.$refs.toolbar.close()},onCommand(t,e){"function"==typeof this[t]?"function"==typeof e?this[t](e(this.$refs.input,this.selection())):this[t](e):window.console.warn(t+" is not a valid command")},onDrop(t){if(this.uploads&&this.$helper.isUploadEvent(t))return this.$refs.fileUpload.drop(t.dataTransfer.files,{url:this.$urls.api+"/"+this.endpoints.field+"/upload",multiple:!1});const e=this.$store.state.drag;"text"===(null==e?void 0:e.type)&&(this.focus(),this.insert(e.data))},onFocus(t){this.$emit("focus",t)},onInput(t){this.$emit("input",t.target.value)},onInvalid(){this.$emit("invalid",this.$v.$invalid,this.$v)},onOut(){this.$refs.input.blur(),this.over=!1},onOver(t){if(this.uploads&&this.$helper.isUploadEvent(t))return t.dataTransfer.dropEffect="copy",this.focus(),void(this.over=!0);const e=this.$store.state.drag;"text"===(null==e?void 0:e.type)&&(t.dataTransfer.dropEffect="copy",this.focus(),this.over=!0)},onShortcut(t){!1!==this.buttons&&"Meta"!==t.key&&"Control"!==t.key&&this.$refs.toolbar&&this.$refs.toolbar.shortcut(t.key,t)},onSubmit(t){return this.$emit("submit",t)},prepend(t){this.insert(t+" "+this.selection())},resize(){this.$library.autosize.update(this.$refs.input)},select(){this.$refs.select()},selectFile(){this.$refs.fileDialog.open({endpoint:this.endpoints.field+"/files",multiple:!1})},selection(){const t=this.$refs.input,e=t.selectionStart,s=t.selectionEnd;return t.value.substring(e,s)},uploadFile(){this.$refs.fileUpload.open({url:this.$urls.api+"/"+this.endpoints.field+"/upload",multiple:!1})},wrap(t){this.insert(t+this.selection()+t)}},validations(){return{value:{required:!this.required||j.required,minLength:!this.minlength||j.minLength(this.minlength),maxLength:!this.maxlength||j.maxLength(this.maxlength)}}}},(function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-textarea-input",attrs:{"data-over":t.over,"data-size":t.size,"data-theme":t.theme}},[e("div",{staticClass:"k-textarea-input-wrapper"},[t.buttons&&!t.disabled?e("k-toolbar",{ref:"toolbar",attrs:{buttons:t.buttons,disabled:t.disabled,uploads:t.uploads},on:{command:t.onCommand},nativeOn:{mousedown:function(t){t.preventDefault()}}}):t._e(),e("textarea",t._b({directives:[{name:"direction",rawName:"v-direction"}],ref:"input",staticClass:"k-textarea-input-native",attrs:{"data-font":t.font},on:{click:t.onClick,focus:t.onFocus,input:t.onInput,keydown:[function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"enter",13,e.key,"Enter")?null:e.metaKey?t.onSubmit.apply(null,arguments):null},function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"enter",13,e.key,"Enter")?null:e.ctrlKey?t.onSubmit.apply(null,arguments):null},function(e){return e.metaKey?t.onShortcut.apply(null,arguments):null},function(e){return e.ctrlKey?t.onShortcut.apply(null,arguments):null}],dragover:t.onOver,dragleave:t.onOut,drop:t.onDrop}},"textarea",{autofocus:t.autofocus,disabled:t.disabled,id:t.id,minlength:t.minlength,name:t.name,placeholder:t.placeholder,required:t.required,spellcheck:t.spellcheck,value:t.value},!1))],1),e("k-toolbar-email-dialog",{ref:"emailDialog",on:{cancel:t.cancel,submit:function(e){return t.insert(e)}}}),e("k-toolbar-link-dialog",{ref:"linkDialog",on:{cancel:t.cancel,submit:function(e){return t.insert(e)}}}),e("k-files-dialog",{ref:"fileDialog",on:{cancel:t.cancel,submit:function(e){return t.insertFile(e)}}}),t.uploads?e("k-upload",{ref:"fileUpload",on:{success:t.insertUpload}}):t._e()],1)}),[],!1,null,null,null,null).exports,ns={props:{display:{type:String,default:"HH:mm"},max:String,min:String,step:{type:Object,default:()=>({size:5,unit:"minute"})},type:{type:String,default:"time"},value:String}};const is=G({mixins:[Ie,ns],computed:{inputType:()=>"time"}},null,null,!1,null,null,null,null).exports,os={props:{autofocus:Boolean,disabled:Boolean,id:[Number,String],text:{type:[Array,String]},required:Boolean,value:Boolean}};const rs=G({mixins:[os],inheritAttrs:!1,computed:{label(){const t=this.text||[this.$t("off"),this.$t("on")];return Array.isArray(t)?this.value?t[1]:t[0]:t}},watch:{value(){this.onInvalid()}},mounted(){this.onInvalid(),this.$props.autofocus&&this.focus()},methods:{focus(){this.$refs.input.focus()},onEnter(t){"Enter"===t.key&&this.$refs.input.click()},onInput(t){this.$emit("input",t)},onInvalid(){this.$emit("invalid",this.$v.$invalid,this.$v)},select(){this.$refs.input.focus()}},validations(){return{value:{required:!this.required||j.required}}}},(function(){var t=this,e=t._self._c;return e("label",{staticClass:"k-toggle-input",attrs:{"data-disabled":t.disabled}},[e("input",{ref:"input",staticClass:"k-toggle-input-native",attrs:{id:t.id,disabled:t.disabled,type:"checkbox"},domProps:{checked:t.value},on:{change:function(e){return t.onInput(e.target.checked)}}}),e("span",{staticClass:"k-toggle-input-label",domProps:{innerHTML:t._s(t.label)}})])}),[],!1,null,null,null,null).exports,ls={mixins:[yt,_t,xt,At],props:{columns:Number,grow:Boolean,labels:Boolean,options:Array,reset:Boolean,value:[String,Number,Boolean]}};const as=G({mixins:[ls],inheritAttrs:!1,watch:{value(){this.onInvalid()}},mounted(){this.onInvalid(),this.$props.autofocus&&this.focus()},methods:{focus(){(this.$el.querySelector("input[checked]")||this.$el.querySelector("input")).focus()},onClick(t){t===this.value&&this.reset&&!this.required&&this.$emit("input","")},onInput(t){this.$emit("input",t)},onInvalid(){this.$emit("invalid",this.$v.$invalid,this.$v)},select(){this.focus()}},validations(){return{value:{required:!this.required||j.required}}}},(function(){var t=this,e=t._self._c;return e("ul",{staticClass:"k-toggles-input",style:"--options:"+(t.columns||t.options.length),attrs:{"data-invalid":t.$v.$invalid,"data-labels":t.labels}},t._l(t.options,(function(s,n){return e("li",{key:n},[e("input",{staticClass:"input-hidden",attrs:{id:t.id+"-"+n,name:t.id,type:"radio"},domProps:{value:s.value,checked:t.value===s.value},on:{click:function(e){return t.onClick(s.value)},change:function(e){return t.onInput(s.value)}}}),e("label",{attrs:{for:t.id+"-"+n,title:s.text}},[s.icon?e("k-icon",{attrs:{type:s.icon}}):t._e(),t.labels?e("span",{staticClass:"k-toggles-text",domProps:{innerHTML:t._s(s.text)}}):t._e()],1)])})),0)}),[],!1,null,null,null,null).exports,us={mixins:[je],props:{autocomplete:{type:String,default:"url"},type:{type:String,default:"url"}}};const cs=G({extends:Me,mixins:[us]},null,null,!1,null,null,null,null).exports,ds={install(t){t.component("k-checkbox-input",Ce),t.component("k-checkboxes-input",Ae),t.component("k-date-input",Ie),t.component("k-email-input",Le),t.component("k-list-input",De),t.component("k-multiselect-input",Ne),t.component("k-number-input",Fe),t.component("k-password-input",Ye),t.component("k-radio-input",He),t.component("k-range-input",Ve),t.component("k-select-input",Je),t.component("k-slug-input",We),t.component("k-tags-input",Ze),t.component("k-tel-input",ts),t.component("k-text-input",Me),t.component("k-textarea-input",ss),t.component("k-time-input",is),t.component("k-toggle-input",rs),t.component("k-toggles-input",as),t.component("k-url-input",cs)}};const ps=G({mixins:[Tt],inheritAttrs:!1,props:{autofocus:Boolean,empty:String,fieldsets:Object,fieldsetGroups:Object,group:String,max:{type:Number,default:null},value:{type:Array,default:()=>[]}},data:()=>({opened:[]}),computed:{hasFieldsets(){return Object.keys(this.fieldsets).length},isEmpty(){return 0===this.value.length},isFull(){return null!==this.max&&this.value.length>=this.max}},methods:{focus(){this.$refs.blocks.focus()}}},(function(){var t=this,e=t._self._c;return e("k-field",t._b({staticClass:"k-blocks-field",scopedSlots:t._u([{key:"options",fn:function(){return[t.hasFieldsets?e("k-dropdown",[e("k-button",{attrs:{icon:"dots"},on:{click:function(e){return t.$refs.options.toggle()}}}),e("k-dropdown-content",{ref:"options",attrs:{align:"right"}},[e("k-dropdown-item",{attrs:{disabled:t.isFull,icon:"add"},on:{click:function(e){return t.$refs.blocks.choose(t.value.length)}}},[t._v(" "+t._s(t.$t("add"))+" ")]),e("hr"),e("k-dropdown-item",{attrs:{disabled:t.isEmpty,icon:"template"},on:{click:function(e){return t.$refs.blocks.copyAll()}}},[t._v(" "+t._s(t.$t("copy.all"))+" ")]),e("k-dropdown-item",{attrs:{disabled:t.isFull,icon:"download"},on:{click:function(e){return t.$refs.blocks.pasteboard()}}},[t._v(" "+t._s(t.$t("paste"))+" ")]),e("hr"),e("k-dropdown-item",{attrs:{disabled:t.isEmpty,icon:"trash"},on:{click:function(e){return t.$refs.blocks.confirmToRemoveAll()}}},[t._v(" "+t._s(t.$t("delete.all"))+" ")])],1)],1):t._e()]},proxy:!0}])},"k-field",t.$props,!1),[e("k-blocks",t._g({ref:"blocks",attrs:{autofocus:t.autofocus,compact:!1,empty:t.empty,endpoints:t.endpoints,fieldsets:t.fieldsets,"fieldset-groups":t.fieldsetGroups,group:t.group,max:t.max,value:t.value},on:{close:function(e){t.opened=e},open:function(e){t.opened=e}}},t.$listeners)),t.isEmpty||t.isFull?t._e():e("k-button",{staticClass:"k-field-add-item-button",attrs:{icon:"add",tooltip:t.$t("add")},on:{click:function(e){return t.$refs.blocks.choose(t.value.length)}}})],1)}),[],!1,null,null,null,null).exports,hs={props:{counter:{type:Boolean,default:!0}},computed:{counterOptions(){if(null===this.value||this.disabled||!1===this.counter)return!1;let t=0;return this.value&&(t=Array.isArray(this.value)?this.value.length:String(this.value).length),{count:t,min:this.min??this.minlength,max:this.max??this.maxlength}}}};const ms=G({mixins:[Tt,Mt,Oe,hs],inheritAttrs:!1,methods:{focus(){this.$refs.input.focus()}}},(function(){var t=this,e=t._self._c;return e("k-field",t._b({staticClass:"k-checkboxes-field",attrs:{counter:t.counterOptions}},"k-field",t.$props,!1),[e("k-input",t._g(t._b({ref:"input",attrs:{id:t._uid,theme:"field",type:"checkboxes"}},"k-input",t.$props,!1),t.$listeners))],1)}),[],!1,null,null,null,null).exports;const fs=G({mixins:[Tt,Mt,Te],inheritAttrs:!1,props:{calendar:{type:Boolean,default:!0},icon:{type:String,default:"calendar"},time:{type:[Boolean,Object],default:()=>({})},times:{type:Boolean,default:!0}},data(){return{isInvalid:!1,iso:this.toIso(this.value)}},computed:{isEmpty(){return this.time?null===this.iso.date&&this.iso.time:null===this.iso.date}},watch:{value(t,e){t!==e&&(this.iso=this.toIso(t))}},methods:{focus(){this.$refs.dateInput.focus()},now(){const t=this.$library.dayjs();return{date:t.toISO("date"),time:this.time?t.toISO("time"):"00:00:00"}},onInput(){if(this.isEmpty)return this.$emit("input","");const t=this.$library.dayjs.iso(this.iso.date+" "+this.iso.time);(t||null!==this.iso.date&&null!==this.iso.time)&&this.$emit("input",(null==t?void 0:t.toISO())||"")},onCalendarInput(t){var e;null==(e=this.$refs.calendar)||e.close(),this.onDateInput(t)},onDateInput(t){t&&!this.iso.time&&(this.iso.time=this.now().time),this.iso.date=t,this.onInput()},onDateInvalid(t){this.isInvalid=t},onTimeInput(t){t&&!this.iso.date&&(this.iso.date=this.now().date),this.iso.time=t,this.onInput()},onTimesInput(t){var e;null==(e=this.$refs.times)||e.close(),this.onTimeInput(t+":00")},toIso(t){const e=this.$library.dayjs.iso(t);return{date:(null==e?void 0:e.toISO("date"))||null,time:(null==e?void 0:e.toISO("time"))||null}}}},(function(){var t=this,e=t._self._c;return e("k-field",t._b({staticClass:"k-date-field",attrs:{input:t._uid}},"k-field",t.$props,!1),[e("div",{ref:"body",staticClass:"k-date-field-body",attrs:{"data-invalid":!t.novalidate&&t.isInvalid,"data-theme":"field"}},[e("k-input",t._b({ref:"dateInput",attrs:{id:t._uid,autofocus:t.autofocus,disabled:t.disabled,display:t.display,max:t.max,min:t.min,required:t.required,value:t.value,theme:"field",type:"date"},on:{invalid:t.onDateInvalid,input:t.onDateInput,submit:function(e){return t.$emit("submit")}},scopedSlots:t._u([t.calendar?{key:"icon",fn:function(){return[e("k-dropdown",[e("k-button",{staticClass:"k-input-icon-button",attrs:{icon:t.icon,tooltip:t.$t("date.select")},on:{click:function(e){return t.$refs.calendar.toggle()}}}),e("k-dropdown-content",{ref:"calendar",attrs:{align:"right"}},[e("k-calendar",{attrs:{value:t.value,min:t.min,max:t.max},on:{input:t.onCalendarInput}})],1)],1)]},proxy:!0}:null],null,!0)},"k-input",t.$props,!1)),t.time?e("k-input",{ref:"timeInput",attrs:{disabled:t.disabled,display:t.time.display,required:t.required,step:t.time.step,value:t.iso.time,icon:t.time.icon,theme:"field",type:"time"},on:{input:t.onTimeInput,submit:function(e){return t.$emit("submit")}},scopedSlots:t._u([t.times?{key:"icon",fn:function(){return[e("k-dropdown",[e("k-button",{staticClass:"k-input-icon-button",attrs:{icon:t.time.icon||"clock",tooltip:t.$t("time.select")},on:{click:function(e){return t.$refs.times.toggle()}}}),e("k-dropdown-content",{ref:"times",attrs:{align:"right"}},[e("k-times",{attrs:{display:t.time.display,value:t.value},on:{input:t.onTimesInput}})],1)],1)]},proxy:!0}:null],null,!0)}):t._e()],1)])}),[],!1,null,null,null,null).exports;const gs=G({mixins:[Tt,Mt,Ee],inheritAttrs:!1,props:{link:{type:Boolean,default:!0},icon:{type:String,default:"email"}},computed:{mailto(){var t;return(null==(t=this.value)?void 0:t.length)>0?"mailto:"+this.value:null}},methods:{focus(){this.$refs.input.focus()}}},(function(){var t=this,e=t._self._c;return e("k-field",t._b({staticClass:"k-email-field",attrs:{input:t._uid}},"k-field",t.$props,!1),[e("k-input",t._g(t._b({ref:"input",attrs:{id:t._uid,theme:"field",type:"email"},scopedSlots:t._u([{key:"icon",fn:function(){return[t.link?e("k-button",{staticClass:"k-input-icon-button",attrs:{icon:t.icon,link:t.mailto,tooltip:t.$t("open"),tabindex:"-1",target:"_blank"}}):t._e()]},proxy:!0}])},"k-input",t.$props,!1),t.$listeners))],1)}),[],!1,null,null,null,null).exports,ks={mixins:[Tt],inheritAttrs:!1,props:{empty:String,info:String,link:Boolean,layout:{type:String,default:"list"},max:Number,multiple:Boolean,parent:String,search:Boolean,size:String,text:String,value:{type:Array,default:()=>[]}},data(){return{selected:this.value}},computed:{btnIcon(){return!this.multiple&&this.selected.length>0?"refresh":"add"},btnLabel(){return!this.multiple&&this.selected.length>0?this.$t("change"):this.$t("add")},collection(){return{empty:this.emptyProps,items:this.selected,layout:this.layout,link:this.link,size:this.size,sortable:!this.disabled&&this.selected.length>1}},isInvalid(){return!(!this.required||0!==this.selected.length)||(!!(this.min&&this.selected.lengththis.max))},items(){return this.models.map(this.item)},more(){return!this.max||this.max>this.selected.length}},watch:{value(t){this.selected=t}},methods:{focus(){},item:t=>t,onInput(){this.$emit("input",this.selected)},open(){if(this.disabled)return!1;this.$refs.selector.open({endpoint:this.endpoints.field,max:this.max,multiple:this.multiple,search:this.search,selected:this.selected.map((t=>t.id))})},remove(t){this.selected.splice(t,1),this.onInput()},removeById(t){this.selected=this.selected.filter((e=>e.id!==t)),this.onInput()},select(t){0!==t.length?(this.selected=this.selected.filter((e=>t.filter((t=>t.id===e.id)).length>0)),t.forEach((t=>{0===this.selected.filter((e=>t.id===e.id)).length&&this.selected.push(t)})),this.onInput()):this.selected=[]}}};const bs=G({mixins:[ks],props:{uploads:[Boolean,Object,Array]},computed:{emptyProps(){return{icon:"image",text:this.empty||this.$t("field.files.empty")}},moreUpload(){return!this.disabled&&this.more&&this.uploads},options(){return this.uploads?{icon:this.btnIcon,text:this.btnLabel,options:[{icon:"check",text:this.$t("select"),click:"open"},{icon:"upload",text:this.$t("upload"),click:"upload"}]}:{options:[{icon:"check",text:this.$t("select"),click:()=>this.open()}]}},uploadParams(){return{accept:this.uploads.accept,max:this.max,multiple:this.multiple,url:this.$urls.api+"/"+this.endpoints.field+"/upload"}}},created(){this.$events.$on("file.delete",this.removeById)},destroyed(){this.$events.$off("file.delete",this.removeById)},methods:{drop(t){return!1!==this.uploads&&this.$refs.fileUpload.drop(t,this.uploadParams)},prompt(){if(this.disabled)return!1;this.moreUpload?this.$refs.options.toggle():this.open()},onAction(t){if(this.moreUpload)switch(t){case"open":return this.open();case"upload":return this.$refs.fileUpload.open(this.uploadParams)}},isSelected(t){return this.selected.find((e=>e.id===t.id))},upload(t,e){!1===this.multiple&&(this.selected=[]),e.forEach((t=>{this.isSelected(t)||this.selected.push(t)})),this.onInput(),this.$events.$emit("model.update")}}},(function(){var t=this,e=t._self._c;return e("k-field",t._b({staticClass:"k-files-field",scopedSlots:t._u([t.more&&!t.disabled?{key:"options",fn:function(){return[e("k-button-group",{staticClass:"k-field-options"},[e("k-options-dropdown",t._b({ref:"options",on:{action:t.onAction}},"k-options-dropdown",t.options,!1))],1)]},proxy:!0}:null],null,!0)},"k-field",t.$props,!1),[e("k-dropzone",{attrs:{disabled:!t.moreUpload},on:{drop:t.drop}},[e("k-collection",t._b({on:{empty:t.prompt,sort:t.onInput,sortChange:function(e){return t.$emit("change",e)}},scopedSlots:t._u([{key:"options",fn:function({index:s}){return[t.disabled?t._e():e("k-button",{attrs:{tooltip:t.$t("remove"),icon:"remove"},on:{click:function(e){return t.remove(s)}}})]}}])},"k-collection",t.collection,!1))],1),e("k-files-dialog",{ref:"selector",on:{submit:t.select}}),e("k-upload",{ref:"fileUpload",on:{success:t.upload}})],1)}),[],!1,null,null,null,null).exports;const vs=G({},(function(){return(0,this._self._c)("div",{staticClass:"k-field k-gap-field"})}),[],!1,null,null,null,null).exports;const ys=G({mixins:[wt,Ct],props:{numbered:Boolean}},(function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-headline-field"},[e("k-headline",{attrs:{"data-numbered":t.numbered,size:"large"}},[t._v(" "+t._s(t.label)+" ")]),t.help?e("footer",{staticClass:"k-field-footer"},[t.help?e("k-text",{staticClass:"k-field-help",attrs:{theme:"help",html:t.help}}):t._e()],1):t._e()],1)}),[],!1,null,null,null,null).exports;const $s=G({mixins:[wt,Ct],props:{text:String,theme:{type:String,default:"info"}}},(function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-field k-info-field"},[e("k-headline",[t._v(t._s(t.label))]),e("k-box",{attrs:{theme:t.theme}},[e("k-text",{attrs:{html:t.text}})],1),t.help?e("footer",{staticClass:"k-field-footer"},[t.help?e("k-text",{staticClass:"k-field-help",attrs:{theme:"help",html:t.help}}):t._e()],1):t._e()],1)}),[],!1,null,null,null,null).exports;const _s=G({components:{"k-block-layouts":G({components:{"k-layout":G({components:{"k-layout-column":G({props:{blocks:Array,endpoints:Object,fieldsetGroups:Object,fieldsets:Object,id:String,isSelected:Boolean,width:String}},(function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-column k-layout-column",attrs:{id:t.id,"data-width":t.width,tabindex:"0"},on:{dblclick:function(e){return t.$refs.blocks.choose(t.blocks.length)}}},[e("k-blocks",{ref:"blocks",attrs:{endpoints:t.endpoints,"fieldset-groups":t.fieldsetGroups,fieldsets:t.fieldsets,value:t.blocks,group:"layout"},on:{input:function(e){return t.$emit("input",e)}},nativeOn:{dblclick:function(t){t.stopPropagation()}}})],1)}),[],!1,null,null,null,null).exports},props:{attrs:[Array,Object],columns:Array,disabled:Boolean,endpoints:Object,fieldsetGroups:Object,fieldsets:Object,id:String,isSelected:Boolean,settings:Object},computed:{tabs(){let t=this.settings.tabs;return Object.entries(t).forEach((([e,s])=>{Object.entries(s.fields).forEach((([s])=>{t[e].fields[s].endpoints={field:this.endpoints.field+"/fields/"+s,section:this.endpoints.section,model:this.endpoints.model}}))})),t}}},(function(){var t=this,e=t._self._c;return e("section",{staticClass:"k-layout",attrs:{"data-selected":t.isSelected,tabindex:"0"},on:{click:function(e){return t.$emit("select")}}},[e("k-grid",{staticClass:"k-layout-columns"},t._l(t.columns,(function(s,n){return e("k-layout-column",t._b({key:s.id,attrs:{endpoints:t.endpoints,"fieldset-groups":t.fieldsetGroups,fieldsets:t.fieldsets},on:{input:function(e){return t.$emit("updateColumn",{column:s,columnIndex:n,blocks:e})}}},"k-layout-column",s,!1))})),1),t.disabled?t._e():e("nav",{staticClass:"k-layout-toolbar"},[t.settings?e("k-button",{staticClass:"k-layout-toolbar-button",attrs:{tooltip:t.$t("settings"),icon:"settings"},on:{click:function(e){return t.$refs.drawer.open()}}}):t._e(),e("k-dropdown",[e("k-button",{staticClass:"k-layout-toolbar-button",attrs:{icon:"angle-down"},on:{click:function(e){return t.$refs.options.toggle()}}}),e("k-dropdown-content",{ref:"options",attrs:{align:"right"}},[e("k-dropdown-item",{attrs:{icon:"angle-up"},on:{click:function(e){return t.$emit("prepend")}}},[t._v(" "+t._s(t.$t("insert.before"))+" ")]),e("k-dropdown-item",{attrs:{icon:"angle-down"},on:{click:function(e){return t.$emit("append")}}},[t._v(" "+t._s(t.$t("insert.after"))+" ")]),e("hr"),t.settings?e("k-dropdown-item",{attrs:{icon:"settings"},on:{click:function(e){return t.$refs.drawer.open()}}},[t._v(" "+t._s(t.$t("settings"))+" ")]):t._e(),e("k-dropdown-item",{attrs:{icon:"copy"},on:{click:function(e){return t.$emit("duplicate")}}},[t._v(" "+t._s(t.$t("duplicate"))+" ")]),e("hr"),e("k-dropdown-item",{attrs:{icon:"trash"},on:{click:function(e){return t.$refs.confirmRemoveDialog.open()}}},[t._v(" "+t._s(t.$t("field.layout.delete"))+" ")])],1)],1),e("k-sort-handle")],1),t.settings?e("k-form-drawer",{ref:"drawer",staticClass:"k-layout-drawer",attrs:{tabs:t.tabs,title:t.$t("settings"),value:t.attrs,icon:"settings"},on:{input:function(e){return t.$emit("updateAttrs",e)}}}):t._e(),e("k-remove-dialog",{ref:"confirmRemoveDialog",attrs:{text:t.$t("field.layout.delete.confirm")},on:{submit:function(e){return t.$emit("remove")}}})],1)}),[],!1,null,null,null,null).exports},props:{disabled:Boolean,empty:String,endpoints:Object,fieldsetGroups:Object,fieldsets:Object,layouts:Array,max:Number,settings:Object,value:Array},data(){return{currentLayout:null,nextIndex:null,rows:this.value,selected:null}},computed:{draggableOptions(){return{id:this._uid,handle:!0,list:this.rows}}},watch:{value(){this.rows=this.value}},methods:{async addLayout(t){let e=await this.$api.post(this.endpoints.field+"/layout",{columns:t});this.rows.splice(this.nextIndex,0,e),this.layouts.length>1&&this.$refs.selector.close(),this.save()},duplicateLayout(t,e){let s={...this.$helper.clone(e),id:this.$helper.uuid()};s.columns=s.columns.map((t=>(t.id=this.$helper.uuid(),t.blocks=t.blocks.map((t=>(t.id=this.$helper.uuid(),t))),t))),this.rows.splice(t+1,0,s),this.save()},removeLayout(t){const e=this.rows.findIndex((e=>e.id===t.id));-1!==e&&this.$delete(this.rows,e),this.save()},save(){this.$emit("input",this.rows)},selectLayout(t){this.nextIndex=t,1!==this.layouts.length?this.$refs.selector.open():this.addLayout(this.layouts[0])},updateColumn(t){this.rows[t.layoutIndex].columns[t.columnIndex].blocks=t.blocks,this.save()},updateAttrs(t,e){this.rows[t].attrs=e,this.save()}}},(function(){var t=this,e=t._self._c;return e("div",[t.rows.length?[e("k-draggable",t._b({staticClass:"k-layouts",on:{sort:t.save}},"k-draggable",t.draggableOptions,!1),t._l(t.rows,(function(s,n){return e("k-layout",t._b({key:s.id,attrs:{disabled:t.disabled,endpoints:t.endpoints,"fieldset-groups":t.fieldsetGroups,fieldsets:t.fieldsets,"is-selected":t.selected===s.id,settings:t.settings},on:{append:function(e){return t.selectLayout(n+1)},duplicate:function(e){return t.duplicateLayout(n,s)},prepend:function(e){return t.selectLayout(n)},remove:function(e){return t.removeLayout(s)},select:function(e){t.selected=s.id},updateAttrs:function(e){return t.updateAttrs(n,e)},updateColumn:function(e){return t.updateColumn({layout:s,layoutIndex:n,...e})}}},"k-layout",s,!1))})),1),t.disabled?t._e():e("k-button",{staticClass:"k-field-add-item-button",attrs:{icon:"add",tooltip:t.$t("add")},on:{click:function(e){return t.selectLayout(t.rows.length)}}})]:[e("k-empty",{staticClass:"k-layout-empty",attrs:{icon:"dashboard"},on:{click:function(e){return t.selectLayout(0)}}},[t._v(" "+t._s(t.empty||t.$t("field.layout.empty"))+" ")])],e("k-dialog",{ref:"selector",staticClass:"k-layout-selector",attrs:{"cancel-button":!1,"submit-button":!1,size:"medium"}},[e("k-headline",[t._v(t._s(t.$t("field.layout.select")))]),e("ul",t._l(t.layouts,(function(s,n){return e("li",{key:n,staticClass:"k-layout-selector-option"},[e("k-grid",{nativeOn:{click:function(e){return t.addLayout(s)}}},t._l(s,(function(t,s){return e("k-column",{key:s,attrs:{width:t}})})),1)],1)})),0)],1)],2)}),[],!1,null,null,null,null).exports},mixins:[Tt],inheritAttrs:!1,props:{empty:String,fieldsetGroups:Object,fieldsets:Object,layouts:{type:Array,default:()=>[["1/1"]]},settings:Object,value:{type:Array,default:()=>[]}}},(function(){var t=this,e=t._self._c;return e("k-field",t._b({staticClass:"k-layout-field"},"k-field",t.$props,!1),[e("k-block-layouts",t._b({on:{input:function(e){return t.$emit("input",e)}}},"k-block-layouts",t.$props,!1))],1)}),[],!1,null,null,null,null).exports;const ws=G({},(function(){return(0,this._self._c)("hr",{staticClass:"k-line-field"})}),[],!1,null,null,null,null).exports;const xs=G({mixins:[Tt,Mt],inheritAttrs:!1,props:{marks:[Array,Boolean],value:String},methods:{focus(){this.$refs.input.focus()}}},(function(){var t=this,e=t._self._c;return e("k-field",t._b({staticClass:"k-list-field",attrs:{input:t._uid,counter:!1}},"k-field",t.$props,!1),[e("k-input",t._b({ref:"input",attrs:{id:t._uid,marks:t.marks,value:t.value,type:"list",theme:"field"},on:{input:function(e){return t.$emit("input",e)}}},"k-input",t.$props,!1))],1)}),[],!1,null,null,null,null).exports;const Ss=G({mixins:[Tt,Mt,Pe,hs],inheritAttrs:!1,props:{icon:{type:String,default:"angle-down"}},mounted(){this.$refs.input.$el.setAttribute("tabindex",0)},methods:{blur(t){this.$refs.input.blur(t)},focus(){this.$refs.input.focus()}}},(function(){var t=this,e=t._self._c;return e("k-field",t._b({staticClass:"k-multiselect-field",attrs:{input:t._uid,counter:t.counterOptions},on:{blur:t.blur},nativeOn:{keydown:function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"enter",13,e.key,"Enter")?null:(e.preventDefault(),t.focus.apply(null,arguments))}}},"k-field",t.$props,!1),[e("k-input",t._g(t._b({ref:"input",attrs:{id:t._uid,theme:"field",type:"multiselect"}},"k-input",t.$props,!1),t.$listeners))],1)}),[],!1,null,null,null,null).exports;const Cs=G({mixins:[Tt,Mt,Re],inheritAttrs:!1,methods:{focus(){this.$refs.input.focus()}}},(function(){var t=this,e=t._self._c;return e("k-field",t._b({staticClass:"k-number-field",attrs:{input:t._uid}},"k-field",t.$props,!1),[e("k-input",t._g(t._b({ref:"input",attrs:{id:t._uid,theme:"field",type:"number"}},"k-input",t.$props,!1),t.$listeners))],1)}),[],!1,null,null,null,null).exports;const Os=G({mixins:[Tt,Mt],props:{empty:String,fields:Object,value:[String,Object]},data(){return{object:this.valueToObject(this.value)}},computed:{drawer(){return{icon:"box",tab:"object",tabs:{object:{fields:this.$helper.field.subfields(this,this.fields)}},title:this.label,value:this.object}},isEmpty(){return!this.object||!(!this.object||0!==Object.keys(this.object).length)},isInvalid(){return!0===this.required&&this.isEmpty}},watch:{value(t){this.object=this.valueToObject(t)}},methods:{onAdd(){this.object=this.$helper.field.form(this.fields),this.$emit("input",this.object),this.open()},onCellInput(t,e){Vue.set(this.object,t,e),this.$emit("input",this.object)},onDrawerInput(t){this.object=t,this.$emit("input",this.object)},onRemove(){this.object={},this.$emit("input",this.object)},open(t){if(this.disabled)return!1;this.$refs.drawer.open(null,t)},valueToObject:t=>"object"!=typeof t?null:t}},(function(){var t=this,e=t._self._c;return e("k-field",t._b({staticClass:"k-object-field",scopedSlots:t._u([t.disabled?null:{key:"options",fn:function(){return[t.isEmpty?e("k-button",{attrs:{icon:"add"},on:{click:t.onAdd}}):e("k-button",{attrs:{icon:"remove"},on:{click:t.onRemove}})]},proxy:!0}],null,!0)},"k-field",t.$props,!1),[t.isEmpty?e("k-empty",{attrs:{"data-invalid":t.isInvalid,icon:"box"},on:{click:t.onAdd}},[t._v(" "+t._s(t.empty||t.$t("field.object.empty"))+" ")]):e("table",{staticClass:"k-table k-object-field-table",attrs:{"data-invalid":t.isInvalid}},[e("tbody",[t._l(t.fields,(function(s){return[s.saveable&&t.$helper.field.isVisible(s,t.value)?e("tr",{key:s.name,on:{click:function(e){return t.open(s.name)}}},[e("th",{attrs:{"data-mobile":"true"}},[e("button",{attrs:{type:"button"}},[t._v(t._s(s.label))])]),e("k-table-cell",{attrs:{column:s,field:s,mobile:!0,value:t.object[s.name]},on:{input:function(e){return t.onCellInput(s.name,e)}}})],1):t._e()]}))],2)]),e("k-form-drawer",t._b({ref:"drawer",on:{input:t.onDrawerInput}},"k-form-drawer",t.drawer,!1))],1)}),[],!1,null,null,null,null).exports;const As=G({mixins:[ks],computed:{emptyProps(){return{icon:"page",text:this.empty||this.$t("field.pages.empty")}}}},(function(){var t=this,e=t._self._c;return e("k-field",t._b({staticClass:"k-pages-field",scopedSlots:t._u([{key:"options",fn:function(){return[e("k-button-group",{staticClass:"k-field-options"},[t.more&&!t.disabled?e("k-button",{staticClass:"k-field-options-button",attrs:{icon:t.btnIcon,text:t.btnLabel},on:{click:t.open}}):t._e()],1)]},proxy:!0}])},"k-field",t.$props,!1),[e("k-collection",t._b({on:{empty:t.open,sort:t.onInput,sortChange:function(e){return t.$emit("change",e)}},scopedSlots:t._u([{key:"options",fn:function({index:s}){return[t.disabled?t._e():e("k-button",{attrs:{tooltip:t.$t("remove"),icon:"remove"},on:{click:function(e){return t.remove(s)}}})]}}])},"k-collection",t.collection,!1)),e("k-pages-dialog",{ref:"selector",on:{submit:t.select}})],1)}),[],!1,null,null,null,null).exports;const Ts=G({mixins:[Tt,Mt,qe,hs],inheritAttrs:!1,props:{minlength:{type:Number,default:8},icon:{type:String,default:"key"}},methods:{focus(){this.$refs.input.focus()}}},(function(){var t=this,e=t._self._c;return e("k-field",t._b({staticClass:"k-password-field",attrs:{input:t._uid,counter:t.counterOptions},scopedSlots:t._u([{key:"options",fn:function(){return[t._t("options")]},proxy:!0}],null,!0)},"k-field",t.$props,!1),[e("k-input",t._g(t._b({ref:"input",attrs:{id:t._uid,theme:"field",type:"password"}},"k-input",t.$props,!1),t.$listeners))],1)}),[],!1,null,null,null,null).exports;const Is=G({mixins:[Tt,Mt,ze],inheritAttrs:!1,methods:{focus(){this.$refs.input.focus()}}},(function(){var t=this,e=t._self._c;return e("k-field",t._b({staticClass:"k-radio-field"},"k-field",t.$props,!1),[e("k-input",t._g(t._b({ref:"input",attrs:{id:t._uid,theme:"field",type:"radio"}},"k-input",t.$props,!1),t.$listeners))],1)}),[],!1,null,null,null,null).exports;const js=G({mixins:[Mt,Tt,Ue],inheritAttrs:!1,methods:{focus(){this.$refs.input.focus()}}},(function(){var t=this,e=t._self._c;return e("k-field",t._b({staticClass:"k-range-field",attrs:{input:t._uid}},"k-field",t.$props,!1),[e("k-input",t._g(t._b({ref:"input",attrs:{id:t._uid,theme:"field",type:"range"}},"k-input",t.$props,!1),t.$listeners))],1)}),[],!1,null,null,null,null).exports;const Ms=G({mixins:[Tt,Mt,Ke],inheritAttrs:!1,props:{icon:{type:String,default:"angle-down"}},methods:{focus(){this.$refs.input.focus()}}},(function(){var t=this,e=t._self._c;return e("k-field",t._b({staticClass:"k-select-field",attrs:{input:t._uid}},"k-field",t.$props,!1),[e("k-input",t._g(t._b({ref:"input",attrs:{id:t._uid,theme:"field",type:"select"}},"k-input",t.$props,!1),t.$listeners))],1)}),[],!1,null,null,null,null).exports;const Es=G({mixins:[Tt,Mt,Ge],inheritAttrs:!1,props:{icon:{type:String,default:"url"},path:{type:String},wizard:{type:[Boolean,Object],default:!1}},data(){return{slug:this.value}},computed:{preview(){return void 0!==this.help?this.help:void 0!==this.path?this.path+this.value:null}},watch:{value(){this.slug=this.value}},methods:{focus(){this.$refs.input.focus()},onWizard(){var t;this.formData[null==(t=this.wizard)?void 0:t.field]&&(this.slug=this.formData[this.wizard.field])}}},(function(){var t=this,e=t._self._c;return e("k-field",t._b({staticClass:"k-slug-field",attrs:{input:t._uid,help:t.preview},scopedSlots:t._u([t.wizard&&t.wizard.text?{key:"options",fn:function(){return[e("k-button",{attrs:{text:t.wizard.text,icon:"wand"},on:{click:t.onWizard}})]},proxy:!0}:null],null,!0)},"k-field",t.$props,!1),[e("k-input",t._g(t._b({ref:"input",attrs:{id:t._uid,value:t.slug,theme:"field",type:"slug"}},"k-input",t.$props,!1),t.$listeners))],1)}),[],!1,null,null,null,null).exports;const Ls=G({mixins:[Tt],inheritAttrs:!1,props:{columns:Object,duplicate:{type:Boolean,default:!0},empty:String,fields:Object,limit:Number,max:Number,min:Number,prepend:{type:Boolean,default:!1},sortable:{type:Boolean,default:!0},sortBy:String,value:{type:Array,default:()=>[]}},data(){return{autofocus:null,items:this.toItems(this.value),currentIndex:null,currentModel:null,trash:null,page:1}},computed:{dragOptions(){return{disabled:!this.isSortable,fallbackClass:"k-sortable-row-fallback"}},form(){return this.$helper.field.subfields(this,this.fields)},index(){return this.limit?(this.page-1)*this.limit+1:1},more(){return!0!==this.disabled&&!(this.max&&this.items.length>=this.max)},isInvalid(){return!0!==this.disabled&&(!!(this.min&&this.items.lengththis.max))},isSortable(){return!this.sortBy&&(!this.limit&&(!0!==this.disabled&&(!(this.items.length<=1)&&!1!==this.sortable)))},pagination(){let t=0;return this.limit&&(t=(this.page-1)*this.limit),{page:this.page,offset:t,limit:this.limit,total:this.items.length,align:"center",details:!0}},options(){if(this.disabled)return[];let t=[],e=this.duplicate&&this.more&&null===this.currentIndex;return t.push({icon:"edit",text:this.$t("edit"),click:"edit"}),e&&t.push({icon:"copy",text:this.$t("duplicate"),click:"duplicate"}),t.push({icon:"remove",text:e?this.$t("remove"):null,click:"remove"}),t},paginatedItems(){return this.limit?this.items.slice(this.pagination.offset,this.pagination.offset+this.limit):this.items}},watch:{value(t){t!=this.items&&(this.items=this.toItems(t))}},methods:{add(t){!0===this.prepend?this.items.unshift(t):this.items.push(t)},confirmToRemoveAll(){this.$refs.dialogRemoveAll.open()},focus(){var t,e;null==(e=null==(t=this.$refs.add)?void 0:t.focus)||e.call(t)},jump(t,e){this.open(t+this.pagination.offset,e)},onAdd(){return!0!==this.disabled&&(null!==this.currentIndex?(this.onFormDiscard(),!1):(this.currentIndex="new",this.currentModel=this.$helper.field.form(this.fields),void this.onFormOpen()))},onFormClose(){this.currentIndex=null,this.currentModel=null},onFormDiscard(){if("new"===this.currentIndex){if(0===Object.values(this.currentModel).filter((t=>!1===this.$helper.object.isEmpty(t))).length)return void this.onFormClose()}this.onFormSubmit()},onFormOpen(t=this.autofocus){this.$nextTick((()=>{var e;null==(e=this.$refs.form)||e.focus(t)}))},async onFormPaginate(t){try{await this.save(),this.open(t)}catch(e){}},async onFormSubmit(){try{await this.save(),this.onFormClose()}catch(t){}},onInput(t=this.items){this.$emit("input",t)},onOption(t,e,s){switch(t){case"remove":this.onFormClose(),this.trash=s+this.pagination.offset,this.$refs.remove.open();break;case"duplicate":this.add(this.items[s+this.pagination.offset]),this.onInput();break;case"edit":this.open(s)}},onRemove(){if(null===this.trash)return!1;this.items.splice(this.trash,1),this.trash=null,this.$refs.remove.close(),this.onInput(),0===this.paginatedItems.length&&this.page>1&&this.page--,this.items=this.sort(this.items)},onRemoveAll(){this.items=[],this.onInput(),this.$refs.dialogRemoveAll.close()},open(t,e){this.currentIndex=t,this.currentModel=this.$helper.clone(this.items[t]),this.onFormOpen(e)},paginate({page:t}){this.page=t},sort(t){return this.sortBy?t.sortBy(this.sortBy):t},async save(){if(null!==this.currentIndex&&void 0!==this.currentIndex)try{return await this.validate(this.currentModel),"new"===this.currentIndex?this.add(this.currentModel):this.items[this.currentIndex]=this.currentModel,this.items=this.sort(this.items),this.onInput(),!0}catch(t){throw this.$store.dispatch("notification/error",{message:this.$t("error.form.incomplete"),details:t}),t}},toItems(t){return!1===Array.isArray(t)?[]:this.sort(t)},async validate(t){const e=await this.$api.post(this.endpoints.field+"/validate",t);if(e.length>0)throw e;return!0},onFormInput(t){this.currentModel=t,this.$emit("formInput",t)}}},(function(){var t=this,e=t._self._c;return e("k-field",t._b({staticClass:"k-structure-field",nativeOn:{click:function(t){t.stopPropagation()}},scopedSlots:t._u([{key:"options",fn:function(){return[e("k-dropdown",[e("k-button",{attrs:{disabled:null!==t.currentIndex,icon:"dots"},on:{click:function(e){return t.$refs.options.toggle()}}}),e("k-dropdown-content",{ref:"options",attrs:{align:"right"}},[e("k-dropdown-item",{attrs:{disabled:!t.more,icon:"add"},on:{click:t.onAdd}},[t._v(" "+t._s(t.$t("add"))+" ")]),e("k-dropdown-item",{attrs:{disabled:0===t.items.length||t.disabled,icon:"trash"},on:{click:t.confirmToRemoveAll}},[t._v(" "+t._s(t.$t("delete.all"))+" ")])],1)],1)]},proxy:!0}])},"k-field",t.$props,!1),[null!==t.currentIndex?e("k-structure-form",{ref:"form",attrs:{fields:t.form,index:t.currentIndex,total:t.items.length,value:t.currentModel},on:{close:t.onFormClose,discard:t.onFormDiscard,input:t.onFormInput,paginate:function(e){return t.onFormPaginate(e.offset)},submit:t.onFormSubmit}}):0===t.items.length?e("k-empty",{attrs:{"data-invalid":t.isInvalid,icon:"list-bullet"},on:{click:t.onAdd}},[t._v(" "+t._s(t.empty||t.$t("field.structure.empty"))+" ")]):[e("k-table",{attrs:{columns:t.columns,disabled:t.disabled,fields:t.fields,empty:t.$t("field.structure.empty"),index:t.index,options:t.options,pagination:!!t.limit&&t.pagination,rows:t.paginatedItems,sortable:t.isSortable,"data-invalid":t.isInvalid},on:{cell:function(e){return t.jump(e.rowIndex,e.columnIndex)},input:t.onInput,option:t.onOption,paginate:t.paginate}}),t.more?e("k-button",{staticClass:"k-field-add-item-button",attrs:{icon:"add",tooltip:t.$t("add")},on:{click:t.onAdd}}):t._e(),t.disabled?t._e():e("k-remove-dialog",{ref:"remove",attrs:{theme:"negative","submit-button":t.$t("delete"),text:t.$t("field.structure.delete.confirm")},on:{submit:t.onRemove}}),e("k-remove-dialog",{ref:"dialogRemoveAll",attrs:{text:t.$t("field.structure.delete.confirm.all")},on:{submit:t.onRemoveAll}})]],2)}),[],!1,null,null,null,null).exports;const Bs=G({mixins:[Tt,Mt,Xe,hs],inheritAttrs:!1,methods:{focus(){this.$refs.input.focus()}}},(function(){var t=this,e=t._self._c;return e("k-field",t._b({staticClass:"k-tags-field",attrs:{input:t._uid,counter:t.counterOptions}},"k-field",t.$props,!1),[e("k-input",t._g(t._b({ref:"input",attrs:{id:t._uid,theme:"field",type:"tags"}},"k-input",t.$props,!1),t.$listeners))],1)}),[],!1,null,null,null,null).exports;const Ds=G({mixins:[Tt,Mt,Qe],inheritAttrs:!1,props:{icon:{type:String,default:"phone"}},methods:{focus(){this.$refs.input.focus()}}},(function(){var t=this,e=t._self._c;return e("k-field",t._b({staticClass:"k-tel-field",attrs:{input:t._uid}},"k-field",t.$props,!1),[e("k-input",t._g(t._b({ref:"input",attrs:{id:t._uid,theme:"field",type:"tel"}},"k-input",t.$props,!1),t.$listeners))],1)}),[],!1,null,null,null,null).exports;const Ps=G({mixins:[Tt,Mt,je,hs],inheritAttrs:!1,computed:{inputType(){return this.$helper.isComponent(`k-${this.type}-input`)?this.type:"text"}},methods:{focus(){this.$refs.input.focus()},select(){this.$refs.input.select()}}},(function(){var t=this,e=t._self._c;return e("k-field",t._b({staticClass:"k-text-field",attrs:{input:t._uid,counter:t.counterOptions},scopedSlots:t._u([{key:"options",fn:function(){return[t._t("options")]},proxy:!0}],null,!0)},"k-field",t.$props,!1),[e("k-input",t._g(t._b({ref:"input",attrs:{id:t._uid,type:t.inputType,theme:"field"}},"k-input",t.$props,!1),t.$listeners))],1)}),[],!1,null,null,null,null).exports;const Ns=G({mixins:[Tt,Mt,es,hs],inheritAttrs:!1,methods:{focus(){this.$refs.input.focus()}}},(function(){var t=this,e=t._self._c;return e("k-field",t._b({staticClass:"k-textarea-field",attrs:{input:t._uid,counter:t.counterOptions}},"k-field",t.$props,!1),[e("k-input",t._g(t._b({ref:"input",attrs:{id:t._uid,type:"textarea",theme:"field"}},"k-input",t.$props,!1),t.$listeners))],1)}),[],!1,null,null,null,null).exports;const Rs=G({mixins:[Tt,Mt,ns],inheritAttrs:!1,props:{icon:{type:String,default:"clock"},times:{type:Boolean,default:!0}},methods:{focus(){this.$refs.input.focus()},select(t){var e;this.$emit("input",t),null==(e=this.$refs.times)||e.close()}}},(function(){var t=this,e=t._self._c;return e("k-field",t._b({staticClass:"k-time-field",attrs:{input:t._uid}},"k-field",t.$props,!1),[e("k-input",t._b({ref:"input",attrs:{id:t._uid,theme:"field",type:"time"},on:{input:function(e){return t.$emit("input",e||"")}},scopedSlots:t._u([t.times?{key:"icon",fn:function(){return[e("k-dropdown",[e("k-button",{staticClass:"k-input-icon-button",attrs:{icon:t.icon||"clock",tooltip:t.$t("time.select")},on:{click:function(e){return t.$refs.times.toggle()}}}),e("k-dropdown-content",{ref:"times",attrs:{align:"right"}},[e("k-times",{attrs:{display:t.display,value:t.value},on:{input:t.select}})],1)],1)]},proxy:!0}:null],null,!0)},"k-input",t.$props,!1))],1)}),[],!1,null,null,null,null).exports;const Fs=G({mixins:[Tt,Mt,os],inheritAttrs:!1,methods:{focus(){this.$refs.input.focus()}}},(function(){var t=this,e=t._self._c;return e("k-field",t._b({staticClass:"k-toggle-field",attrs:{input:t._uid}},"k-field",t.$props,!1),[e("k-input",t._g(t._b({ref:"input",attrs:{id:t._uid,theme:"field",type:"toggle"}},"k-input",t.$props,!1),t.$listeners))],1)}),[],!1,null,null,null,null).exports;const qs=G({mixins:[Tt,Mt,ls],inheritAttrs:!1,methods:{focus(){this.$refs.input.focus()},onInput(t){this.$emit("input",t)}}},(function(){var t=this,e=t._self._c;return e("k-field",t._b({staticClass:"k-toggles-field"},"k-field",t.$props,!1),[e("k-input",t._g(t._b({ref:"input",class:{grow:t.grow},attrs:{id:t._uid,theme:"field",type:"toggles"}},"k-input",t.$props,!1),t.$listeners))],1)}),[],!1,null,null,null,null).exports;const Ys=G({mixins:[Tt,Mt,us],inheritAttrs:!1,props:{link:{type:Boolean,default:!0},icon:{type:String,default:"url"}},computed:{isValidUrl(){return""!==this.value&&!0===this.$helper.url.isUrl(this.value,!0)}},methods:{focus(){this.$refs.input.focus()}}},(function(){var t=this,e=t._self._c;return e("k-field",t._b({staticClass:"k-url-field",attrs:{input:t._uid}},"k-field",t.$props,!1),[e("k-input",t._g(t._b({ref:"input",attrs:{id:t._uid,theme:"field",type:"url"},scopedSlots:t._u([{key:"icon",fn:function(){return[t.link&&t.isValidUrl?e("k-button",{staticClass:"k-input-icon-button",attrs:{icon:t.icon,link:t.value,tooltip:t.$t("open"),tabindex:"-1",target:"_blank"}}):t._e()]},proxy:!0}])},"k-input",t.$props,!1),t.$listeners))],1)}),[],!1,null,null,null,null).exports;const zs=G({mixins:[ks],computed:{emptyProps(){return{icon:"users",text:this.empty||this.$t("field.users.empty")}}}},(function(){var t=this,e=t._self._c;return e("k-field",t._b({staticClass:"k-users-field",scopedSlots:t._u([{key:"options",fn:function(){return[e("k-button-group",{staticClass:"k-field-options"},[t.more&&!t.disabled?e("k-button",{staticClass:"k-field-options-button",attrs:{icon:t.btnIcon,text:t.btnLabel},on:{click:t.open}}):t._e()],1)]},proxy:!0}])},"k-field",t.$props,!1),[e("k-collection",t._b({on:{empty:t.open,sort:t.onInput,sortChange:function(e){return t.$emit("change",e)}},scopedSlots:t._u([{key:"options",fn:function({index:s}){return[t.disabled?t._e():e("k-button",{attrs:{tooltip:t.$t("remove"),icon:"remove"},on:{click:function(e){return t.remove(s)}}})]}}])},"k-collection",t.collection,!1)),e("k-users-dialog",{ref:"selector",on:{submit:t.select}})],1)}),[],!1,null,null,null,null).exports;const Hs=G({mixins:[Tt,Mt,be],inheritAttrs:!1,methods:{focus(){this.$refs.input.focus()}}},(function(){var t=this,e=t._self._c;return e("k-field",t._b({staticClass:"k-writer-field",attrs:{input:t._uid,counter:!1}},"k-field",t.$props,!1),[e("k-input",t._b({attrs:{after:t.after,before:t.before,icon:t.icon,theme:"field"}},"k-input",t.$props,!1),[e("k-writer",t._b({ref:"input",staticClass:"k-writer-field-input",attrs:{value:t.value},on:{input:function(e){return t.$emit("input",e)}}},"k-writer",t.$props,!1))],1)],1)}),[],!1,null,null,null,null).exports,Us={install(t){t.component("k-blocks-field",ps),t.component("k-checkboxes-field",ms),t.component("k-date-field",fs),t.component("k-email-field",gs),t.component("k-files-field",bs),t.component("k-gap-field",vs),t.component("k-headline-field",ys),t.component("k-info-field",$s),t.component("k-layout-field",_s),t.component("k-line-field",ws),t.component("k-list-field",xs),t.component("k-multiselect-field",Ss),t.component("k-number-field",Cs),t.component("k-object-field",Os),t.component("k-pages-field",As),t.component("k-password-field",Ts),t.component("k-radio-field",Is),t.component("k-range-field",js),t.component("k-select-field",Ms),t.component("k-slug-field",Es),t.component("k-structure-field",Ls),t.component("k-tags-field",Bs),t.component("k-text-field",Ps),t.component("k-textarea-field",Ns),t.component("k-tel-field",Ds),t.component("k-time-field",Rs),t.component("k-toggle-field",Fs),t.component("k-toggles-field",qs),t.component("k-url-field",Ys),t.component("k-users-field",zs),t.component("k-writer-field",Hs)}},Vs={install(t){t.component("k-calendar",mt),t.component("k-counter",ft),t.component("k-autocomplete",ht),t.component("k-form",gt),t.component("k-form-buttons",kt),t.component("k-form-indicator",bt),t.component("k-field",It),t.component("k-fieldset",jt),t.component("k-input",Et),t.component("k-login",Lt),t.component("k-login-code",Bt),t.component("k-times",Dt),t.component("k-upload",Pt),t.component("k-writer",ve),t.component("k-login-alert",ye),t.component("k-structure-form",$e),t.component("k-toolbar",we),t.component("k-toolbar-email-dialog",xe),t.component("k-toolbar-link-dialog",Se),t.use(ds),t.use(Us)}};const Ks=G({props:{cover:Boolean,ratio:String},computed:{ratioPadding(){return this.$helper.ratio(this.ratio)}}},(function(){var t=this;return(0,t._self._c)("span",{staticClass:"k-aspect-ratio",style:{"padding-bottom":t.ratioPadding},attrs:{"data-cover":t.cover}},[t._t("default")],2)}),[],!1,null,null,null,null).exports;const Js=G({},(function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-bar"},[t.$slots.left?e("div",{staticClass:"k-bar-slot",attrs:{"data-position":"left"}},[t._t("left")],2):t._e(),t.$slots.center?e("div",{staticClass:"k-bar-slot",attrs:{"data-position":"center"}},[t._t("center")],2):t._e(),t.$slots.right?e("div",{staticClass:"k-bar-slot",attrs:{"data-position":"right"}},[t._t("right")],2):t._e()])}),[],!1,null,null,null,null).exports;const Gs=G({props:{theme:{type:String,default:"none"},text:String,html:{type:Boolean,default:!1}}},(function(){var t=this,e=t._self._c;return e("div",t._g({staticClass:"k-box",attrs:{"data-theme":t.theme}},t.$listeners),[t._t("default",(function(){return[t.html?e("k-text",{attrs:{html:t.text}}):e("k-text",[t._v(" "+t._s(t.text)+" ")])]}))],2)}),[],!1,null,null,null,null).exports;const Ws=G({inheritAttrs:!1,props:{back:String,color:String,element:{type:String,default:"li"},image:Object,link:String,text:String}},(function(){var t=this,e=t._self._c;return e(t.link?"k-link":"p",{tag:"component",staticClass:"k-bubble",style:{color:t.$helper.color(t.color),background:t.$helper.color(t.back)},attrs:{to:t.link},nativeOn:{click:function(t){t.stopPropagation()}}},[t.image?e("k-item-image",{attrs:{image:t.image,layout:"list"}}):t._e(),t._v(" "+t._s(t.text)+" ")],1)}),[],!1,null,null,null,null).exports;const Xs=G({inheritAttrs:!1,props:{bubbles:Array},computed:{items(){let t=this.bubbles;return"string"==typeof t&&(t=t.split(",")),t.map((t=>"string"==typeof t?{text:t}:t))}}},(function(){var t=this,e=t._self._c;return e("ul",{staticClass:"k-bubbles"},t._l(t.items,(function(s,n){return e("li",{key:n},[e("k-bubble",t._b({},"k-bubble",s,!1))],1)})),0)}),[],!1,null,null,null,null).exports;const Zs=G({props:{columns:{type:[Object,Array],default:()=>({})},empty:Object,help:String,items:{type:[Array,Object],default:()=>[]},layout:{type:String,default:"list"},link:{type:Boolean,default:!0},size:String,sortable:Boolean,pagination:{type:[Boolean,Object],default:()=>!1}},computed:{hasPagination(){return!1!==this.pagination&&(!0!==this.paginationOptions.hide&&!(this.pagination.total<=this.pagination.limit))},hasFooter(){return!(!this.hasPagination&&!this.help)},paginationOptions(){return{limit:10,details:!0,keys:!1,total:0,hide:!1,..."object"!=typeof this.pagination?{}:this.pagination}}},watch:{$props(){this.$forceUpdate()}},methods:{onEmpty(t){t.stopPropagation(),this.$emit("empty")},onOption(...t){this.$emit("action",...t),this.$emit("option",...t)}}},(function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-collection"},[t.items.length?e("k-items",{attrs:{columns:t.columns,items:t.items,layout:t.layout,link:t.link,size:t.size,sortable:t.sortable},on:{change:function(e){return t.$emit("change",e)},item:function(e){return t.$emit("item",e)},option:t.onOption,sort:function(e){return t.$emit("sort",e)}},scopedSlots:t._u([{key:"options",fn:function({item:e,itemIndex:s}){return[t._t("options",null,null,{item:e,index:s})]}}],null,!0)}):e("k-empty",t._g(t._b({attrs:{layout:t.layout}},"k-empty",t.empty,!1),t.$listeners.empty?{click:t.onEmpty}:{})),t.hasFooter?e("footer",{staticClass:"k-collection-footer"},[t.help?e("k-text",{staticClass:"k-collection-help",attrs:{theme:"help",html:t.help}}):t._e(),e("div",{staticClass:"k-collection-pagination"},[t.hasPagination?e("k-pagination",t._b({on:{paginate:function(e){return t.$emit("paginate",e)}}},"k-pagination",t.paginationOptions,!1)):t._e()],1)],1):t._e()],1)}),[],!1,null,null,null,null).exports;const Qs=G({props:{width:String,sticky:Boolean}},(function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-column",attrs:{"data-width":t.width,"data-sticky":t.sticky}},[e("div",[t._t("default")],2)])}),[],!1,null,null,null,null).exports;const tn=G({props:{disabled:{type:Boolean,default:!1}},data:()=>({files:[],dragging:!1,over:!1}),methods:{cancel(){this.reset()},reset(){this.dragging=!1,this.over=!1},onDrop(t){return!0===this.disabled||!1===this.$helper.isUploadEvent(t)?this.reset():(this.$events.$emit("dropzone.drop"),this.files=t.dataTransfer.files,this.$emit("drop",this.files),void this.reset())},onEnter(t){!1===this.disabled&&this.$helper.isUploadEvent(t)&&(this.dragging=!0)},onLeave(){this.reset()},onOver(t){!1===this.disabled&&this.$helper.isUploadEvent(t)&&(t.dataTransfer.dropEffect="copy",this.over=!0)}}},(function(){var t=this;return(0,t._self._c)("div",{staticClass:"k-dropzone",attrs:{"data-dragging":t.dragging,"data-over":t.over},on:{dragenter:t.onEnter,dragleave:t.onLeave,dragover:t.onOver,drop:t.onDrop}},[t._t("default")],2)}),[],!1,null,null,null,null).exports;const en=G({props:{text:String,icon:String,layout:{type:String,default:"list"}},computed:{element(){return void 0!==this.$listeners.click?"button":"div"}}},(function(){var t=this,e=t._self._c;return e(t.element,t._g({tag:"component",staticClass:"k-empty",attrs:{"data-layout":t.layout,type:"button"===t.element&&"button"}},t.$listeners),[t.icon?e("k-icon",{attrs:{type:t.icon}}):t._e(),e("p",[t._t("default",(function(){return[t._v(t._s(t.text))]}))],2)],1)}),[],!1,null,null,null,null).exports;const sn=G({props:{details:Array,image:Object,url:String}},(function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-file-preview"},[e("k-view",{staticClass:"k-file-preview-layout"},[e("div",{staticClass:"k-file-preview-image"},[e("k-link",{staticClass:"k-file-preview-image-link",attrs:{to:t.url,title:t.$t("open"),target:"_blank"}},[e("k-item-image",{attrs:{image:t.image,layout:"cards"}})],1)],1),e("div",{staticClass:"k-file-preview-details"},[e("ul",t._l(t.details,(function(s){return e("li",{key:s.title},[e("h3",[t._v(t._s(s.title))]),e("p",[s.link?e("k-link",{attrs:{to:s.link,tabindex:"-1",target:"_blank"}},[t._v(" /"+t._s(s.text)+" ")]):[t._v(" "+t._s(s.text)+" ")]],2)])})),0)])])],1)}),[],!1,null,null,null,null).exports;const nn=G({props:{gutter:String}},(function(){var t=this;return(0,t._self._c)("div",{staticClass:"k-grid",attrs:{"data-gutter":t.gutter}},[t._t("default")],2)}),[],!1,null,null,null,null).exports;const on=G({props:{editable:Boolean,tab:String,tabs:{type:Array,default:()=>[]}},computed:{tabsWithBadges(){const t=Object.keys(this.$store.getters["content/changes"]());return this.tabs.map((e=>{let s=[];return Object.values(e.columns).forEach((t=>{Object.values(t.sections).forEach((t=>{"fields"===t.type&&Object.keys(t.fields).forEach((t=>{s.push(t)}))}))})),e.badge=s.filter((e=>t.includes(e.toLowerCase()))).length,e}))}}},(function(){var t=this,e=t._self._c;return e("header",{staticClass:"k-header",attrs:{"data-editable":t.editable,"data-tabs":t.tabsWithBadges.length>1}},[e("k-headline",{attrs:{tag:"h1",size:"huge"}},[t.editable&&t.$listeners.edit?e("span",{staticClass:"k-headline-editable",on:{click:function(e){return t.$emit("edit")}}},[t._t("default"),e("k-icon",{attrs:{type:"edit"}})],2):t._t("default")],2),t.$slots.left||t.$slots.right?e("k-bar",{staticClass:"k-header-buttons",scopedSlots:t._u([{key:"left",fn:function(){return[t._t("left")]},proxy:!0},{key:"right",fn:function(){return[t._t("right")]},proxy:!0}],null,!0)}):t._e(),e("k-tabs",{attrs:{tab:t.tab,tabs:t.tabsWithBadges,theme:"notice"}})],1)}),[],!1,null,null,null,null).exports;const rn=G({inheritAttrs:!1},(function(){var t=this,e=t._self._c;return e("k-panel",{staticClass:"k-panel-inside",attrs:{tabindex:"0"}},[e("header",{staticClass:"k-panel-header"},[e("k-topbar",{attrs:{breadcrumb:t.$view.breadcrumb,license:t.$license,menu:t.$menu,view:t.$view}})],1),e("main",{staticClass:"k-panel-view scroll-y"},[t._t("default")],2),t._t("footer")],2)}),[],!1,null,null,null,null).exports;const ln=G({inheritAttrs:!1,props:{data:Object,flag:Object,image:[Object,Boolean],info:String,layout:{type:String,default:"list"},link:{type:[Boolean,String,Function]},options:{type:[Array,Function,String]},sortable:Boolean,target:String,text:String,width:String},computed:{hasFigure(){return!1!==this.image&&Object.keys(this.image).length>0},title(){return this.text||"-"}},methods:{onOption(t){this.$emit("action",t),this.$emit("option",t)}}},(function(){var t=this,e=t._self._c;return e("article",t._b({staticClass:"k-item",class:!!t.layout&&"k-"+t.layout+"-item",attrs:{"data-has-figure":t.hasFigure,"data-has-flag":Boolean(t.flag),"data-has-info":Boolean(t.info),"data-has-options":Boolean(t.options),tabindex:"-1"},on:{click:function(e){return t.$emit("click",e)},dragstart:function(e){return t.$emit("drag",e)}}},"article",t.data,!1),[t._t("image",(function(){return[t.hasFigure?e("k-item-image",{attrs:{image:t.image,layout:t.layout,width:t.width}}):t._e()]})),t.sortable?e("k-sort-handle",{staticClass:"k-item-sort-handle"}):t._e(),e("header",{staticClass:"k-item-content"},[t._t("default",(function(){return[e("h3",{staticClass:"k-item-title"},[!1!==t.link?e("k-link",{staticClass:"k-item-title-link",attrs:{target:t.target,to:t.link}},[e("span",{domProps:{innerHTML:t._s(t.title)}})]):e("span",{domProps:{innerHTML:t._s(t.title)}})],1),t.info?e("p",{staticClass:"k-item-info",domProps:{innerHTML:t._s(t.info)}}):t._e()]}))],2),t.flag||t.options||t.$slots.options?e("footer",{staticClass:"k-item-footer"},[e("nav",{staticClass:"k-item-buttons",on:{click:function(t){t.stopPropagation()}}},[t.flag?e("k-status-icon",t._b({},"k-status-icon",t.flag,!1)):t._e(),t._t("options",(function(){return[t.options?e("k-options-dropdown",{staticClass:"k-item-options-dropdown",attrs:{options:t.options},on:{option:t.onOption}}):t._e()]}))],2)]):t._e()],2)}),[],!1,null,null,null,null).exports;const an=G({inheritAttrs:!1,props:{image:[Object,Boolean],layout:{type:String,default:"list"},width:String},computed:{back(){return this.image.back||"black"},ratio(){return"cards"===this.layout&&this.image.ratio||"1/1"},size(){switch(this.layout){case"cards":return"large";case"cardlets":return"medium";default:return"regular"}},sizes(){switch(this.width){case"1/2":case"2/4":return"(min-width: 30em) and (max-width: 65em) 59em, (min-width: 65em) 44em, 27em";case"1/3":return"(min-width: 30em) and (max-width: 65em) 59em, (min-width: 65em) 29.333em, 27em";case"1/4":return"(min-width: 30em) and (max-width: 65em) 59em, (min-width: 65em) 22em, 27em";case"2/3":return"(min-width: 30em) and (max-width: 65em) 59em, (min-width: 65em) 27em, 27em";case"3/4":return"(min-width: 30em) and (max-width: 65em) 59em, (min-width: 65em) 66em, 27em";default:return"(min-width: 30em) and (max-width: 65em) 59em, (min-width: 65em) 88em, 27em"}}}},(function(){var t=this,e=t._self._c;return t.image?e("div",{staticClass:"k-item-figure",style:{background:t.$helper.color(t.back)}},[t.image.src?e("k-image",{staticClass:"k-item-image",attrs:{cover:t.image.cover,ratio:t.ratio,sizes:t.sizes,src:t.image.src,srcset:t.image.srcset}}):e("k-aspect-ratio",{attrs:{ratio:t.ratio}},[e("k-icon",{staticClass:"k-item-icon",attrs:{color:t.$helper.color(t.image.color),type:t.image.icon}})],1)],1):t._e()}),[],!1,null,null,null,null).exports;const un=G({inheritAttrs:!1,props:{columns:{type:[Object,Array],default:()=>({})},items:{type:Array,default:()=>[]},layout:{type:String,default:"list"},link:{type:Boolean,default:!0},image:{type:[Object,Boolean],default:()=>({})},sortable:Boolean,empty:{type:[String,Object]},size:{type:String,default:"default"}},computed:{dragOptions(){return{sort:this.sortable,disabled:!1===this.sortable,draggable:".k-draggable-item"}},table(){return{columns:this.columns,rows:this.items,sortable:this.sortable}}},methods:{onDragStart(t,e){this.$store.dispatch("drag",{type:"text",data:e})},onOption(t,e,s){this.$emit("option",t,e,s)},imageOptions(t){let e=this.image,s=t.image;return!1!==e&&!1!==s&&("object"!=typeof e&&(e={}),"object"!=typeof s&&(s={}),{...s,...e})}}},(function(){var t=this,e=t._self._c;return"table"===t.layout?e("k-table",t._b({on:{change:function(e){return t.$emit("change",e)},sort:function(e){return t.$emit("sort",e)},option:t.onOption}},"k-table",t.table,!1)):e("k-draggable",{staticClass:"k-items",class:"k-"+t.layout+"-items",attrs:{handle:!0,options:t.dragOptions,"data-layout":t.layout,"data-size":t.size,list:t.items},on:{change:function(e){return t.$emit("change",e)},end:function(e){return t.$emit("sort",t.items,e)}}},[t._l(t.items,(function(s,n){return[t._t("default",(function(){return[e("k-item",t._b({key:s.id||n,class:{"k-draggable-item":t.sortable&&s.sortable},attrs:{image:t.imageOptions(s),layout:t.layout,link:!!t.link&&s.link,sortable:t.sortable&&s.sortable,width:s.column},on:{click:function(e){return t.$emit("item",s,n)},drag:function(e){return t.onDragStart(e,s.dragText)},option:function(e){return t.onOption(e,s,n)}},nativeOn:{mouseover:function(e){return t.$emit("hover",e,s,n)}},scopedSlots:t._u([{key:"options",fn:function(){return[t._t("options",null,null,{item:s,itemIndex:n})]},proxy:!0}],null,!0)},"k-item",s,!1))]}),null,{item:s,itemIndex:n})]}))],2)}),[],!1,null,null,null,null).exports;const cn=G({inheritAttrs:!0,props:{autofocus:{type:Boolean,default:!0},centered:{type:Boolean,default:!1},dimmed:{type:Boolean,default:!0},loading:{type:Boolean,default:!1}},data:()=>({isOpen:!1,scrollTop:0}),methods:{close(){!1!==this.isOpen&&(this.isOpen=!1,this.$emit("close"),this.restoreScrollPosition(),this.$events.$off("keydown.esc",this.close))},focus(){var t,e;let s=this.$refs.overlay.querySelector("\n [autofocus],\n [data-autofocus]\n ");return null===s&&(s=this.$refs.overlay.querySelector("\n input,\n textarea,\n select,\n button\n ")),"function"==typeof(null==s?void 0:s.focus)?s.focus():"function"==typeof(null==(e=null==(t=this.$slots.default[0])?void 0:t.context)?void 0:e.focus)?this.$slots.default[0].context.focus():void 0},open(){!0!==this.isOpen&&(this.storeScrollPosition(),this.isOpen=!0,this.$emit("open"),this.$events.$on("keydown.esc",this.close),setTimeout((()=>{!0===this.autofocus&&this.focus(),document.querySelector(".k-overlay > *").addEventListener("mousedown",(t=>t.stopPropagation())),this.$emit("ready")}),1))},restoreScrollPosition(){const t=document.querySelector(".k-panel-view");(null==t?void 0:t.scrollTop)&&(t.scrollTop=this.scrollTop)},storeScrollPosition(){const t=document.querySelector(".k-panel-view");(null==t?void 0:t.scrollTop)?this.scrollTop=t.scrollTop:this.scrollTop=0}}},(function(){var t=this,e=t._self._c;return t.isOpen?e("portal",[e("div",t._g({ref:"overlay",staticClass:"k-overlay",class:t.$vnode.data.staticClass,attrs:{"data-centered":t.loading||t.centered,"data-dimmed":t.dimmed,"data-loading":t.loading,dir:t.$translation.direction},on:{mousedown:t.close}},t.$listeners),[t.loading?e("k-loader",{staticClass:"k-overlay-loader"}):t._t("default",null,{close:t.close,isOpen:t.isOpen})],2)]):t._e()}),[],!1,null,null,null,null).exports;const dn=G({computed:{defaultLanguage(){return!!this.$language&&this.$language.default},dialog(){return this.$helper.clone(this.$store.state.dialog)},dir(){return this.$translation.direction},language(){return this.$language?this.$language.code:null},role(){return this.$user?this.$user.role:null},user(){return this.$user?this.$user.id:null}},watch:{dir:{handler(){document.body.dir=this.dir},immediate:!0}},created(){this.$events.$on("drop",this.drop)},destroyed(){this.$events.$off("drop",this.drop)},methods:{drop(){this.$store.dispatch("drag",null)}}},(function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-panel",attrs:{"data-dragging":t.$store.state.drag,"data-loading":t.$store.state.isLoading,"data-language":t.language,"data-language-default":t.defaultLanguage,"data-role":t.role,"data-translation":t.$translation.code,"data-user":t.user,dir:t.dir}},[t._t("default"),t.$store.state.dialog&&t.$store.state.dialog.props?[e("k-fiber-dialog",t._b({},"k-fiber-dialog",t.dialog,!1))]:t._e(),!1!==t.$store.state.fatal?e("k-fatal",{attrs:{html:t.$store.state.fatal}}):t._e(),!1===t.$system.isLocal?e("k-offline-warning"):t._e(),e("k-icons")],2)}),[],!1,null,null,null,null).exports;const pn=G({props:{reports:Array,size:{type:String,default:"large"}},methods:{component(t){return null!==this.target(t)?"k-link":"div"},target(t){return t.link?t.link:t.click?t.click:t.dialog?()=>this.$dialog(t.dialog):null}}},(function(){var t=this,e=t._self._c;return e("dl",{staticClass:"k-stats",attrs:{"data-size":t.size}},t._l(t.reports,(function(s,n){return e(t.component(s),{key:n,tag:"component",staticClass:"k-stat",attrs:{"data-theme":s.theme,to:t.target(s)}},[e("dt",{staticClass:"k-stat-label"},[t._v(t._s(s.label))]),e("dd",{staticClass:"k-stat-value"},[t._v(t._s(s.value))]),e("dd",{staticClass:"k-stat-info"},[t._v(t._s(s.info))])])})),1)}),[],!1,null,null,null,null).exports;const hn=G({inheritAttrs:!1,props:{columns:Object,disabled:Boolean,fields:{type:Object,default:()=>({})},empty:String,index:{type:[Number,Boolean],default:1},rows:Array,options:[Array,Function],pagination:[Object,Boolean],sortable:Boolean},data(){return{values:this.rows}},computed:{colspan(){let t=this.columnsCount;return this.hasIndexColumn&&t++,this.hasOptions&&t++,t},columnsCount(){return Object.keys(this.columns).length},dragOptions(){return{disabled:!this.sortable,fallbackClass:"k-table-row-fallback",ghostClass:"k-table-row-ghost"}},hasIndexColumn(){return this.sortable||!1!==this.index},hasOptions(){var t;return(null==(t=this.options)?void 0:t.length)>0||Object.values(this.values).filter((t=>t.options)).length>0}},watch:{rows(){this.values=this.rows}},methods:{isColumnEmpty(t){return 0===this.rows.filter((e=>!1===this.$helper.object.isEmpty(e[t]))).length},label(t,e){return t.label||this.$helper.string.ucfirst(e)},onChange(t){this.$emit("change",t)},onCell(t){this.$emit("cell",t)},onCellUpdate({columnIndex:t,rowIndex:e,value:s}){this.values[e][t]=s,this.$emit("input",this.values)},onHeader(t){this.$emit("header",t)},onOption(t,e,s){this.$emit("option",t,e,s)},onSort(){this.$emit("input",this.values),this.$emit("sort",this.values)},width(t){return"string"!=typeof t?"auto":!1===t.includes("/")?t:this.$helper.ratio(t,"auto",!1)}}},(function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-table"},[e("table",{attrs:{"data-disabled":t.disabled,"data-indexed":t.hasIndexColumn}},[e("thead",[e("tr",[t.hasIndexColumn?e("th",{staticClass:"k-table-index-column",attrs:{"data-mobile":""}},[t._v(" # ")]):t._e(),t._l(t.columns,(function(s,n){return e("th",{key:n+"-header",staticClass:"k-table-column",style:"width:"+t.width(s.width),attrs:{"data-align":s.align,"data-mobile":s.mobile},on:{click:function(e){return t.onHeader({column:s,columnIndex:n})}}},[t._t("header",(function(){return[t._v(" "+t._s(t.label(s,n))+" ")]}),null,{column:s,columnIndex:n,label:t.label(s,n)})],2)})),t.hasOptions?e("th",{staticClass:"k-table-options-column",attrs:{"data-mobile":""}}):t._e()],2)]),e("k-draggable",{attrs:{list:t.values,options:t.dragOptions,handle:!0,element:"tbody"},on:{change:t.onChange,end:t.onSort}},[0===t.rows.length?e("tr",[e("td",{staticClass:"k-table-empty",attrs:{colspan:t.columnsCount}},[t._v(" "+t._s(t.empty)+" ")])]):t._l(t.values,(function(s,n){return e("tr",{key:n},[t.hasIndexColumn?e("td",{staticClass:"k-table-index-column",attrs:{"data-sortable":t.sortable&&!1!==s.sortable,"data-mobile":""}},[t._t("index",(function(){return[e("div",{staticClass:"k-table-index",domProps:{textContent:t._s(t.index+n)}})]}),null,{row:s,rowIndex:n}),t.sortable&&!1!==s.sortable?e("k-sort-handle",{staticClass:"k-table-sort-handle"}):t._e()],2):t._e(),t._l(t.columns,(function(i,o){return e("k-table-cell",{key:n+"-"+o,staticClass:"k-table-column",style:"width:"+t.width(i.width),attrs:{column:i,field:t.fields[o],row:s,mobile:i.mobile,value:s[o]},on:{input:function(e){return t.onCellUpdate({columnIndex:o,rowIndex:n,value:e})}},nativeOn:{click:function(e){return t.onCell({row:s,rowIndex:n,column:i,columnIndex:o})}}})})),t.hasOptions?e("td",{staticClass:"k-table-options-column",attrs:{"data-mobile":""}},[t._t("options",(function(){return[e("k-options-dropdown",{attrs:{options:s.options||t.options,text:(s.options||t.options).length>1},on:{option:function(e){return t.onOption(e,s,n)}}})]}),null,{row:s,rowIndex:n,options:t.options})],2):t._e()],2)}))],2)],1),t.pagination?e("k-pagination",t._b({staticClass:"k-table-pagination",on:{paginate:function(e){return t.$emit("paginate",e)}}},"k-pagination",t.pagination,!1)):t._e()],1)}),[],!1,null,null,null,null).exports;const mn=G({inheritAttrs:!1,props:{column:Object,field:Object,mobile:{type:Boolean,default:!1},row:Object,value:{default:""}},computed:{component(){return this.$helper.isComponent(`k-${this.type}-field-preview`)?`k-${this.type}-field-preview`:this.$helper.isComponent(`k-table-${this.type}-cell`)?`k-table-${this.type}-cell`:Array.isArray(this.value)?"k-array-field-preview":"object"==typeof this.value?"k-object-field-preview":"k-text-field-preview"},type(){var t;return this.column.type||(null==(t=this.field)?void 0:t.type)}}},(function(){var t=this,e=t._self._c;return e("td",{attrs:{"data-align":t.column.align,"data-mobile":t.mobile}},[!1===t.$helper.object.isEmpty(t.value)?[e(t.component,{tag:"component",attrs:{column:t.column,field:t.field,row:t.row,value:t.value},on:{input:function(e){return t.$emit("input",e)}}})]:t._e()],2)}),[],!1,null,null,null,null).exports;const fn=G({props:{value:[String,Object]}},(function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-table-update-status-cell"},["string"==typeof t.value?e("span",{staticClass:"k-table-update-status-cell-version"},[t._v(" "+t._s(t.value)+" ")]):e("k-dropdown",{attrs:{"data-theme":t.value.theme}},[e("k-button",{staticClass:"k-table-update-status-cell-button",attrs:{icon:t.value.icon,href:t.value.url},on:{click:function(e){return e.stopPropagation(),t.$refs.dropdown.toggle()}}},[t._v(" "+t._s(t.value.currentVersion)+" ")]),e("k-dropdown-content",{ref:"dropdown",attrs:{align:"right"}},[e("dl",{staticClass:"k-plugin-info"},[e("div",[e("dt",[t._v(t._s(t.$t("plugin")))]),e("dd",[t._v(t._s(t.value.pluginName))])]),e("div",[e("dt",[t._v(t._s(t.$t("version.current")))]),e("dd",[t._v(t._s(t.value.currentVersion))])]),e("div",[e("dt",[t._v(t._s(t.$t("version.latest")))]),e("dd",[t._v(t._s(t.value.latestVersion))])]),e("div",[e("dt",[t._v(t._s(t.$t("system.updateStatus")))]),e("dd",{attrs:{"data-theme":t.value.theme}},[t._v(t._s(t.value.label))])])]),t.value.url?e("k-dropdown-item",{attrs:{icon:"open",link:t.value.url}},[t._v(" "+t._s(t.$t("versionInformation"))+" ")]):t._e()],1)],1)],1)}),[],!1,null,null,null,null).exports;const gn=G({props:{tab:String,tabs:Array,theme:String},data(){return{size:null,visibleTabs:this.tabs,invisibleTabs:[]}},computed:{current(){return(this.tabs.find((t=>t.name===this.tab))||this.tabs[0]||{}).name}},watch:{tabs:{handler(t){this.visibleTabs=t,this.invisibleTabs=[],this.resize(!0)},immediate:!0}},created(){window.addEventListener("resize",this.resize)},destroyed(){window.removeEventListener("resize",this.resize)},methods:{resize(t){if(this.tabs&&!(this.tabs.length<=1)){if(this.tabs.length<=3)return this.visibleTabs=this.tabs,void(this.invisibleTabs=[]);if(window.innerWidth>=700){if("large"===this.size&&!t)return;this.visibleTabs=this.tabs,this.invisibleTabs=[],this.size="large"}else{if("small"===this.size&&!t)return;this.visibleTabs=this.tabs.slice(0,2),this.invisibleTabs=this.tabs.slice(2),this.size="small"}}}}},(function(){var t=this,e=t._self._c;return t.tabs&&t.tabs.length>1?e("div",{staticClass:"k-tabs",attrs:{"data-theme":t.theme}},[e("nav",[t._l(t.visibleTabs,(function(s){return e("k-button",{key:s.name,staticClass:"k-tab-button",attrs:{link:s.link,current:t.current===s.name,icon:s.icon,tooltip:s.label}},[t._v(" "+t._s(s.label||s.text||s.name)+" "),s.badge?e("span",{staticClass:"k-tabs-badge"},[t._v(" "+t._s(s.badge)+" ")]):t._e()])})),t.invisibleTabs.length?e("k-button",{staticClass:"k-tab-button k-tabs-dropdown-button",attrs:{text:t.$t("more"),icon:"dots"},on:{click:function(e){return e.stopPropagation(),t.$refs.more.toggle()}}}):t._e()],2),t.invisibleTabs.length?e("k-dropdown-content",{ref:"more",staticClass:"k-tabs-dropdown",attrs:{align:"right"}},t._l(t.invisibleTabs,(function(s){return e("k-dropdown-item",{key:"more-"+s.name,attrs:{link:s.link,current:t.tab===s.name,icon:s.icon,tooltip:s.label}},[t._v(" "+t._s(s.label||s.text||s.name)+" ")])})),1):t._e()],1):t._e()}),[],!1,null,null,null,null).exports;const kn=G({props:{align:String}},(function(){var t=this;return(0,t._self._c)("div",{staticClass:"k-view",attrs:{"data-align":t.align}},[t._t("default")],2)}),[],!1,null,null,null,null).exports,bn={install(t){t.component("k-aspect-ratio",Ks),t.component("k-bar",Js),t.component("k-box",Gs),t.component("k-bubble",Ws),t.component("k-bubbles",Xs),t.component("k-collection",Zs),t.component("k-column",Qs),t.component("k-dropzone",tn),t.component("k-empty",en),t.component("k-file-preview",sn),t.component("k-grid",nn),t.component("k-header",on),t.component("k-inside",rn),t.component("k-item",ln),t.component("k-item-image",an),t.component("k-items",un),t.component("k-overlay",cn),t.component("k-panel",dn),t.component("k-stats",pn),t.component("k-table",hn),t.component("k-table-cell",mn),t.component("k-table-update-status-cell",fn),t.component("k-tabs",gn),t.component("k-view",kn)}},vn={};const yn=G({components:{draggable:()=>function(t,e,s){if(!e||0===e.length)return t();const n=document.getElementsByTagName("link");return Promise.all(e.map((t=>{if((t=function(t){return"/"+t}(t))in vn)return;vn[t]=!0;const e=t.endsWith(".css"),i=e?'[rel="stylesheet"]':"";if(s)for(let s=n.length-1;s>=0;s--){const i=n[s];if(i.href===t&&(!e||"stylesheet"===i.rel))return}else if(document.querySelector(`link[href="${t}"]${i}`))return;const o=document.createElement("link");return o.rel=e?"stylesheet":"modulepreload",e||(o.as="script",o.crossOrigin=""),o.href=t,document.head.appendChild(o),e?new Promise(((e,s)=>{o.addEventListener("load",e),o.addEventListener("error",(()=>s(new Error(`Unable to preload CSS for ${t}`))))})):void 0}))).then((()=>t()))}((()=>import("./vuedraggable.js")),[])},props:{data:Object,element:String,handle:[String,Boolean],list:[Array,Object],move:Function,options:Object},data(){return{listeners:{...this.$listeners,start:t=>{this.$store.dispatch("drag",{}),this.$listeners.start&&this.$listeners.start(t)},end:t=>{this.$store.dispatch("drag",null),this.$listeners.end&&this.$listeners.end(t)}}}},computed:{dragOptions(){let t=!1;return t=!0===this.handle?".k-sort-handle":this.handle,{fallbackClass:"k-sortable-fallback",fallbackOnBody:!0,forceFallback:!0,ghostClass:"k-sortable-ghost",handle:t,scroll:document.querySelector(".k-panel-view"),...this.options}}}},(function(){var t=this;return(0,t._self._c)("draggable",t._g(t._b({staticClass:"k-draggable",attrs:{"component-data":t.data,tag:t.element,list:t.list,move:t.move},scopedSlots:t._u([{key:"footer",fn:function(){return[t._t("footer")]},proxy:!0}],null,!0)},"draggable",t.dragOptions,!1),t.listeners),[t._t("default")],2)}),[],!1,null,null,null,null).exports;const $n=G({data:()=>({error:null}),errorCaptured(t){return this.$config.debug&&window.console.warn(t),this.error=t,!1},render(){return this.error?this.$slots.error?this.$slots.error[0]:this.$scopedSlots.error?this.$scopedSlots.error({error:this.error}):Vue.h("k-box",{attrs:{theme:"negative"}},this.error.message||this.error):this.$slots.default[0]}},null,null,!1,null,null,null,null).exports;const _n=G({props:{html:String},mounted(){try{let t=this.$refs.iframe.contentWindow.document;t.open(),t.write(this.html),t.close()}catch(t){console.error(t)}}},(function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-fatal"},[e("div",{staticClass:"k-fatal-box"},[e("k-bar",{scopedSlots:t._u([{key:"left",fn:function(){return[e("k-headline",[t._v(" The JSON response could not be parsed ")])]},proxy:!0},{key:"right",fn:function(){return[e("k-button",{attrs:{icon:"cancel",text:"Close"},on:{click:function(e){return t.$store.dispatch("fatal",!1)}}})]},proxy:!0}])}),e("iframe",{ref:"iframe",staticClass:"k-fatal-iframe"})],1)])}),[],!1,null,null,null,null).exports;const wn=G({props:{link:String,size:{type:String},tag:{type:String,default:"h2"},theme:{type:String}}},(function(){var t=this,e=t._self._c;return e(t.tag,t._g({tag:"component",staticClass:"k-headline",attrs:{"data-theme":t.theme,"data-size":t.size}},t.$listeners),[t.link?e("k-link",{attrs:{to:t.link}},[t._t("default")],2):t._t("default")],2)}),[],!1,null,null,null,null).exports;const xn=G({props:{alt:String,color:String,back:String,size:String,type:String},computed:{isEmoji(){return this.$helper.string.hasEmoji(this.type)}}},(function(){var t=this,e=t._self._c;return e("span",{class:"k-icon k-icon-"+t.type,style:{background:t.$helper.color(t.back)},attrs:{"aria-label":t.alt,role:t.alt?"img":null,"aria-hidden":!t.alt,"data-back":t.back,"data-size":t.size}},[t.isEmoji?e("span",{staticClass:"k-icon-emoji"},[t._v(t._s(t.type))]):e("svg",{style:{color:t.$helper.color(t.color)},attrs:{viewBox:"0 0 16 16"}},[e("use",{attrs:{"xlink:href":"#icon-"+t.type}})])])}),[],!1,null,null,null,null).exports;const Sn=G({icons:window.panel.plugins.icons},(function(){var t=this,e=t._self._c;return e("svg",{staticClass:"k-icons",attrs:{"aria-hidden":"true",xmlns:"http://www.w3.org/2000/svg",overflow:"hidden"}},[e("defs",t._l(t.$options.icons,(function(s,n){return e("symbol",{key:n,attrs:{id:"icon-"+n,viewBox:"0 0 16 16"},domProps:{innerHTML:t._s(s)}})})),0)])}),[],!1,null,null,null,null).exports;const Cn=G({props:{alt:String,back:String,cover:Boolean,ratio:String,sizes:String,src:String,srcset:String},data:()=>({loaded:{type:Boolean,default:!1},error:{type:Boolean,default:!1}}),computed:{ratioPadding(){return this.$helper.ratio(this.ratio||"1/1")}},created(){let t=new Image;t.onload=()=>{this.loaded=!0,this.$emit("load")},t.onerror=()=>{this.error=!0,this.$emit("error")},t.src=this.src}},(function(){var t=this,e=t._self._c;return e("span",t._g({staticClass:"k-image",attrs:{"data-ratio":t.ratio,"data-back":t.back,"data-cover":t.cover}},t.$listeners),[e("span",{style:"padding-bottom:"+t.ratioPadding},[t.loaded?e("img",{key:t.src,attrs:{alt:t.alt||"",src:t.src,srcset:t.srcset,sizes:t.sizes},on:{dragstart:function(t){t.preventDefault()}}}):t._e(),t.loaded||t.error?t._e():e("k-loader",{attrs:{position:"center",theme:"light"}}),!t.loaded&&t.error?e("k-icon",{staticClass:"k-image-error",attrs:{type:"cancel"}}):t._e()],1)])}),[],!1,null,null,null,null).exports;const On=G({},(function(){var t=this._self._c;return t("span",{staticClass:"k-loader"},[t("k-icon",{staticClass:"k-loader-icon",attrs:{type:"loader"}})],1)}),[],!1,null,null,null,null).exports;const An=G({data:()=>({offline:!1}),created(){this.$events.$on("offline",this.isOffline),this.$events.$on("online",this.isOnline)},destroyed(){this.$events.$off("offline",this.isOffline),this.$events.$off("online",this.isOnline)},methods:{isOnline(){this.offline=!1},isOffline(){this.offline=!0}}},(function(){var t=this,e=t._self._c;return t.offline?e("div",{staticClass:"k-offline-warning"},[e("p",[e("k-icon",{attrs:{type:"bolt"}}),t._v(" "+t._s(t.$t("error.offline")))],1)]):t._e()}),[],!1,null,null,null,null).exports,Tn=(t,e=!1)=>{if(t>=0&&t<=100)return!0;if(e)throw new Error("value has to be between 0 and 100");return!1};const In=G({props:{value:{type:Number,default:0,validator:Tn}},data(){return{state:this.value}},watch:{value(t){this.state=t}},methods:{set(t){Tn(t,!0),this.state=t}}},(function(){var t=this;return(0,t._self._c)("progress",{staticClass:"k-progress",attrs:{max:"100"},domProps:{value:t.state}},[t._v(t._s(t.state)+"%")])}),[],!1,null,null,null,null).exports;const jn=G({},(function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-registration"},[e("p",[t._v(t._s(t.$t("license.unregistered")))]),e("k-button",{staticClass:"k-topbar-button",attrs:{responsive:!0,tooltip:t.$t("license.unregistered"),icon:"key"},on:{click:function(e){return t.$dialog("registration")}}},[t._v(" "+t._s(t.$t("license.register"))+" ")]),e("k-button",{staticClass:"k-topbar-button",attrs:{responsive:!0,link:"https://getkirby.com/buy",target:"_blank",icon:"cart"}},[t._v(" "+t._s(t.$t("license.buy"))+" ")])],1)}),[],!1,null,null,null,null).exports;const Mn=G({props:{icon:{type:String,default:"sort"}}},(function(){return(0,this._self._c)("k-icon",{staticClass:"k-sort-handle",attrs:{type:this.icon,"aria-hidden":"true"}})}),[],!1,null,null,null,null).exports;const En=G({props:{click:{type:Function,default:()=>{}},disabled:Boolean,responsive:Boolean,status:String,text:String,tooltip:String},computed:{icon(){return"draft"===this.status?"circle-outline":"unlisted"===this.status?"circle-half":"circle"},theme(){return"draft"===this.status?"negative":"unlisted"===this.status?"info":"positive"},title(){let t=this.tooltip||this.text;return this.disabled&&(t+=` (${this.$t("disabled")})`),t}},methods:{onClick(){this.click(),this.$emit("click")}}},(function(){var t=this;return(0,t._self._c)("k-button",{class:"k-status-icon k-status-icon-"+t.status,attrs:{disabled:t.disabled,icon:t.icon,responsive:t.responsive,text:t.text,theme:t.theme,tooltip:t.title},on:{click:t.onClick}})}),[],!1,null,null,null,null).exports;const Ln=G({props:{align:String,html:String,size:String,theme:String},computed:{attrs(){return{class:"k-text","data-align":this.align,"data-size":this.size,"data-theme":this.theme}}}},(function(){var t=this,e=t._self._c;return t.html?e("div",t._b({domProps:{innerHTML:t._s(t.html)}},"div",t.attrs,!1)):e("div",t._b({},"div",t.attrs,!1),[t._t("default")],2)}),[],!1,null,null,null,null).exports;const Bn=G({props:{user:[Object,String]}},(function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-user-info"},[t.user.avatar?e("k-image",{attrs:{cover:!0,src:t.user.avatar.url,ratio:"1/1"}}):e("k-icon",{attrs:{type:"user"}}),t._v(" "+t._s(t.user.name||t.user.email||t.user)+" ")],1)}),[],!1,null,null,null,null).exports,Dn={install(t){t.component("k-draggable",yn),t.component("k-error-boundary",$n),t.component("k-fatal",_n),t.component("k-headline",wn),t.component("k-icon",xn),t.component("k-icons",Sn),t.component("k-image",Cn),t.component("k-loader",On),t.component("k-offline-warning",An),t.component("k-progress",In),t.component("k-registration",jn),t.component("k-status-icon",En),t.component("k-sort-handle",Mn),t.component("k-text",Ln),t.component("k-user-info",Bn)}};const Pn=G({props:{crumbs:{type:Array,default:()=>[]},label:{type:String,default:"Breadcrumb"},view:Object},computed:{dropdown(){return this.segments.map((t=>({...t,text:t.label,icon:"angle-right"})))},segments(){return[{link:this.view.link,label:this.view.breadcrumbLabel,icon:this.view.icon,loading:this.$store.state.isLoading},...this.crumbs]}},methods:{isLast(t){return this.crumbs.length-1===t}}},(function(){var t=this,e=t._self._c;return e("nav",{staticClass:"k-breadcrumb",attrs:{"aria-label":t.label}},[e("k-dropdown",{staticClass:"k-breadcrumb-dropdown"},[e("k-button",{attrs:{icon:"road-sign"},on:{click:function(e){return t.$refs.dropdown.toggle()}}}),e("k-dropdown-content",{ref:"dropdown",attrs:{options:t.dropdown,theme:"light"}})],1),e("ol",t._l(t.segments,(function(s,n){return e("li",{key:n},[e("k-link",{staticClass:"k-breadcrumb-link",attrs:{title:s.text||s.label,to:s.link,"aria-current":!!t.isLast(n)&&"page"}},[s.loading?e("k-loader",{staticClass:"k-breadcrumb-icon"}):s.icon?e("k-icon",{staticClass:"k-breadcrumb-icon",attrs:{type:s.icon}}):t._e(),e("span",{staticClass:"k-breadcrumb-link-text"},[t._v(" "+t._s(s.text||s.label)+" ")])],1)],1)})),0)],1)}),[],!1,null,null,null,null).exports;const Nn=G({inheritAttrs:!1,props:{autofocus:Boolean,click:Function,current:[String,Boolean],disabled:Boolean,icon:String,id:[String,Number],link:String,responsive:Boolean,rel:String,role:String,target:String,tabindex:String,text:[String,Number],theme:String,tooltip:String,type:{type:String,default:"button"}},computed:{component(){return!0===this.disabled?"k-button-disabled":this.link?"k-button-link":"k-button-native"}},methods:{focus(){this.$refs.button.focus&&this.$refs.button.focus()},tab(){this.$refs.button.tab&&this.$refs.button.tab()},untab(){this.$refs.button.untab&&this.$refs.button.untab()}}},(function(){var t=this;return(0,t._self._c)(t.component,t._g(t._b({ref:"button",tag:"component"},"component",t.$props,!1),t.$listeners),[t.text?[t._v(" "+t._s(t.text)+" ")]:t._t("default")],2)}),[],!1,null,null,null,null).exports;const Rn=G({inheritAttrs:!1,props:{icon:String,id:[String,Number],responsive:Boolean,theme:String,tooltip:String}},(function(){var t=this,e=t._self._c;return e("span",{staticClass:"k-button",attrs:{id:t.id,"data-disabled":!0,"data-responsive":t.responsive,"data-theme":t.theme,title:t.tooltip}},[t.icon?e("k-icon",{staticClass:"k-button-icon",attrs:{type:t.icon,alt:t.tooltip}}):t._e(),t.$slots.default?e("span",{staticClass:"k-button-text"},[t._t("default")],2):t._e()],1)}),[],!1,null,null,null,null).exports;const Fn=G({props:{buttons:Array}},(function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-button-group"},[t.$slots.default?t._t("default"):t._l(t.buttons,(function(s,n){return e("k-button",t._b({key:n},"k-button",s,!1))}))],2)}),[],!1,null,null,null,null).exports;const qn=G({inheritAttrs:!1,props:{autofocus:Boolean,current:[String,Boolean],icon:String,id:[String,Number],link:String,rel:String,responsive:Boolean,role:String,target:String,tabindex:String,theme:String,tooltip:String},methods:{focus(){this.$el.focus()}}},(function(){var t=this,e=t._self._c;return e("k-link",t._g({staticClass:"k-button",attrs:{id:t.id,"aria-current":t.current,autofocus:t.autofocus,"data-theme":t.theme,"data-responsive":t.responsive,rel:t.rel,role:t.role,tabindex:t.tabindex,target:t.target,title:t.tooltip,to:t.link}},t.$listeners),[t.icon?e("k-icon",{staticClass:"k-button-icon",attrs:{type:t.icon,alt:t.tooltip}}):t._e(),t.$slots.default?e("span",{staticClass:"k-button-text"},[t._t("default")],2):t._e()],1)}),[],!1,null,null,null,null).exports,Yn={mounted(){this.$el.addEventListener("keyup",this.onTab,!0),this.$el.addEventListener("blur",this.onUntab,!0)},destroyed(){this.$el.removeEventListener("keyup",this.onTab,!0),this.$el.removeEventListener("blur",this.onUntab,!0)},methods:{focus(){this.$el.focus&&this.$el.focus()},onTab(t){9===t.keyCode&&this.$el.setAttribute("data-tabbed",!0)},onUntab(){this.$el.removeAttribute("data-tabbed")},tab(){this.$el.focus(),this.$el.setAttribute("data-tabbed",!0)},untab(){this.$el.removeAttribute("data-tabbed")}}};const zn=G({mixins:[Yn],inheritAttrs:!1,props:{autofocus:Boolean,click:{type:Function,default:()=>{}},current:[String,Boolean],icon:String,id:[String,Number],responsive:Boolean,role:String,tabindex:String,theme:String,tooltip:String,type:{type:String,default:"button"}}},(function(){var t=this,e=t._self._c;return e("button",t._g({staticClass:"k-button",attrs:{id:t.id,"aria-current":t.current,autofocus:t.autofocus,"data-theme":t.theme,"data-responsive":t.responsive,role:t.role,tabindex:t.tabindex,title:t.tooltip,type:t.type},on:{click:t.click}},t.$listeners),[t.icon?e("k-icon",{staticClass:"k-button-icon",attrs:{type:t.icon,alt:t.tooltip}}):t._e(),t.$slots.default?e("span",{staticClass:"k-button-text"},[t._t("default")],2):t._e()],1)}),[],!1,null,null,null,null).exports;const Hn=G({},(function(){return(0,this._self._c)("span",{staticClass:"k-dropdown",on:{click:function(t){t.stopPropagation()}}},[this._t("default")],2)}),[],!1,null,null,null,null).exports;let Un=null;const Vn=G({props:{align:{type:String,default:"left"},options:[Array,Function,String],theme:{type:String,default:"dark"}},data:()=>({current:-1,dropup:!1,isOpen:!1,items:[]}),methods:{async fetchOptions(t){if(!this.options)return t(this.items);"string"==typeof this.options?this.$dropdown(this.options)(t):"function"==typeof this.options?this.options(t):Array.isArray(this.options)&&t(this.options)},onOptionClick(t){"function"==typeof t.click?t.click.call(this):t.click&&this.$emit("action",t.click)},open(){this.reset(),Un&&Un!==this&&Un.close(),this.fetchOptions((t=>{this.$events.$on("keydown",this.navigate),this.$events.$on("click",this.close),this.items=t,this.isOpen=!0,Un=this,this.onOpen(),this.$emit("open")}))},reset(){this.current=-1,this.$events.$off("keydown",this.navigate),this.$events.$off("click",this.close)},close(){this.reset(),this.isOpen=Un=!1,this.$emit("close")},toggle(){this.isOpen?this.close():this.open()},focus(t=0){var e;(null==(e=this.$children[t])?void 0:e.focus)&&(this.current=t,this.$children[t].focus())},onOpen(){this.dropup=!1,this.$nextTick((()=>{if(this.$el){let t=window.innerHeight||document.body.clientHeight||document.documentElement.clientHeight,e=50,s=this.$el.getBoundingClientRect().top||0,n=this.$el.clientHeight;s+n>t-e&&n+2*ethis.$children.length-1){const t=this.$children.filter((t=>!1===t.disabled));this.current=this.$children.indexOf(t[t.length-1]);break}if(this.$children[this.current]&&!1===this.$children[this.current].disabled){this.focus(this.current);break}}break;case"Tab":for(;;){if(this.current++,this.current>this.$children.length-1){this.close(),this.$emit("leave",t.code);break}if(this.$children[this.current]&&!1===this.$children[this.current].disabled)break}}}}},(function(){var t=this,e=t._self._c;return t.isOpen?e("div",{staticClass:"k-dropdown-content",attrs:{"data-align":t.align,"data-dropup":t.dropup,"data-theme":t.theme}},[t._t("default",(function(){return[t._l(t.items,(function(s,n){return["-"===s?e("hr",{key:t._uid+"-item-"+n}):e("k-dropdown-item",t._b({key:t._uid+"-item-"+n,ref:t._uid+"-item-"+n,refInFor:!0,on:{click:function(e){return t.onOptionClick(s)}}},"k-dropdown-item",s,!1),[t._v(" "+t._s(s.text)+" ")])]}))]}))],2):t._e()}),[],!1,null,null,null,null).exports;const Kn=G({inheritAttrs:!1,props:{disabled:Boolean,icon:String,image:[String,Object],link:String,target:String,theme:String,upload:String,current:[String,Boolean]},data(){return{listeners:{...this.$listeners,click:t=>{this.$parent.close(),this.$emit("click",t)}}}},methods:{focus(){this.$refs.button.focus()},tab(){this.$refs.button.tab()}}},(function(){var t=this;return(0,t._self._c)("k-button",t._g(t._b({ref:"button",staticClass:"k-dropdown-item"},"k-button",t.$props,!1),t.listeners),[t._t("default")],2)}),[],!1,null,null,null,null).exports;const Jn=G({mixins:[Yn],props:{disabled:Boolean,rel:String,tabindex:[String,Number],target:String,title:String,to:[String,Function]},data(){return{relAttr:"_blank"===this.target?"noreferrer noopener":this.rel,listeners:{...this.$listeners,click:this.onClick}}},computed:{href(){return"function"==typeof this.to?"":"/"!==this.to[0]||this.target?!0===this.to.includes("@")&&!1===this.to.includes("/")&&!1===this.to.startsWith("mailto:")?"mailto:"+this.to:this.to:this.$url(this.to)}},methods:{isRoutable(t){if(t.metaKey||t.altKey||t.ctrlKey||t.shiftKey)return!1;if(t.defaultPrevented)return!1;if(void 0!==t.button&&0!==t.button)return!1;if(this.target)return!1;if("string"==typeof this.href){if(this.href.includes("://")||this.href.startsWith("//"))return!1;if(this.href.includes("mailto:"))return!1}return!0},onClick(t){if(!0===this.disabled)return t.preventDefault(),!1;"function"==typeof this.to&&(t.preventDefault(),this.to()),this.isRoutable(t)&&(t.preventDefault(),this.$go(this.to)),this.$emit("click",t)}}},(function(){var t=this,e=t._self._c;return t.to&&!t.disabled?e("a",t._g({ref:"link",staticClass:"k-link",attrs:{href:t.href,rel:t.relAttr,tabindex:t.tabindex,target:t.target,title:t.title}},t.listeners),[t._t("default")],2):e("span",{staticClass:"k-link",attrs:{title:t.title,"data-disabled":""}},[t._t("default")],2)}),[],!1,null,null,null,null).exports;const Gn=G({computed:{defaultLanguage(){return this.$languages.find((t=>!0===t.default))},language(){return this.$language},languages(){return this.$languages.filter((t=>!1===t.default))}},methods:{change(t){this.$emit("change",t),this.$go(window.location,{query:{language:t.code}})}}},(function(){var t=this,e=t._self._c;return t.languages.length?e("k-dropdown",{staticClass:"k-languages-dropdown"},[e("k-button",{attrs:{text:t.language.name,responsive:!0,icon:"globe"},on:{click:function(e){return t.$refs.languages.toggle()}}}),t.languages?e("k-dropdown-content",{ref:"languages"},[e("k-dropdown-item",{on:{click:function(e){return t.change(t.defaultLanguage)}}},[t._v(" "+t._s(t.defaultLanguage.name)+" ")]),e("hr"),t._l(t.languages,(function(s){return e("k-dropdown-item",{key:s.code,on:{click:function(e){return t.change(s)}}},[t._v(" "+t._s(s.name)+" ")])}))],2):t._e()],1):t._e()}),[],!1,null,null,null,null).exports;const Wn=G({props:{align:{type:String,default:"right"},icon:{type:String,default:"dots"},options:{type:[Array,Function,String],default:()=>[]},text:{type:[Boolean,String],default:!0},theme:{type:String,default:"dark"}},computed:{hasSingleOption(){return Array.isArray(this.options)&&1===this.options.length}},methods:{onAction(t,e,s){"function"==typeof t?t.call(this):(this.$emit("action",t,e,s),this.$emit("option",t,e,s))},toggle(){this.$refs.options.toggle()}}},(function(){var t=this,e=t._self._c;return t.hasSingleOption?e("k-button",{staticClass:"k-options-dropdown-toggle",attrs:{icon:t.options[0].icon||t.icon,tooltip:t.options[0].tooltip||t.options[0].text},on:{click:function(e){return t.onAction(t.options[0].option||t.options[0].click,t.options[0],0)}}},[!0===t.text?[t._v(" "+t._s(t.options[0].text)+" ")]:!1!==t.text?[t._v(" "+t._s(t.text)+" ")]:t._e()],2):t.options.length?e("k-dropdown",{staticClass:"k-options-dropdown"},[e("k-button",{staticClass:"k-options-dropdown-toggle",attrs:{icon:t.icon,tooltip:t.$t("options")},on:{click:function(e){return t.$refs.options.toggle()}}},[t.text&&!0!==t.text?[t._v(" "+t._s(t.text)+" ")]:t._e()],2),e("k-dropdown-content",{ref:"options",staticClass:"k-options-dropdown-content",attrs:{align:t.align,options:t.options},on:{action:t.onAction}})],1):t._e()}),[],!1,null,null,null,null).exports;const Xn=G({props:{align:{type:String,default:"left"},details:{type:Boolean,default:!1},dropdown:{type:Boolean,default:!0},keys:{type:Boolean,default:!1},limit:{type:Number,default:10},page:{type:Number,default:1},pageLabel:{type:String,default:()=>window.panel.$t("pagination.page")},total:{type:Number,default:0},prevLabel:{type:String,default:()=>window.panel.$t("prev")},nextLabel:{type:String,default:()=>window.panel.$t("next")},validate:{type:Function,default:()=>Promise.resolve()}},data(){return{currentPage:this.page}},computed:{show(){return this.pages>1},start(){return(this.currentPage-1)*this.limit+1},end(){let t=this.start-1+this.limit;return t>this.total?this.total:t},detailsText(){return 1===this.limit?this.start+" / ":this.start+"-"+this.end+" / "},pages(){return Math.ceil(this.total/this.limit)},hasPrev(){return this.start>1},hasNext(){return this.endthis.limit},offset(){return this.start-1}},watch:{page(t){this.currentPage=parseInt(t)}},created(){!0===this.keys&&window.addEventListener("keydown",this.navigate,!1)},destroyed(){window.removeEventListener("keydown",this.navigate,!1)},methods:{async goTo(t){try{await this.validate(t),t<1&&(t=1),t>this.pages&&(t=this.pages),this.currentPage=t,this.$refs.dropdown&&this.$refs.dropdown.close(),this.$emit("paginate",{page:this.currentPage,start:this.start,end:this.end,limit:this.limit,offset:this.offset})}catch(e){}},prev(){this.goTo(this.currentPage-1)},next(){this.goTo(this.currentPage+1)},navigate(t){switch(t.code){case"ArrowLeft":this.prev();break;case"ArrowRight":this.next()}}}},(function(){var t=this,e=t._self._c;return t.show?e("nav",{staticClass:"k-pagination",attrs:{"data-align":t.align}},[t.show?e("k-button",{attrs:{disabled:!t.hasPrev,tooltip:t.prevLabel,icon:"angle-left"},on:{click:t.prev}}):t._e(),t.details?[t.dropdown?[e("k-dropdown",[e("k-button",{staticClass:"k-pagination-details",attrs:{disabled:!t.hasPages},on:{click:function(e){return t.$refs.dropdown.toggle()}}},[t.total>1?[t._v(" "+t._s(t.detailsText)+" ")]:t._e(),t._v(" "+t._s(t.total)+" ")],2),e("k-dropdown-content",{ref:"dropdown",staticClass:"k-pagination-selector",on:{open:function(e){t.$nextTick((()=>t.$refs.page.focus()))}}},[e("div",{staticClass:"k-pagination-settings"},[e("label",{attrs:{for:"k-pagination-page"}},[e("span",[t._v(t._s(t.pageLabel)+":")]),e("select",{ref:"page",attrs:{id:"k-pagination-page"}},t._l(t.pages,(function(s){return e("option",{key:s,domProps:{selected:t.page===s,value:s}},[t._v(" "+t._s(s)+" ")])})),0)]),e("k-button",{attrs:{icon:"check"},on:{click:function(e){return t.goTo(t.$refs.page.value)}}})],1)])],1)]:[e("span",{staticClass:"k-pagination-details"},[t.total>1?[t._v(" "+t._s(t.detailsText)+" ")]:t._e(),t._v(" "+t._s(t.total)+" ")],2)]]:t._e(),t.show?e("k-button",{attrs:{disabled:!t.hasNext,tooltip:t.nextLabel,icon:"angle-right"},on:{click:t.next}}):t._e()],2):t._e()}),[],!1,null,null,null,null).exports;const Zn=G({props:{prev:{type:[Boolean,Object],default:!1},next:{type:[Boolean,Object],default:!1}},computed:{buttons(){return[{...this.button(this.prev),icon:"angle-left"},{...this.button(this.next),icon:"angle-right"}]}},methods:{button:t=>t||{disabled:!0,link:"#"}}},(function(){return(0,this._self._c)("k-button-group",{staticClass:"k-prev-next",attrs:{buttons:this.buttons}})}),[],!1,null,null,null,null).exports;const Qn=G({props:{types:{type:Object,default:()=>({})},type:String},data(){return{isLoading:!1,hasResults:!0,items:[],currentType:this.getType(this.type),q:null,selected:-1}},watch:{q(t,e){t!==e&&this.search(this.q)},currentType(t,e){t!==e&&this.search(this.q)},type(){this.currentType=this.getType(this.type)}},created(){this.search=tt(this.search,250),this.$events.$on("keydown.cmd.shift.f",this.open),this.$events.$on("keydown.cmd./",this.open)},destroyed(){this.$events.$off("keydown.cmd.shift.f",this.open),this.$events.$off("keydown.cmd./",this.open)},methods:{changeType(t){this.currentType=this.getType(t),this.$nextTick((()=>{this.$refs.input.focus()}))},close(){this.$refs.overlay.close(),this.hasResults=!0,this.items=[],this.q=null},getType(t){return this.types[t]||this.types[Object.keys(this.types)[0]]},navigate(t){this.$go(t.link),this.close()},onDown(){this.selected=0&&this.select(this.selected-1)},open(t){null==t||t.preventDefault(),this.$refs.overlay.open()},async search(t){this.isLoading=!0,this.$refs.types&&this.$refs.types.close();try{if(null===t||""===t)throw Error("Empty query");const e=await this.$search(this.currentType.id,t);if(!1===e)throw Error("JSON parsing failed");this.items=e.results}catch(e){this.items=[]}finally{this.select(-1),this.isLoading=!1,this.hasResults=this.items.length>0}},select(t){if(this.selected=t,this.$refs.items){const e=this.$refs.items.$el.querySelectorAll(".k-item");[...e].forEach((t=>delete t.dataset.selected)),t>=0&&(e[t].dataset.selected=!0)}}}},(function(){var t=this,e=t._self._c;return e("k-overlay",{ref:"overlay"},[e("div",{staticClass:"k-search",attrs:{role:"search"}},[e("div",{staticClass:"k-search-input"},[e("k-dropdown",{staticClass:"k-search-types"},[e("k-button",{attrs:{icon:t.currentType.icon,text:t.currentType.label},on:{click:function(e){return t.$refs.types.toggle()}}}),e("k-dropdown-content",{ref:"types"},t._l(t.types,(function(s,n){return e("k-dropdown-item",{key:n,attrs:{icon:s.icon},on:{click:function(e){return t.changeType(n)}}},[t._v(" "+t._s(s.label)+" ")])})),1)],1),e("input",{ref:"input",attrs:{placeholder:t.$t("search")+" …","aria-label":t.$t("search"),autofocus:!0,type:"text"},domProps:{value:t.q},on:{input:function(e){return t.onInput(e.target.value)},keydown:[function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"down",40,e.key,["Down","ArrowDown"])?null:(e.preventDefault(),t.onDown.apply(null,arguments))},function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"up",38,e.key,["Up","ArrowUp"])?null:(e.preventDefault(),t.onUp.apply(null,arguments))},function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"tab",9,e.key,"Tab")?null:(e.preventDefault(),t.onTab.apply(null,arguments))},function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"enter",13,e.key,"Enter")?null:t.onEnter.apply(null,arguments)},function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"esc",27,e.key,["Esc","Escape"])?null:t.close.apply(null,arguments)}]}}),e("k-button",{staticClass:"k-search-close",attrs:{icon:t.isLoading?"loader":"cancel",tooltip:t.$t("close")},on:{click:t.close}})],1),!t.q||t.hasResults&&!t.items.length?t._e():e("div",{staticClass:"k-search-results"},[t.items.length?e("k-collection",{ref:"items",attrs:{items:t.items},on:{hover:t.onHover},nativeOn:{mouseout:function(e){return t.select(-1)}}}):t.hasResults?t._e():e("p",{staticClass:"k-search-empty"},[t._v(" "+t._s(t.$t("search.results.none"))+" ")])],1)])])}),[],!1,null,null,null,null).exports;const ti=G({props:{removable:Boolean},methods:{remove(){this.removable&&this.$emit("remove")},focus(){this.$refs.button.focus()}}},(function(){var t=this,e=t._self._c;return e("span",{ref:"button",staticClass:"k-tag",attrs:{tabindex:"0"},on:{keydown:function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"delete",[8,46],e.key,["Backspace","Delete","Del"])?null:(e.preventDefault(),t.remove.apply(null,arguments))}}},[e("span",{staticClass:"k-tag-text"},[t._t("default")],2),t.removable?e("k-icon",{staticClass:"k-tag-toggle",attrs:{type:"cancel-small"},nativeOn:{click:function(e){return t.remove.apply(null,arguments)}}}):t._e()],1)}),[],!1,null,null,null,null).exports;const ei=G({props:{breadcrumb:Array,license:Boolean,menu:Array,title:String,view:Object},computed:{notification(){return this.$store.state.notification.type&&"error"!==this.$store.state.notification.type?this.$store.state.notification:null}}},(function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-topbar"},[e("k-view",[e("div",{staticClass:"k-topbar-wrapper"},[e("k-dropdown",{staticClass:"k-topbar-menu"},[e("k-button",{staticClass:"k-topbar-button k-topbar-menu-button",attrs:{tooltip:t.$t("menu"),icon:"bars"},on:{click:function(e){return t.$refs.menu.toggle()}}},[e("k-icon",{attrs:{type:"angle-down"}})],1),e("k-dropdown-content",{ref:"menu",staticClass:"k-topbar-menu",attrs:{options:t.menu,theme:"light"}})],1),e("k-breadcrumb",{staticClass:"k-topbar-breadcrumb",attrs:{crumbs:t.breadcrumb,view:t.view}}),e("div",{staticClass:"k-topbar-signals"},[t.notification?e("k-button",{staticClass:"k-topbar-notification k-topbar-button",attrs:{text:t.notification.message,theme:"positive"},on:{click:function(e){return t.$store.dispatch("notification/close")}}}):t.license?t._e():e("k-registration"),e("k-form-indicator"),e("k-button",{staticClass:"k-topbar-button",attrs:{tooltip:t.$t("search"),icon:"search"},on:{click:function(e){return t.$refs.search.open()}}})],1)],1)]),e("k-search",{ref:"search",attrs:{type:t.$view.search||"pages",types:t.$searches}})],1)}),[],!1,null,null,null,null).exports,si={install(t){t.component("k-breadcrumb",Pn),t.component("k-button",Nn),t.component("k-button-disabled",Rn),t.component("k-button-group",Fn),t.component("k-button-link",qn),t.component("k-button-native",zn),t.component("k-dropdown",Hn),t.component("k-dropdown-content",Vn),t.component("k-dropdown-item",Kn),t.component("k-languages-dropdown",Gn),t.component("k-link",Jn),t.component("k-options-dropdown",Wn),t.component("k-pagination",Xn),t.component("k-prev-next",Zn),t.component("k-search",Qn),t.component("k-tag",ti),t.component("k-topbar",ei)}};const ni=G({props:{empty:String,blueprint:String,lock:[Boolean,Object],parent:String,tab:Object},computed:{content(){return this.$store.getters["content/values"]()}},methods:{exists(t){return this.$helper.isComponent(`k-${t}-section`)}}},(function(){var t=this,e=t._self._c;return 0===t.tab.columns.length?e("k-box",{attrs:{html:!0,text:t.empty,theme:"info"}}):e("k-grid",{staticClass:"k-sections",attrs:{gutter:"large"}},t._l(t.tab.columns,(function(s,n){return e("k-column",{key:t.parent+"-column-"+n,attrs:{width:s.width,sticky:s.sticky}},[t._l(s.sections,(function(i,o){return[t.$helper.field.isVisible(i,t.content)?[t.exists(i.type)?e("k-"+i.type+"-section",t._b({key:t.parent+"-column-"+n+"-section-"+o+"-"+t.blueprint,tag:"component",class:"k-section k-section-name-"+i.name,attrs:{column:s.width,lock:t.lock,name:i.name,parent:t.parent,timestamp:t.$view.timestamp},on:{submit:function(e){return t.$emit("submit",e)}}},"component",i,!1)):[e("k-box",{key:t.parent+"-column-"+n+"-section-"+o,attrs:{text:t.$t("error.section.type.invalid",{type:i.type}),theme:"negative"}})]]:t._e()]}))],2)})),1)}),[],!1,null,null,null,null).exports,ii={props:{blueprint:String,lock:[Boolean,Object],help:String,name:String,parent:String,timestamp:Number},methods:{load(){return this.$api.get(this.parent+"/sections/"+this.name)}}};const oi=G({mixins:[ii],inheritAttrs:!1,data:()=>({fields:{},isLoading:!0,issue:null}),computed:{values(){return this.$store.getters["content/values"]()}},watch:{timestamp(){this.fetch()}},created(){this.input=tt(this.input,50),this.fetch()},methods:{input(t,e,s){this.$store.dispatch("content/update",[s,t[s]])},async fetch(){try{const t=await this.load();this.fields=t.fields,Object.keys(this.fields).forEach((t=>{this.fields[t].section=this.name,this.fields[t].endpoints={field:this.parent+"/fields/"+t,section:this.parent+"/sections/"+this.name,model:this.parent}}))}catch(t){this.issue=t}finally{this.isLoading=!1}},onSubmit(t){this.$store.dispatch("content/update",[null,t]),this.$events.$emit("keydown.cmd.s",t)}}},(function(){var t=this,e=t._self._c;return t.isLoading?t._e():e("section",{staticClass:"k-fields-section"},[t.issue?[e("k-headline",{staticClass:"k-fields-issue-headline"},[t._v(" Error ")]),e("k-box",{attrs:{text:t.issue.message,html:!1,theme:"negative"}})]:t._e(),e("k-form",{attrs:{fields:t.fields,validate:!0,value:t.values,disabled:t.lock&&"lock"===t.lock.state},on:{input:t.input,submit:t.onSubmit}})],2)}),[],!1,null,null,null,null).exports;const ri=G({inheritAttrs:!1,props:{blueprint:String,column:String,parent:String,name:String,timestamp:Number},data:()=>({data:[],error:null,isLoading:!1,isProcessing:!1,options:{columns:{},empty:null,headline:null,help:null,layout:"list",link:null,max:null,min:null,size:null,sortable:null},pagination:{page:null},searchterm:null,searching:!1}),computed:{addIcon:()=>"add",buttons(){let t=[];return this.canSearch&&t.push({icon:"filter",text:this.$t("search"),click:this.onSearchToggle,responsive:!0}),this.canAdd&&t.push({icon:this.addIcon,text:this.$t("add"),click:this.onAdd}),t},canAdd:()=>!0,canDrop:()=>!1,canSearch(){return this.options.search},collection(){return{columns:this.options.columns,empty:this.emptyPropsWithSearch,layout:this.options.layout,help:this.options.help,items:this.items,pagination:this.pagination,sortable:!this.isProcessing&&this.options.sortable,size:this.options.size}},emptyProps(){return{icon:"page",text:this.$t("pages.empty")}},emptyPropsWithSearch(){return{...this.emptyProps,text:this.searching?this.$t("search.results.none"):this.options.empty||this.emptyProps.text}},items(){return this.data},isInvalid(){var t;return!((null==(t=this.searchterm)?void 0:t.length)>0)&&(!!(this.options.min&&this.data.lengththis.options.max))},paginationId(){return"kirby$pagination$"+this.parent+"/"+this.name},type:()=>"models"},watch:{searchterm:tt((function(){this.pagination.page=0,this.reload()}),200),timestamp(){this.reload()}},created(){this.load()},methods:{async load(t){t||(this.isLoading=!0),this.isProcessing=!0,null===this.pagination.page&&(this.pagination.page=localStorage.getItem(this.paginationId)||1);try{const t=await this.$api.get(this.parent+"/sections/"+this.name,{page:this.pagination.page,searchterm:this.searchterm});this.options=t.options,this.pagination=t.pagination,this.data=t.data}catch(e){this.error=e.message}finally{this.isProcessing=!1,this.isLoading=!1}},onAction(){},onAdd(){},onChange(){},onDrop(){},onSort(){},onPaginate(t){localStorage.setItem(this.paginationId,t.page),this.pagination=t,this.reload()},onSearchToggle(){this.searching=!this.searching,this.searchterm=null},onUpload(){},async reload(){await this.load(!0)},update(){this.reload(),this.$events.$emit("model.update")}}},(function(){var t=this,e=t._self._c;return!1===t.isLoading?e("section",{class:`k-models-section k-${t.type}-section`,attrs:{"data-processing":t.isProcessing}},[e("header",{staticClass:"k-section-header"},[e("k-headline",{attrs:{link:t.options.link}},[t._v(" "+t._s(t.options.headline||" ")+" "),t.options.min?e("abbr",{attrs:{title:t.$t("section.required")}},[t._v("*")]):t._e()]),e("k-button-group",{attrs:{buttons:t.buttons}})],1),t.error?e("k-box",{attrs:{theme:"negative"}},[e("k-text",{attrs:{size:"small"}},[e("strong",[t._v(" "+t._s(t.$t("error.section.notLoaded",{name:t.name}))+": ")]),t._v(" "+t._s(t.error)+" ")])],1):[e("k-dropzone",{attrs:{disabled:!t.canDrop},on:{drop:t.onDrop}},[t.searching&&t.options.search?e("k-input",{staticClass:"k-models-section-search",attrs:{autofocus:!0,placeholder:t.$t("search")+" …",value:t.searchterm,type:"text"},on:{input:function(e){t.searchterm=e},keydown:function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"esc",27,e.key,["Esc","Escape"])?null:t.onSearchToggle.apply(null,arguments)}}}):t._e(),e("k-collection",t._g(t._b({attrs:{"data-invalid":t.isInvalid},on:{action:t.onAction,change:t.onChange,sort:t.onSort,paginate:t.onPaginate}},"k-collection",t.collection,!1),t.canAdd?{empty:t.onAdd}:{}))],1),e("k-upload",{ref:"upload",on:{success:t.onUpload,error:t.reload}})]],2):t._e()}),[],!1,null,null,null,null).exports;const li=G({extends:ri,computed:{addIcon:()=>"upload",canAdd(){return this.$permissions.files.create&&!1!==this.options.upload},canDrop(){return!1!==this.canAdd},emptyProps(){return{icon:"image",text:this.$t("files.empty")}},items(){return this.data.map((t=>(t.sortable=this.options.sortable,t.column=this.column,t.options=this.$dropdown(t.link,{query:{view:"list",update:this.options.sortable,delete:this.data.length>this.options.min}}),t.data={"data-id":t.id,"data-template":t.template},t)))},type:()=>"files",uploadProps(){return{...this.options.upload,url:this.$urls.api+"/"+this.options.upload.api}}},created(){this.$events.$on("model.update",this.reload),this.$events.$on("file.sort",this.reload)},destroyed(){this.$events.$off("model.update",this.reload),this.$events.$off("file.sort",this.reload)},methods:{onAction(t,e){"replace"===t&&this.replace(e)},onAdd(){this.canAdd&&this.$refs.upload.open(this.uploadProps)},onDrop(t){this.canAdd&&this.$refs.upload.drop(t,this.uploadProps)},async onSort(t){if(!1===this.options.sortable)return!1;this.isProcessing=!0;try{await this.$api.patch(this.options.apiUrl+"/files/sort",{files:t.map((t=>t.id)),index:this.pagination.offset}),this.$store.dispatch("notification/success",":)"),this.$events.$emit("file.sort")}catch(e){this.reload(),this.$store.dispatch("notification/error",e.message)}finally{this.isProcessing=!1}},onUpload(){this.$events.$emit("file.create"),this.$events.$emit("model.update"),this.$store.dispatch("notification/success",":)")},replace(t){this.$refs.upload.open({url:this.$urls.api+"/"+t.link,accept:"."+t.extension+","+t.mime,multiple:!1})}}},null,null,!1,null,null,null,null).exports;const ai=G({mixins:[ii],data:()=>({label:null,text:null,theme:null}),async created(){const t=await this.load();this.label=t.label,this.text=t.text,this.theme=t.theme||"info"}},(function(){var t=this,e=t._self._c;return e("section",{staticClass:"k-info-section"},[e("k-headline",{staticClass:"k-info-section-label"},[t._v(" "+t._s(t.label)+" ")]),e("k-box",{attrs:{theme:t.theme}},[e("k-text",{attrs:{html:t.text}})],1)],1)}),[],!1,null,null,null,null).exports;const ui=G({extends:ri,computed:{canAdd(){return this.options.add&&this.$permissions.pages.create},items(){return this.data.map((t=>{const e=!1!==t.permissions.changeStatus;return t.flag={status:t.status,tooltip:this.$t("page.status")+": "+this.$t("page.status."+t.status),disabled:!e,click:()=>this.$dialog(t.link+"/changeStatus")},t.sortable=t.permissions.sort&&this.options.sortable,t.deletable=this.data.length>this.options.min,t.column=this.column,t.options=this.$dropdown(t.link,{query:{view:"list",delete:t.deletable,sort:t.sortable}}),t.data={"data-id":t.id,"data-status":t.status,"data-template":t.template},t}))}},created(){this.$events.$on("page.changeStatus",this.reload),this.$events.$on("page.sort",this.reload)},destroyed(){this.$events.$off("page.changeStatus",this.reload),this.$events.$off("page.sort",this.reload)},methods:{onAdd(){this.canAdd&&this.$dialog("pages/create",{query:{parent:this.options.link||this.parent,view:this.parent,section:this.name}})},async onChange(t){let e=null;if(t.added&&(e="added"),t.moved&&(e="moved"),e){this.isProcessing=!0;const n=t[e].element,i=t[e].newIndex+1+this.pagination.offset;try{await this.$api.pages.changeStatus(n.id,"listed",i),this.$store.dispatch("notification/success",":)"),this.$events.$emit("page.sort",n)}catch(s){this.$store.dispatch("notification/error",{message:s.message,details:s.details}),await this.reload()}finally{this.isProcessing=!1}}}}},null,null,!1,null,null,null,null).exports;const ci=G({mixins:[ii],data:()=>({isLoading:!0,headline:null,reports:null,size:null}),async created(){const t=await this.load();this.isLoading=!1,this.headline=t.headline,this.reports=t.reports,this.size=t.size},methods:{}},(function(){var t=this,e=t._self._c;return!1===t.isLoading?e("section",{staticClass:"k-stats-section"},[e("header",{staticClass:"k-section-header"},[e("k-headline",[t._v(" "+t._s(t.headline)+" ")])],1),t.reports.length>0?e("k-stats",{attrs:{reports:t.reports,size:t.size}}):e("k-empty",{attrs:{icon:"chart"}},[t._v(" "+t._s(t.empty||t.$t("stats.empty")))])],1):t._e()}),[],!1,null,null,null,null).exports,di={install(t){t.component("k-sections",ni),t.component("k-fields-section",oi),t.component("k-files-section",li),t.component("k-info-section",ai),t.component("k-pages-section",ui),t.component("k-stats-section",ci)}};const pi=G({props:{blueprint:String,next:Object,prev:Object,permissions:{type:Object,default:()=>({})},lock:{type:[Boolean,Object]},model:{type:Object,default:()=>({})},tab:{type:Object,default:()=>({columns:[]})},tabs:{type:Array,default:()=>[]}},computed:{id(){return this.model.link},isLocked(){var t;return"lock"===(null==(t=this.lock)?void 0:t.state)},protectedFields:()=>[]},watch:{"model.id":{handler(){this.content()},immediate:!0}},created(){this.$events.$on("model.reload",this.reload),this.$events.$on("keydown.left",this.toPrev),this.$events.$on("keydown.right",this.toNext)},destroyed(){this.$events.$off("model.reload",this.reload),this.$events.$off("keydown.left",this.toPrev),this.$events.$off("keydown.right",this.toNext)},methods:{content(){this.$store.dispatch("content/create",{id:this.id,api:this.id,content:this.model.content,ignore:this.protectedFields})},async reload(){await this.$reload(),this.content()},toPrev(t){this.prev&&"body"===t.target.localName&&this.$go(this.prev.link)},toNext(t){this.next&&"body"===t.target.localName&&this.$go(this.next.link)}}},null,null,!1,null,null,null,null).exports;const hi=G({extends:pi,computed:{avatarOptions(){return[{icon:"upload",text:this.$t("change"),click:()=>this.$refs.upload.open()},{icon:"trash",text:this.$t("delete"),click:this.deleteAvatar}]},buttons(){return[{icon:"email",text:`${this.$t("email")}: ${this.model.email}`,disabled:!this.permissions.changeEmail||this.isLocked,click:()=>this.$dialog(this.id+"/changeEmail")},{icon:"bolt",text:`${this.$t("role")}: ${this.model.role}`,disabled:!this.permissions.changeRole||this.isLocked,click:()=>this.$dialog(this.id+"/changeRole")},{icon:"globe",text:`${this.$t("language")}: ${this.model.language}`,disabled:!this.permissions.changeLanguage||this.isLocked,click:()=>this.$dialog(this.id+"/changeLanguage")}]},uploadApi(){return this.$urls.api+"/"+this.id+"/avatar"}},methods:{async deleteAvatar(){await this.$api.users.deleteAvatar(this.model.id),this.avatar=null,this.$store.dispatch("notification/success",":)"),this.$reload()},onAvatar(){this.model.avatar?this.$refs.picture.toggle():this.$refs.upload.open()},uploadedAvatar(){this.$store.dispatch("notification/success",":)"),this.$reload()}}},(function(){var t=this,e=t._self._c;return e("k-inside",{scopedSlots:t._u([{key:"footer",fn:function(){return[e("k-form-buttons",{attrs:{lock:t.lock}})]},proxy:!0}])},[e("div",{staticClass:"k-user-view",attrs:{"data-locked":t.isLocked,"data-id":t.model.id,"data-template":t.blueprint}},[e("div",{staticClass:"k-user-profile"},[e("k-view",[e("k-dropdown",[e("k-button",{staticClass:"k-user-view-image",attrs:{tooltip:t.$t("avatar"),disabled:t.isLocked},on:{click:t.onAvatar}},[t.model.avatar?e("k-image",{attrs:{cover:!0,src:t.model.avatar,ratio:"1/1"}}):e("k-icon",{attrs:{back:"gray-900",color:"gray-200",type:"user"}})],1),t.model.avatar?e("k-dropdown-content",{ref:"picture",attrs:{options:t.avatarOptions}}):t._e()],1),e("k-button-group",{attrs:{buttons:t.buttons}})],1)],1),e("k-view",[e("k-header",{attrs:{editable:t.permissions.changeName&&!t.isLocked,tab:t.tab.name,tabs:t.tabs},on:{edit:function(e){return t.$dialog(t.id+"/changeName")}},scopedSlots:t._u([{key:"left",fn:function(){return[e("k-button-group",[e("k-dropdown",{staticClass:"k-user-view-options"},[e("k-button",{attrs:{disabled:t.isLocked,text:t.$t("settings"),icon:"cog"},on:{click:function(e){return t.$refs.settings.toggle()}}}),e("k-dropdown-content",{ref:"settings",attrs:{options:t.$dropdown(t.id)}})],1),e("k-languages-dropdown")],1)]},proxy:!0},{key:"right",fn:function(){return[t.model.account?t._e():e("k-prev-next",{attrs:{prev:t.prev,next:t.next}})]},proxy:!0}])},[t.model.name&&0!==t.model.name.length?[t._v(" "+t._s(t.model.name)+" ")]:e("span",{staticClass:"k-user-name-placeholder"},[t._v(" "+t._s(t.$t("name"))+" … ")])],2),e("k-sections",{attrs:{blueprint:t.blueprint,empty:t.$t("user.blueprint",{blueprint:t.$esc(t.blueprint)}),lock:t.lock,parent:t.id,tab:t.tab}}),e("k-upload",{ref:"upload",attrs:{url:t.uploadApi,multiple:!1,accept:"image/*"},on:{success:t.uploadedAvatar}})],1)],1)])}),[],!1,null,null,null,null).exports;const mi=G({extends:hi,prevnext:!1},null,null,!1,null,null,null,null).exports;const fi=G({props:{error:String,layout:String}},(function(){var t=this,e=t._self._c;return e(`k-${t.layout}`,{tag:"component"},[e("k-view",{staticClass:"k-error-view"},[e("div",{staticClass:"k-error-view-content"},[e("k-text",[e("p",[e("k-icon",{staticClass:"k-error-view-icon",attrs:{type:"alert"}})],1),t._t("default",(function(){return[e("p",[t._v(" "+t._s(t.error)+" ")])]}))],2)],1)])],1)}),[],!1,null,null,null,null).exports;const gi=G({extends:pi,props:{preview:Object},methods:{action(t){if("replace"===t)this.$refs.upload.open({url:this.$urls.api+"/"+this.id,accept:"."+this.model.extension+","+this.model.mime,multiple:!1})},onUpload(){this.$store.dispatch("notification/success",":)"),this.$reload()}}},(function(){var t=this,e=t._self._c;return e("k-inside",{scopedSlots:t._u([{key:"footer",fn:function(){return[e("k-form-buttons",{attrs:{lock:t.lock}})]},proxy:!0}])},[e("div",{staticClass:"k-file-view",attrs:{"data-locked":t.isLocked,"data-id":t.model.id,"data-template":t.blueprint}},[e("k-file-preview",t._b({},"k-file-preview",t.preview,!1)),e("k-view",{staticClass:"k-file-content"},[e("k-header",{attrs:{editable:t.permissions.changeName&&!t.isLocked,tab:t.tab.name,tabs:t.tabs},on:{edit:function(e){return t.$dialog(t.id+"/changeName")}},scopedSlots:t._u([{key:"left",fn:function(){return[e("k-button-group",[e("k-button",{staticClass:"k-file-view-options",attrs:{link:t.preview.url,responsive:!0,text:t.$t("open"),icon:"open",target:"_blank"}}),e("k-dropdown",{staticClass:"k-file-view-options"},[e("k-button",{attrs:{disabled:t.isLocked,responsive:!0,text:t.$t("settings"),icon:"cog"},on:{click:function(e){return t.$refs.settings.toggle()}}}),e("k-dropdown-content",{ref:"settings",attrs:{options:t.$dropdown(t.id)},on:{action:t.action}})],1),e("k-languages-dropdown")],1)]},proxy:!0},{key:"right",fn:function(){return[e("k-prev-next",{attrs:{prev:t.prev,next:t.next}})]},proxy:!0}])},[t._v(" "+t._s(t.model.filename)+" ")]),e("k-sections",{attrs:{blueprint:t.blueprint,empty:t.$t("file.blueprint",{blueprint:t.$esc(t.blueprint)}),lock:t.lock,parent:t.id,tab:t.tab}}),e("k-upload",{ref:"upload",on:{success:t.onUpload}})],1)],1)])}),[],!1,null,null,null,null).exports;const ki=G({props:{isInstallable:Boolean,isInstalled:Boolean,isOk:Boolean,requirements:Object,translations:Array},data(){return{user:{name:"",email:"",language:this.$translation.code,password:"",role:"admin"}}},computed:{fields(){return{email:{label:this.$t("email"),type:"email",link:!1,autofocus:!0,required:!0},password:{label:this.$t("password"),type:"password",placeholder:this.$t("password")+" …",required:!0},language:{label:this.$t("language"),type:"select",options:this.translations,icon:"globe",empty:!1,required:!0}}},isReady(){return this.isOk&&this.isInstallable},isComplete(){return this.isOk&&this.isInstalled}},methods:{async install(){try{await this.$api.system.install(this.user),await this.$reload({globals:["$system","$translation"]}),this.$store.dispatch("notification/success",this.$t("welcome")+"!")}catch(t){this.$store.dispatch("notification/error",t)}}}},(function(){var t=this,e=t._self._c;return e("k-panel",[e("k-view",{staticClass:"k-installation-view",attrs:{align:"center"}},[t.isComplete?e("k-text",[e("k-headline",[t._v(t._s(t.$t("installation.completed")))]),e("k-link",{attrs:{to:"/login"}},[t._v(" "+t._s(t.$t("login"))+" ")])],1):t.isReady?e("form",{on:{submit:function(e){return e.preventDefault(),t.install.apply(null,arguments)}}},[e("h1",{staticClass:"sr-only"},[t._v(" "+t._s(t.$t("installation"))+" ")]),e("k-fieldset",{attrs:{fields:t.fields,novalidate:!0,value:t.user},on:{input:function(e){t.user=e}}}),e("k-button",{attrs:{text:t.$t("install"),type:"submit",icon:"check"}})],1):e("div",[e("k-headline",[t._v(" "+t._s(t.$t("installation.issues.headline"))+" ")]),e("ul",{staticClass:"k-installation-issues"},[!1===t.isInstallable?e("li",[e("k-icon",{attrs:{type:"alert"}}),e("span",{domProps:{innerHTML:t._s(t.$t("installation.disabled"))}})],1):t._e(),!1===t.requirements.php?e("li",[e("k-icon",{attrs:{type:"alert"}}),e("span",{domProps:{innerHTML:t._s(t.$t("installation.issues.php"))}})],1):t._e(),!1===t.requirements.server?e("li",[e("k-icon",{attrs:{type:"alert"}}),e("span",{domProps:{innerHTML:t._s(t.$t("installation.issues.server"))}})],1):t._e(),!1===t.requirements.mbstring?e("li",[e("k-icon",{attrs:{type:"alert"}}),e("span",{domProps:{innerHTML:t._s(t.$t("installation.issues.mbstring"))}})],1):t._e(),!1===t.requirements.curl?e("li",[e("k-icon",{attrs:{type:"alert"}}),e("span",{domProps:{innerHTML:t._s(t.$t("installation.issues.curl"))}})],1):t._e(),!1===t.requirements.accounts?e("li",[e("k-icon",{attrs:{type:"alert"}}),e("span",{domProps:{innerHTML:t._s(t.$t("installation.issues.accounts"))}})],1):t._e(),!1===t.requirements.content?e("li",[e("k-icon",{attrs:{type:"alert"}}),e("span",{domProps:{innerHTML:t._s(t.$t("installation.issues.content"))}})],1):t._e(),!1===t.requirements.media?e("li",[e("k-icon",{attrs:{type:"alert"}}),e("span",{domProps:{innerHTML:t._s(t.$t("installation.issues.media"))}})],1):t._e(),!1===t.requirements.sessions?e("li",[e("k-icon",{attrs:{type:"alert"}}),e("span",{domProps:{innerHTML:t._s(t.$t("installation.issues.sessions"))}})],1):t._e()]),e("k-button",{attrs:{text:t.$t("retry"),icon:"refresh"},on:{click:t.$reload}})],1)],1)],1)}),[],!1,null,null,null,null).exports;const bi=G({props:{languages:{type:Array,default:()=>[]}},computed:{languagesCollection(){return this.languages.map((t=>({...t,image:{back:"black",color:"gray",icon:"globe"},link:()=>{if(!this.$permissions.languages.update)return null;this.$dialog(`languages/${t.id}/update`)},options:[{icon:"edit",text:this.$t("edit"),disabled:!this.$permissions.languages.update,click(){this.$dialog(`languages/${t.id}/update`)}},{icon:"trash",text:this.$t("delete"),disabled:t.default&&1!==this.languages.length||!this.$permissions.languages.delete,click(){this.$dialog(`languages/${t.id}/delete`)}}]})))},primaryLanguage(){return this.languagesCollection.filter((t=>t.default))},secondaryLanguages(){return this.languagesCollection.filter((t=>!1===t.default))}}},(function(){var t=this,e=t._self._c;return e("k-inside",[e("k-view",{staticClass:"k-languages-view"},[e("k-header",[t._v(" "+t._s(t.$t("view.languages"))+" "),e("k-button-group",{attrs:{slot:"left"},slot:"left"},[e("k-button",{attrs:{text:t.$t("language.create"),disabled:!t.$permissions.languages.create,icon:"add"},on:{click:function(e){return t.$dialog("languages/create")}}})],1)],1),e("section",{staticClass:"k-languages"},[t.languages.length>0?[e("section",{staticClass:"k-languages-view-section"},[e("header",{staticClass:"k-languages-view-section-header"},[e("k-headline",[t._v(t._s(t.$t("languages.default")))])],1),e("k-collection",{attrs:{items:t.primaryLanguage}})],1),e("section",{staticClass:"k-languages-view-section"},[e("header",{staticClass:"k-languages-view-section-header"},[e("k-headline",[t._v(t._s(t.$t("languages.secondary")))])],1),t.secondaryLanguages.length?e("k-collection",{attrs:{items:t.secondaryLanguages}}):e("k-empty",{attrs:{icon:"globe",disabled:!t.$permissions.languages.create},on:{click:function(e){return t.$dialog("languages/create")}}},[t._v(" "+t._s(t.$t("languages.secondary.empty"))+" ")])],1)]:0===t.languages.length?[e("k-empty",{attrs:{icon:"globe",disabled:!t.$permissions.languages.create},on:{click:function(e){return t.$dialog("languages/create")}}},[t._v(" "+t._s(t.$t("languages.empty"))+" ")])]:t._e()],2)],1)],1)}),[],!1,null,null,null,null).exports;const vi=G({components:{"k-login-plugin":window.panel.plugins.login||Lt},props:{methods:Array,pending:Object},data:()=>({issue:""}),computed:{form(){return this.pending.email?"code":"login"},viewClass(){return"code"===this.form?"k-login-code-view":"k-login-view"}},created(){this.$store.dispatch("content/clear")},methods:{async onError(t){null!==t?(!0===t.details.challengeDestroyed&&await this.$reload({globals:["$system"]}),this.issue=t.message):this.issue=null}}},(function(){var t=this,e=t._self._c;return e("k-panel",[e("k-view",{class:t.viewClass,attrs:{align:"center"}},[e("div",[e("h1",{staticClass:"sr-only"},[t._v(" "+t._s(t.$t("login"))+" ")]),t.issue?e("k-login-alert",{on:{click:function(e){t.issue=null}}},[t._v(" "+t._s(t.issue)+" ")]):t._e(),"code"===t.form?e("k-login-code",t._b({on:{error:t.onError}},"k-login-code",t.$props,!1)):e("k-login-plugin",{attrs:{methods:t.methods},on:{error:t.onError}})],1)])],1)}),[],!1,null,null,null,null).exports;const yi=G({extends:pi,props:{status:Object},computed:{protectedFields:()=>["title"]}},(function(){var t=this,e=t._self._c;return e("k-inside",{scopedSlots:t._u([{key:"footer",fn:function(){return[e("k-form-buttons",{attrs:{lock:t.lock}})]},proxy:!0}])},[e("k-view",{staticClass:"k-page-view",attrs:{"data-locked":t.isLocked,"data-id":t.model.id,"data-template":t.blueprint}},[e("k-header",{attrs:{editable:t.permissions.changeTitle&&!t.isLocked,tab:t.tab.name,tabs:t.tabs},on:{edit:function(e){return t.$dialog(t.id+"/changeTitle")}},scopedSlots:t._u([{key:"left",fn:function(){return[e("k-button-group",[t.permissions.preview&&t.model.previewUrl?e("k-button",{staticClass:"k-page-view-preview",attrs:{link:t.model.previewUrl,responsive:!0,text:t.$t("open"),icon:"open",target:"_blank"}}):t._e(),t.status?e("k-status-icon",{attrs:{status:t.model.status,disabled:!t.permissions.changeStatus||t.isLocked,responsive:!0,text:t.status.label},on:{click:function(e){return t.$dialog(t.id+"/changeStatus")}}}):t._e(),e("k-dropdown",{staticClass:"k-page-view-options"},[e("k-button",{attrs:{disabled:!0===t.isLocked,responsive:!0,text:t.$t("settings"),icon:"cog"},on:{click:function(e){return t.$refs.settings.toggle()}}}),e("k-dropdown-content",{ref:"settings",attrs:{options:t.$dropdown(t.id)}})],1),e("k-languages-dropdown")],1)]},proxy:!0},{key:"right",fn:function(){return[t.model.id?e("k-prev-next",{attrs:{prev:t.prev,next:t.next}}):t._e()]},proxy:!0}])},[t._v(" "+t._s(t.model.title)+" ")]),e("k-sections",{attrs:{blueprint:t.blueprint,empty:t.$t("page.blueprint",{blueprint:t.$esc(t.blueprint)}),lock:t.lock,parent:t.id,tab:t.tab}})],1)],1)}),[],!1,null,null,null,null).exports;const $i=G({props:{id:String},computed:{view(){return"k-"+this.id+"-plugin-view"}}},(function(){var t=this._self._c;return t("k-inside",[t(this.view,{tag:"component"})],1)}),[],!1,null,null,null,null).exports;const _i=G({data:()=>({isLoading:!1,issue:"",values:{password:null,passwordConfirmation:null}}),computed:{fields(){return{password:{autofocus:!0,label:this.$t("user.changePassword.new"),icon:"key",type:"password"},passwordConfirmation:{label:this.$t("user.changePassword.new.confirm"),icon:"key",type:"password"}}}},mounted(){this.$store.dispatch("title",this.$t("view.resetPassword"))},methods:{async submit(){if(!this.values.password||this.values.password.length<8)return this.issue=this.$t("error.user.password.invalid"),!1;if(this.values.password!==this.values.passwordConfirmation)return this.issue=this.$t("error.user.password.notSame"),!1;this.isLoading=!0;try{await this.$api.users.changePassword(this.$user.id,this.values.password),this.$store.dispatch("notification/success",":)"),this.$go("/")}catch(t){this.issue=t.message}finally{this.isLoading=!1}}}},(function(){var t=this,e=t._self._c;return e("k-inside",[e("k-view",{staticClass:"k-password-reset-view",attrs:{align:"center"}},[e("k-form",{attrs:{fields:t.fields,"submit-button":t.$t("change"),value:t.values},on:{input:function(e){t.values=e},submit:t.submit},scopedSlots:t._u([{key:"header",fn:function(){return[e("h1",{staticClass:"sr-only"},[t._v(" "+t._s(t.$t("view.resetPassword"))+" ")]),t.issue?e("k-login-alert",{on:{click:function(e){t.issue=null}}},[t._v(" "+t._s(t.issue)+" ")]):t._e(),e("k-user-info",{attrs:{user:t.$user}})]},proxy:!0},{key:"footer",fn:function(){return[e("div",{staticClass:"k-login-buttons"},[e("k-button",{staticClass:"k-login-button",attrs:{icon:"check",type:"submit"}},[t._v(" "+t._s(t.$t("change"))+" "),t.isLoading?[t._v(" … ")]:t._e()],2)],1)]},proxy:!0}])})],1)],1)}),[],!1,null,null,null,null).exports;const wi=G({extends:pi,computed:{protectedFields:()=>["title"]}},(function(){var t=this,e=t._self._c;return e("k-inside",{scopedSlots:t._u([{key:"footer",fn:function(){return[e("k-form-buttons",{attrs:{lock:t.lock}})]},proxy:!0}])},[e("k-view",{staticClass:"k-site-view",attrs:{"data-locked":t.isLocked,"data-id":"/","data-template":"site"}},[e("k-header",{attrs:{editable:t.permissions.changeTitle&&!t.isLocked,tabs:t.tabs,tab:t.tab.name},on:{edit:function(e){return t.$dialog("site/changeTitle")}},scopedSlots:t._u([{key:"left",fn:function(){return[e("k-button-group",[e("k-button",{staticClass:"k-site-view-preview",attrs:{link:t.model.previewUrl,responsive:!0,text:t.$t("open"),icon:"open",target:"_blank"}}),e("k-languages-dropdown")],1)]},proxy:!0}])},[t._v(" "+t._s(t.model.title)+" ")]),e("k-sections",{attrs:{blueprint:t.blueprint,empty:t.$t("site.blueprint"),lock:t.lock,tab:t.tab,parent:"site"},on:{submit:function(e){return t.$emit("submit",e)}}})],1)],1)}),[],!1,null,null,null,null).exports;const xi=G({props:{environment:Array,exceptions:Array,plugins:Array,security:Array,urls:Object},data:()=>({accessible:[]}),computed:{securityIssues(){const t=this.accessible.map((t=>({id:t,text:this.$t("system.issues."+t),link:"https://getkirby.com/security/"+t,icon:"folder"})));return this.security.concat(t).map((t=>({image:{back:"var(--color-red-200)",icon:t.icon||"alert",color:"var(--color-red)"},...t})))}},async created(){this.exceptions.length>0&&(console.info("The following errors occurred during the update check of Kirby and/or plugins:"),this.exceptions.map((t=>console.warn(t))),console.info("End of errors from the update check.")),console.info("Running system health checks for the Panel system view; failed requests in the following console output are expected behavior.");const t=(Promise.allSettled||Promise.all).bind(Promise),e=Object.entries(this.urls).map(this.check);await t(e),console.info(`System health checks ended. ${this.accessible.length} issues with accessible files/folders found (see the security list in the system view).`)},methods:{async check([t,e]){if(!e)return!1;!0===await this.isAccessible(e)&&this.accessible.push(t)},isAccessible:async t=>(await fetch(t,{cache:"no-store"})).status<400,retry(){this.$go(window.location.href)}}},(function(){var t=this,e=t._self._c;return e("k-inside",[e("k-view",{staticClass:"k-system-view"},[e("k-header",[t._v(" "+t._s(t.$t("view.system"))+" ")]),e("section",{staticClass:"k-system-view-section"},[e("header",{staticClass:"k-system-view-section-header"},[e("k-headline",[t._v(t._s(t.$t("environment")))])],1),e("k-stats",{staticClass:"k-system-info",attrs:{reports:t.environment,size:"medium"}})],1),t.securityIssues.length?e("section",{staticClass:"k-system-view-section"},[e("header",{staticClass:"k-system-view-section-header"},[e("k-headline",[t._v(t._s(t.$t("security")))]),e("k-button",{attrs:{tooltip:t.$t("retry"),icon:"refresh"},on:{click:t.retry}})],1),e("k-items",{attrs:{items:t.securityIssues}})],1):t._e(),t.plugins.length?e("section",{staticClass:"k-system-view-section"},[e("header",{staticClass:"k-system-view-section-header"},[e("k-headline",{attrs:{link:"https://getkirby.com/plugins"}},[t._v(" "+t._s(t.$t("plugins"))+" ")])],1),e("k-table",{attrs:{index:!1,columns:{name:{label:t.$t("name"),type:"url",mobile:!0},author:{label:t.$t("author")},license:{label:t.$t("license")},version:{label:t.$t("version"),type:"update-status",mobile:!0,width:"10rem"}},rows:t.plugins}})],1):t._e()],1)],1)}),[],!1,null,null,null,null).exports;const Si=G({props:{role:Object,roles:Array,search:String,title:String,users:Object},computed:{items(){return this.users.data.map((t=>(t.options=this.$dropdown(t.link),t)))}},methods:{paginate(t){this.$reload({query:{page:t.page}})}}},(function(){var t=this,e=t._self._c;return e("k-inside",[e("k-view",{staticClass:"k-users-view"},[e("k-header",{scopedSlots:t._u([{key:"left",fn:function(){return[e("k-button-group",{attrs:{buttons:[{disabled:!1===t.$permissions.users.create,text:t.$t("user.create"),icon:"add",click:()=>t.$dialog("users/create")}]}})]},proxy:!0},t.roles.length>1?{key:"right",fn:function(){return[e("k-button-group",[e("k-dropdown",[e("k-button",{attrs:{responsive:!0,text:`${t.$t("role")}: ${t.role?t.role.title:t.$t("role.all")}`,icon:"funnel"},on:{click:function(e){return t.$refs.roles.toggle()}}}),e("k-dropdown-content",{ref:"roles",attrs:{align:"right"}},[e("k-dropdown-item",{attrs:{icon:"bolt",link:"/users"}},[t._v(" "+t._s(t.$t("role.all"))+" ")]),e("hr"),t._l(t.roles,(function(s){return e("k-dropdown-item",{key:s.id,attrs:{link:"/users/?role="+s.id,icon:"bolt"}},[t._v(" "+t._s(s.title)+" ")])}))],2)],1)],1)]},proxy:!0}:null],null,!0)},[t._v(" "+t._s(t.$t("view.users"))+" ")]),t.users.data.length>0?[e("k-collection",{attrs:{items:t.items,pagination:t.users.pagination},on:{paginate:t.paginate}})]:0===t.users.pagination.total?[e("k-empty",{attrs:{icon:"users"}},[t._v(" "+t._s(t.$t("role.empty"))+" ")])]:t._e()],2)],1)}),[],!1,null,null,null,null).exports,Ci={install(t){t.component("k-account-view",mi),t.component("k-error-view",fi),t.component("k-file-view",gi),t.component("k-installation-view",ki),t.component("k-languages-view",bi),t.component("k-login-view",vi),t.component("k-page-view",yi),t.component("k-plugin-view",$i),t.component("k-reset-password-view",_i),t.component("k-site-view",wi),t.component("k-system-view",xi),t.component("k-users-view",Si),t.component("k-user-view",hi)}};const Oi=G({computed:{placeholder(){return this.field("code",{}).placeholder},languages(){return this.field("language",{options:[]}).options}},methods:{focus(){this.$refs.code.focus()}}},(function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-block-type-code-editor"},[e("k-input",{ref:"code",attrs:{buttons:!1,placeholder:t.placeholder,spellcheck:!1,value:t.content.code,type:"textarea"},on:{input:function(e){return t.update({code:e})}}}),t.languages.length?e("div",{staticClass:"k-block-type-code-editor-language"},[e("k-icon",{attrs:{type:"code"}}),e("k-input",{ref:"language",attrs:{empty:!1,options:t.languages,value:t.content.language,type:"select"},on:{input:function(e){return t.update({language:e})}}})],1):t._e()],1)}),[],!1,null,null,null,null).exports,Ai=Object.freeze(Object.defineProperty({__proto__:null,default:Oi},Symbol.toStringTag,{value:"Module"}));const Ti=G({},(function(){var t=this;return(0,t._self._c)("k-block-title",{attrs:{content:t.content,fieldset:t.fieldset},on:{dblclick:function(e){return t.$emit("open")}}})}),[],!1,null,null,null,null).exports,Ii=Object.freeze(Object.defineProperty({__proto__:null,default:Ti},Symbol.toStringTag,{value:"Module"}));const ji=G({computed:{captionMarks(){return this.field("caption",{marks:!0}).marks},crop(){return this.content.crop||!1},ratio(){return this.content.ratio||!1}}},(function(){var t=this,e=t._self._c;return e("figure",[e("ul",{on:{dblclick:t.open}},[0===t.content.images.length?t._l(5,(function(s){return e("li",{key:s,staticClass:"k-block-type-gallery-placeholder"},[e("k-aspect-ratio",{attrs:{ratio:t.ratio}})],1)})):t._l(t.content.images,(function(s){return e("li",{key:s.id},[e("k-aspect-ratio",{attrs:{ratio:t.ratio,cover:t.crop}},[e("img",{attrs:{src:s.url,srcset:s.image.srcset,alt:s.alt}})])],1)}))],2),t.content.caption?e("figcaption",[e("k-writer",{attrs:{inline:!0,marks:t.captionMarks,value:t.content.caption},on:{input:function(e){return t.$emit("update",{caption:e})}}})],1):t._e()])}),[],!1,null,null,null,null).exports,Mi=Object.freeze(Object.defineProperty({__proto__:null,default:ji},Symbol.toStringTag,{value:"Module"}));const Ei=G({computed:{textField(){return this.field("text",{marks:!0})}},methods:{focus(){this.$refs.input.focus()}}},(function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-block-type-heading-input",attrs:{"data-level":t.content.level}},[e("k-writer",{ref:"input",attrs:{inline:!0,marks:t.textField.marks,placeholder:t.textField.placeholder,value:t.content.text},on:{input:function(e){return t.update({text:e})}}})],1)}),[],!1,null,null,null,null).exports,Li=Object.freeze(Object.defineProperty({__proto__:null,default:Ei},Symbol.toStringTag,{value:"Module"}));const Bi=G({computed:{captionMarks(){return this.field("caption",{marks:!0}).marks},crop(){return this.content.crop||!1},src(){var t;return"web"===this.content.location?this.content.src:!!(null==(t=this.content.image[0])?void 0:t.url)&&this.content.image[0].url},ratio(){return this.content.ratio||!1}}},(function(){var t=this,e=t._self._c;return e("k-block-figure",{attrs:{caption:t.content.caption,"caption-marks":t.captionMarks,"empty-text":t.$t("field.blocks.image.placeholder")+" …","is-empty":!t.src,"empty-icon":"image"},on:{open:t.open,update:t.update}},[t.src?[t.ratio?e("k-aspect-ratio",{attrs:{ratio:t.ratio,cover:t.crop}},[e("img",{attrs:{alt:t.content.alt,src:t.src}})]):e("img",{staticClass:"k-block-type-image-auto",attrs:{alt:t.content.alt,src:t.src}})]:t._e()],2)}),[],!1,null,null,null,null).exports,Di=Object.freeze(Object.defineProperty({__proto__:null,default:Bi},Symbol.toStringTag,{value:"Module"}));const Pi=G({},(function(){return this._self._c,this._m(0)}),[function(){var t=this._self._c;return t("div",[t("hr")])}],!1,null,null,null,null).exports,Ni=Object.freeze(Object.defineProperty({__proto__:null,default:Pi},Symbol.toStringTag,{value:"Module"}));const Ri=G({computed:{marks(){return this.field("text",{}).marks}},methods:{focus(){this.$refs.input.focus()}}},(function(){var t=this;return(0,t._self._c)("k-input",{ref:"input",staticClass:"k-block-type-list-input",attrs:{marks:t.marks,value:t.content.text,type:"list"},on:{input:function(e){return t.update({text:e})}}})}),[],!1,null,null,null,null).exports,Fi=Object.freeze(Object.defineProperty({__proto__:null,default:Ri},Symbol.toStringTag,{value:"Module"}));const qi=G({computed:{placeholder(){return this.field("text",{}).placeholder}},methods:{focus(){this.$refs.input.focus()}}},(function(){var t=this;return(0,t._self._c)("k-input",{ref:"input",staticClass:"k-block-type-markdown-input",attrs:{buttons:!1,placeholder:t.placeholder,spellcheck:!1,value:t.content.text,type:"textarea"},on:{input:function(e){return t.update({text:e})}}})}),[],!1,null,null,null,null).exports,Yi=Object.freeze(Object.defineProperty({__proto__:null,default:qi},Symbol.toStringTag,{value:"Module"}));const zi=G({computed:{citationField(){return this.field("citation",{})},textField(){return this.field("text",{})}},methods:{focus(){this.$refs.text.focus()}}},(function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-block-type-quote-editor"},[e("k-writer",{ref:"text",staticClass:"k-block-type-quote-text",attrs:{inline:t.textField.inline??!1,marks:t.textField.marks,placeholder:t.textField.placeholder,value:t.content.text},on:{input:function(e){return t.update({text:e})}}}),e("k-writer",{ref:"citation",staticClass:"k-block-type-quote-citation",attrs:{inline:t.citationField.inline??!0,marks:t.citationField.marks,placeholder:t.citationField.placeholder,value:t.content.citation},on:{input:function(e){return t.update({citation:e})}}})],1)}),[],!1,null,null,null,null).exports,Hi=Object.freeze(Object.defineProperty({__proto__:null,default:zi},Symbol.toStringTag,{value:"Module"}));const Ui=G({inheritAttrs:!1,computed:{columns(){return this.table.columns||this.fields},fields(){return this.table.fields||{}},rows(){return this.content.rows||[]},table(){let t=null;for(const e of Object.values(this.fieldset.tabs))e.fields.rows&&(t=e.fields.rows);return t||{}}}},(function(){var t=this;return(0,t._self._c)("k-table",{staticClass:"k-block-type-table-preview",attrs:{columns:t.columns,empty:t.$t("field.structure.empty"),rows:t.rows},nativeOn:{dblclick:function(e){return t.open.apply(null,arguments)}}})}),[],!1,null,null,null,null).exports,Vi=Object.freeze(Object.defineProperty({__proto__:null,default:Ui},Symbol.toStringTag,{value:"Module"}));const Ki=G({computed:{component(){const t="k-"+this.textField.type+"-input";return this.$helper.isComponent(t)?t:"k-writer"},textField(){return this.field("text",{})}},methods:{focus(){this.$refs.input.focus()}}},(function(){var t=this;return(0,t._self._c)(t.component,t._b({ref:"input",tag:"component",staticClass:"k-block-type-text-input",attrs:{value:t.content.text},on:{input:function(e){return t.update({text:e})}}},"component",t.textField,!1))}),[],!1,null,null,null,null).exports,Ji=Object.freeze(Object.defineProperty({__proto__:null,default:Ki},Symbol.toStringTag,{value:"Module"}));const Gi=G({computed:{captionMarks(){return this.field("caption",{marks:!0}).marks},video(){return this.$helper.embed.video(this.content.url,!0)}}},(function(){var t=this,e=t._self._c;return e("k-block-figure",{attrs:{caption:t.content.caption,"caption-marks":t.captionMarks,"empty-text":t.$t("field.blocks.video.placeholder")+" …","is-empty":!t.video,"empty-icon":"video"},on:{open:t.open,update:t.update}},[e("k-aspect-ratio",{attrs:{ratio:"16/9"}},[t.video?e("iframe",{attrs:{src:t.video,referrerpolicy:"strict-origin-when-cross-origin"}}):t._e()])],1)}),[],!1,null,null,null,null).exports,Wi=Object.freeze(Object.defineProperty({__proto__:null,default:Gi},Symbol.toStringTag,{value:"Module"}));const Xi=G({inheritAttrs:!1,props:{attrs:[Array,Object],content:[Array,Object],endpoints:Object,fieldset:Object,id:String,isBatched:Boolean,isFull:Boolean,isHidden:Boolean,isLastInBatch:Boolean,isSelected:Boolean,name:String,next:Object,prev:Object,type:String},data:()=>({skipFocus:!1}),computed:{className(){let t=["k-block-type-"+this.type];return this.fieldset.preview!==this.type&&t.push("k-block-type-"+this.fieldset.preview),!1===this.wysiwyg&&t.push("k-block-type-default"),t},customComponent(){return this.wysiwyg?this.wysiwygComponent:"k-block-type-default"},isEditable(){return!1!==this.fieldset.editable},listeners(){return{...this.$listeners,confirmToRemove:this.confirmToRemove,open:this.open}},tabs(){let t=this.fieldset.tabs;return Object.entries(t).forEach((([e,s])=>{Object.entries(s.fields).forEach((([s])=>{t[e].fields[s].section=this.name,t[e].fields[s].endpoints={field:this.endpoints.field+"/fieldsets/"+this.type+"/fields/"+s,section:this.endpoints.section,model:this.endpoints.model}}))})),t},wysiwyg(){return!1!==this.wysiwygComponent},wysiwygComponent(){if(!1===this.fieldset.preview)return!1;let t;return this.fieldset.preview&&(t="k-block-type-"+this.fieldset.preview,this.$helper.isComponent(t))?t:(t="k-block-type-"+this.type,!!this.$helper.isComponent(t)&&t)}},methods:{close(){this.$refs.drawer.close()},confirmToRemove(){this.$refs.removeDialog.open()},focus(){!0!==this.skipFocus&&("function"==typeof this.$refs.editor.focus?this.$refs.editor.focus():this.$refs.container.focus())},onFocusIn(t){var e,s;(null==(s=null==(e=this.$refs.options)?void 0:e.$el)?void 0:s.contains(t.target))||this.$emit("focus",t)},goTo(t){t&&(this.skipFocus=!0,this.close(),this.$nextTick((()=>{t.$refs.container.focus(),t.open(),this.skipFocus=!1})))},open(){var t;null==(t=this.$refs.drawer)||t.open()},remove(){this.$refs.removeDialog.close(),this.$emit("remove",this.id)}}},(function(){var t=this,e=t._self._c;return e("div",{ref:"container",staticClass:"k-block-container",class:"k-block-container-type-"+t.type,attrs:{"data-batched":t.isBatched,"data-disabled":t.fieldset.disabled,"data-hidden":t.isHidden,"data-id":t.id,"data-last-in-batch":t.isLastInBatch,"data-selected":t.isSelected,"data-translate":t.fieldset.translate,tabindex:"0"},on:{keydown:[function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"down",40,e.key,["Down","ArrowDown"])?null:e.ctrlKey&&e.shiftKey?(e.preventDefault(),t.$emit("sortDown")):null},function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"up",38,e.key,["Up","ArrowUp"])?null:e.ctrlKey&&e.shiftKey?(e.preventDefault(),t.$emit("sortUp")):null}],focus:function(e){return t.$emit("focus")},focusin:t.onFocusIn}},[e("div",{staticClass:"k-block",class:t.className},[e(t.customComponent,t._g(t._b({ref:"editor",tag:"component"},"component",t.$props,!1),t.listeners))],1),e("k-block-options",t._g({ref:"options",attrs:{"is-batched":t.isBatched,"is-editable":t.isEditable,"is-full":t.isFull,"is-hidden":t.isHidden}},t.listeners)),t.isEditable&&!t.isBatched?e("k-form-drawer",{ref:"drawer",staticClass:"k-block-drawer",attrs:{id:t.id,icon:t.fieldset.icon||"box",tabs:t.tabs,title:t.fieldset.name,value:t.content},on:{close:function(e){t.focus(),t.$emit("close")},input:function(e){return t.$emit("update",e)},open:function(e){return t.$emit("open")}},scopedSlots:t._u([{key:"options",fn:function(){return[t.isHidden?e("k-button",{staticClass:"k-drawer-option",attrs:{icon:"hidden"},on:{click:function(e){return t.$emit("show")}}}):t._e(),e("k-button",{staticClass:"k-drawer-option",attrs:{disabled:!t.prev,icon:"angle-left"},on:{click:function(e){return e.preventDefault(),e.stopPropagation(),t.goTo(t.prev)}}}),e("k-button",{staticClass:"k-drawer-option",attrs:{disabled:!t.next,icon:"angle-right"},on:{click:function(e){return e.preventDefault(),e.stopPropagation(),t.goTo(t.next)}}}),e("k-button",{staticClass:"k-drawer-option",attrs:{icon:"trash"},on:{click:function(e){return e.preventDefault(),e.stopPropagation(),t.confirmToRemove.apply(null,arguments)}}})]},proxy:!0}],null,!1,2211169536)}):t._e(),e("k-remove-dialog",{ref:"removeDialog",attrs:{text:t.$t("field.blocks.delete.confirm")},on:{submit:t.remove}})],1)}),[],!1,null,null,null,null).exports;const Zi=G({components:{"k-block-pasteboard":G({inheritAttrs:!1,computed:{shortcut(){return this.$helper.keyboard.metaKey()+"+v"}},methods:{close(){this.$refs.dialog.close()},open(){this.$refs.dialog.open()},onPaste(t){this.$emit("paste",t),this.close()}}},(function(){var t=this,e=t._self._c;return e("k-dialog",{ref:"dialog",staticClass:"k-block-importer",attrs:{"cancel-button":!1,"submit-button":!1,size:"large"}},[e("label",{attrs:{for:"pasteboard"},domProps:{innerHTML:t._s(t.$t("field.blocks.fieldsets.paste",{shortcut:t.shortcut}))}}),e("textarea",{attrs:{id:"pasteboard"},on:{paste:function(e){return e.preventDefault(),t.onPaste.apply(null,arguments)}}})])}),[],!1,null,null,null,null).exports},inheritAttrs:!1,props:{autofocus:Boolean,empty:String,endpoints:Object,fieldsets:Object,fieldsetGroups:Object,group:String,max:{type:Number,default:null},value:{type:Array,default:()=>[]}},data(){return{isEditing:!1,isMultiSelectKey:!1,batch:[],blocks:this.value,current:null,isFocussed:!1}},computed:{draggableOptions(){return{id:this._uid,handle:".k-sort-handle",list:this.blocks,move:this.move,delay:10,data:{fieldsets:this.fieldsets,isFull:this.isFull},options:{group:this.group}}},hasFieldsets(){return Object.keys(this.fieldsets).length},isEmpty(){return 0===this.blocks.length},isFull(){return null!==this.max&&this.blocks.length>=this.max},selected(){return this.current},selectedOrBatched(){return this.batch.length>0?this.batch:this.selected?[this.selected]:[]}},watch:{value(){this.blocks=this.value}},created(){this.$events.$on("blur",this.onBlur),this.$events.$on("copy",this.onCopy),this.$events.$on("focus",this.onOutsideFocus),this.$events.$on("keydown",this.onKey),this.$events.$on("keyup",this.onKey),this.$events.$on("paste",this.onPaste)},destroyed(){this.$events.$off("blur",this.onBlur),this.$events.$off("copy",this.onCopy),this.$events.$off("focus",this.onOutsideFocus),this.$events.$off("keydown",this.onKey),this.$events.$off("keyup",this.onKey),this.$events.$off("paste",this.onPaste)},mounted(){setTimeout((()=>{!0===this.$props.autofocus&&this.focus()}),100)},methods:{append(t,e){if("string"!=typeof t){if(Array.isArray(t)){let s=this.$helper.clone(t).map((t=>(t.id=this.$helper.uuid(),t)));const n=Object.keys(this.fieldsets);if(s=s.filter((t=>n.includes(t.type))),this.max){const t=this.max-this.blocks.length;s=s.slice(0,t)}this.blocks.splice(e,0,...s),this.save()}}else this.add(t,e)},async add(t="text",e){const s=await this.$api.get(this.endpoints.field+"/fieldsets/"+t);this.blocks.splice(e,0,s),this.save(),this.$nextTick((()=>{this.focusOrOpen(s)}))},addToBatch(t){null!==this.selected&&!1===this.batch.includes(this.selected)&&(this.batch.push(this.selected),this.current=null),!1===this.batch.includes(t.id)&&this.batch.push(t.id)},choose(t){if(1===Object.keys(this.fieldsets).length){const e=Object.values(this.fieldsets)[0].type;this.add(e,t)}else this.$refs.selector.open(t)},chooseToConvert(t){this.$refs.selector.open(t,{disabled:[t.type],headline:this.$t("field.blocks.changeType"),event:"convert"})},click(t){this.$emit("click",t)},confirmToRemoveAll(){this.$refs.removeAll.open()},confirmToRemoveSelected(){this.$refs.removeSelected.open()},copy(t){if(0===this.blocks.length)return!1;if(0===this.selectedOrBatched.length)return!1;let e=[];for(const s of this.blocks)this.selectedOrBatched.includes(s.id)&&e.push(s);if(0===e.length)return!1;this.$helper.clipboard.write(e,t),t instanceof ClipboardEvent==!1&&(this.batch=this.selectedOrBatched),this.$store.dispatch("notification/success",`${e.length} copied!`)},copyAll(){this.selectAll(),this.copy(),this.deselectAll()},async convert(t,e){var s;const n=this.findIndex(e.id);if(-1===n)return!1;const i=t=>{let e={};for(const s of Object.values((null==t?void 0:t.tabs)??{}))e={...e,...s.fields};return e},o=this.blocks[n],r=await this.$api.get(this.endpoints.field+"/fieldsets/"+t),l=this.fieldsets[o.type],a=this.fieldsets[t];if(!a)return!1;let u=r.content;const c=i(a),d=i(l);for(const[p,h]of Object.entries(c)){const t=d[p];(null==t?void 0:t.type)===h.type&&(null==(s=null==o?void 0:o.content)?void 0:s[p])&&(u[p]=o.content[p])}this.blocks[n]={...r,id:o.id,content:u},this.save()},deselectAll(){this.batch=[],this.current=null},async duplicate(t,e){const s={...this.$helper.clone(t),id:this.$helper.uuid()};this.blocks.splice(e+1,0,s),this.save()},fieldset(t){return this.fieldsets[t.type]||{icon:"box",name:t.type,tabs:{content:{fields:{}}},type:t.type}},find(t){return this.blocks.find((e=>e.id===t))},findIndex(t){return this.blocks.findIndex((e=>e.id===t))},focus(t){var e,s,n;null==(n=null==(s=this.$refs["block-"+((null==t?void 0:t.id)??(null==(e=this.blocks[0])?void 0:e.id))])?void 0:s[0])||n.focus()},focusOrOpen(t){this.fieldsets[t.type].wysiwyg?this.focus(t):this.open(t)},hide(t){Vue.set(t,"isHidden",!0),this.save()},isBatched(t){return this.batch.includes(t.id)},isInputEvent(){const t=document.querySelector(":focus");return t&&(null==t?void 0:t.matches("input, textarea, [contenteditable], .k-writer"))},isLastInBatch(t){const[e]=this.batch.slice(-1);return e&&t.id===e},isOnlyInstance:()=>1===document.querySelectorAll(".k-blocks").length,isSelected(t){return this.selected&&this.selected===t.id},move(t){if(t.from!==t.to){const e=t.draggedContext.element,s=t.relatedContext.component.componentData||t.relatedContext.component.$parent.componentData;if(!1===Object.keys(s.fieldsets).includes(e.type))return!1;if(!0===s.isFull)return!1}return!0},onBlur(){0===this.batch.length&&(this.isMultiSelectKey=!1)},onCopy(t){return!1!==this.$el.contains(t.target)&&!0!==this.isEditing&&!this.$store.state.dialog&&!0!==this.isInputEvent(t)&&this.copy(t)},onKey(t){this.isMultiSelectKey=t.metaKey||t.ctrlKey||t.altKey},onOutsideFocus(t){if("function"==typeof t.target.closest&&t.target.closest(".k-dialog"))return;const e=document.querySelector(".k-overlay:last-of-type");if(!1===this.$el.contains(t.target)&&(!e||!1===e.contains(t.target)))return this.select(null);if(e){const e=this.$el.closest(".k-layout-column");if(!1===(null==e?void 0:e.contains(t.target)))return this.select(null)}},onPaste(t){var e;return!0===(null==(e=this.$refs.selector)?void 0:e.isOpen())?this.paste(t):!0!==this.isInputEvent(t)&&(!0!==this.isEditing&&!this.$store.state.dialog&&((0!==this.selectedOrBatched.length||!1!==this.$el.contains(t.target))&&this.paste(t)))},open(t){var e;null==(e=this.$refs["block-"+t.id])||e[0].open()},async paste(t){const e=this.$helper.clipboard.read(t),s=await this.$api.post(this.endpoints.field+"/paste",{html:e});let n=this.selectedOrBatched[this.selectedOrBatched.length-1],i=this.findIndex(n);-1===i&&(i=this.blocks.length),this.append(s,i+1)},pasteboard(){this.$refs.pasteboard.open()},prevNext(t){var e;if(this.blocks[t])return null==(e=this.$refs["block-"+this.blocks[t].id])?void 0:e[0]},remove(t){var e;const s=this.findIndex(t.id);-1!==s&&((null==(e=this.selected)?void 0:e.id)===t.id&&this.select(null),this.$delete(this.blocks,s),this.save())},removeAll(){this.batch=[],this.blocks=[],this.save(),this.$refs.removeAll.close()},removeSelected(){for(const t of this.batch){const e=this.findIndex(t);-1!==e&&this.$delete(this.blocks,e)}this.deselectAll(),this.save(),this.$refs.removeSelected.close()},save(){this.$emit("input",this.blocks)},select(t,e=null){if(e&&this.isMultiSelectKey&&this.onKey(e),t&&this.isMultiSelectKey)return this.addToBatch(t),void(this.current=null);this.batch=[],this.current=t?t.id:null},selectAll(){this.batch=Object.values(this.blocks).map((t=>t.id))},show(t){Vue.set(t,"isHidden",!1),this.save()},sort(t,e,s){if(s<0)return;let n=this.$helper.clone(this.blocks);n.splice(e,1),n.splice(s,0,t),this.blocks=n,this.save(),this.$nextTick((()=>{this.focus(t)}))},update(t,e){const s=this.findIndex(t.id);if(-1!==s)for(const n in e)Vue.set(this.blocks[s].content,n,e[n]);this.save()}}},(function(){var t=this,e=t._self._c;return e("div",{ref:"wrapper",staticClass:"k-blocks",attrs:{"data-empty":0===t.blocks.length,"data-multi-select-key":t.isMultiSelectKey},on:{focusin:function(e){t.focussed=!0},focusout:function(e){t.focussed=!1}}},[t.hasFieldsets?[e("k-draggable",t._b({staticClass:"k-blocks-list",on:{sort:t.save},scopedSlots:t._u([{key:"footer",fn:function(){return[e("k-empty",{staticClass:"k-blocks-empty",attrs:{icon:"box"},on:{click:function(e){return t.choose(t.blocks.length)}}},[t._v(" "+t._s(t.empty||t.$t("field.blocks.empty"))+" ")])]},proxy:!0}],null,!1,2413899928)},"k-draggable",t.draggableOptions,!1),t._l(t.blocks,(function(s,n){return e("k-block",t._b({key:s.id,ref:"block-"+s.id,refInFor:!0,attrs:{endpoints:t.endpoints,fieldset:t.fieldset(s),"is-batched":t.isBatched(s),"is-last-in-batch":t.isLastInBatch(s),"is-full":t.isFull,"is-hidden":!0===s.isHidden,"is-selected":t.isSelected(s),next:t.prevNext(n+1),prev:t.prevNext(n-1)},on:{append:function(e){return t.append(e,n+1)},blur:function(e){return t.select(null)},choose:function(e){return t.choose(e)},chooseToAppend:function(e){return t.choose(n+1)},chooseToConvert:function(e){return t.chooseToConvert(s)},chooseToPrepend:function(e){return t.choose(n)},close:function(e){t.isEditing=!1},copy:function(e){return t.copy()},confirmToRemoveSelected:t.confirmToRemoveSelected,duplicate:function(e){return t.duplicate(s,n)},focus:function(e){return t.select(s)},hide:function(e){return t.hide(s)},open:function(e){t.isEditing=!0},paste:function(e){return t.pasteboard()},prepend:function(e){return t.add(e,n)},remove:function(e){return t.remove(s)},sortDown:function(e){return t.sort(s,n,n+1)},sortUp:function(e){return t.sort(s,n,n-1)},show:function(e){return t.show(s)},update:function(e){return t.update(s,e)}},nativeOn:{click:function(e){return e.stopPropagation(),t.select(s,e)}}},"k-block",s,!1))})),1),e("k-block-selector",{ref:"selector",attrs:{fieldsets:t.fieldsets,"fieldset-groups":t.fieldsetGroups},on:{add:t.add,convert:t.convert,paste:function(e){return t.paste(e)}}}),e("k-remove-dialog",{ref:"removeAll",attrs:{text:t.$t("field.blocks.delete.confirm.all")},on:{submit:t.removeAll}}),e("k-remove-dialog",{ref:"removeSelected",attrs:{text:t.$t("field.blocks.delete.confirm.selected")},on:{submit:t.removeSelected}}),e("k-block-pasteboard",{ref:"pasteboard",on:{paste:function(e){return t.paste(e)}}})]:[e("k-box",{attrs:{theme:"info"}},[t._v(" No fieldsets yet ")])]],2)}),[],!1,null,null,null,null).exports;const Qi=G({inheritAttrs:!1,props:{caption:String,captionMarks:[Boolean,Array],cover:{type:Boolean,default:!0},isEmpty:Boolean,emptyIcon:String,emptyText:String,ratio:String},computed:{ratioPadding(){return this.$helper.ratio(this.ratio||"16/9")}}},(function(){var t=this,e=t._self._c;return e("figure",{staticClass:"k-block-figure"},[t.isEmpty?e("k-button",{staticClass:"k-block-figure-empty",attrs:{icon:t.emptyIcon,text:t.emptyText},on:{click:function(e){return t.$emit("open")}}}):e("span",{staticClass:"k-block-figure-container",on:{dblclick:function(e){return t.$emit("open")}}},[t._t("default")],2),t.caption?e("figcaption",[e("k-writer",{attrs:{inline:!0,marks:t.captionMarks,value:t.caption},on:{input:function(e){return t.$emit("update",{caption:e})}}})],1):t._e()],1)}),[],!1,null,null,null,null).exports;const to=G({props:{isBatched:Boolean,isEditable:Boolean,isFull:Boolean,isHidden:Boolean},methods:{open(){this.$refs.options.open()}}},(function(){var t=this,e=t._self._c;return e("k-dropdown",{staticClass:"k-block-options"},[t.isBatched?[e("k-button",{staticClass:"k-block-options-button",attrs:{tooltip:t.$t("copy"),icon:"template"},on:{click:function(e){return e.preventDefault(),t.$emit("copy")}}}),e("k-button",{staticClass:"k-block-options-button",attrs:{tooltip:t.$t("remove"),icon:"trash"},on:{click:function(e){return e.preventDefault(),t.$emit("confirmToRemoveSelected")}}})]:[t.isEditable?e("k-button",{staticClass:"k-block-options-button",attrs:{tooltip:t.$t("edit"),icon:"edit"},on:{click:function(e){return t.$emit("open")}}}):t._e(),e("k-button",{staticClass:"k-block-options-button",attrs:{disabled:t.isFull,tooltip:t.$t("insert.after"),icon:"add"},on:{click:function(e){return t.$emit("chooseToAppend")}}}),e("k-button",{staticClass:"k-block-options-button",attrs:{tooltip:t.$t("delete"),icon:"trash"},on:{click:function(e){return t.$emit("confirmToRemove")}}}),e("k-button",{staticClass:"k-block-options-button",attrs:{tooltip:t.$t("more"),icon:"dots"},on:{click:function(e){return t.$refs.options.toggle()}}}),e("k-button",{staticClass:"k-block-options-button k-sort-handle",attrs:{tooltip:t.$t("sort"),icon:"sort"},on:{keydown:[function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"up",38,e.key,["Up","ArrowUp"])?null:(e.preventDefault(),t.$emit("sortUp"))},function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"down",40,e.key,["Down","ArrowDown"])?null:(e.preventDefault(),t.$emit("sortDown"))}]}}),e("k-dropdown-content",{ref:"options",attrs:{align:"right"}},[e("k-dropdown-item",{attrs:{disabled:t.isFull,icon:"angle-up"},on:{click:function(e){return t.$emit("chooseToPrepend")}}},[t._v(" "+t._s(t.$t("insert.before"))+" ")]),e("k-dropdown-item",{attrs:{disabled:t.isFull,icon:"angle-down"},on:{click:function(e){return t.$emit("chooseToAppend")}}},[t._v(" "+t._s(t.$t("insert.after"))+" ")]),e("hr"),t.isEditable?e("k-dropdown-item",{attrs:{icon:"edit"},on:{click:function(e){return t.$emit("open")}}},[t._v(" "+t._s(t.$t("edit"))+" ")]):t._e(),e("k-dropdown-item",{attrs:{icon:"refresh"},on:{click:function(e){return t.$emit("chooseToConvert")}}},[t._v(" "+t._s(t.$t("field.blocks.changeType"))+" ")]),e("hr"),e("k-dropdown-item",{attrs:{icon:"template"},on:{click:function(e){return t.$emit("copy")}}},[t._v(" "+t._s(t.$t("copy"))+" ")]),e("k-dropdown-item",{attrs:{icon:"download"},on:{click:function(e){return t.$emit("paste")}}},[t._v(" "+t._s(t.$t("paste.after"))+" ")]),e("hr"),e("k-dropdown-item",{attrs:{icon:t.isHidden?"preview":"hidden"},on:{click:function(e){return t.$emit(t.isHidden?"show":"hide")}}},[t._v(" "+t._s(!0===t.isHidden?t.$t("show"):t.$t("hide"))+" ")]),e("k-dropdown-item",{attrs:{disabled:t.isFull,icon:"copy"},on:{click:function(e){return t.$emit("duplicate")}}},[t._v(" "+t._s(t.$t("duplicate"))+" ")]),e("hr"),e("k-dropdown-item",{attrs:{icon:"trash"},on:{click:function(e){return t.$emit("confirmToRemove")}}},[t._v(" "+t._s(t.$t("delete"))+" ")])],1)]],2)}),[],!1,null,null,null,null).exports;const eo=G({inheritAttrs:!1,props:{endpoint:String,fieldsets:Object,fieldsetGroups:Object},data(){return{dialogIsOpen:!1,disabled:[],headline:null,payload:null,event:"add",groups:this.createGroups()}},computed:{shortcut(){return this.$helper.keyboard.metaKey()+"+v"}},methods:{add(t){this.$emit(this.event,t,this.payload),this.$refs.dialog.close()},close(){this.$refs.dialog.close()},createGroups(){let t={},e=0;const s=this.fieldsetGroups||{blocks:{label:this.$t("field.blocks.fieldsets.label"),sets:Object.keys(this.fieldsets)}};return Object.keys(s).forEach((n=>{let i=s[n];i.open=!1!==i.open,i.fieldsets=i.sets.filter((t=>this.fieldsets[t])).map((t=>(e++,{...this.fieldsets[t],index:e}))),0!==i.fieldsets.length&&(t[n]=i)})),t},isOpen(){return this.dialogIsOpen},navigate(t){var e,s;null==(s=null==(e=this.$refs["fieldset-"+t])?void 0:e[0])||s.focus()},onClose(){this.dialogIsOpen=!1,this.$events.$off("paste",this.close)},onOpen(){this.dialogIsOpen=!0,this.$events.$on("paste",this.close)},open(t,e={}){const s={event:"add",disabled:[],headline:null,...e};this.event=s.event,this.disabled=s.disabled,this.headline=s.headline,this.payload=t,this.$refs.dialog.open()}}},(function(){var t=this,e=t._self._c;return e("k-dialog",{ref:"dialog",staticClass:"k-block-selector",attrs:{"cancel-button":!1,"submit-button":!1,size:"medium"},on:{open:t.onOpen,close:t.onClose}},[t.headline?e("k-headline",[t._v(" "+t._s(t.headline)+" ")]):t._e(),t._l(t.groups,(function(s,n){return e("details",{key:n,attrs:{open:s.open}},[e("summary",[t._v(t._s(s.label))]),e("div",{staticClass:"k-block-types"},t._l(s.fieldsets,(function(s){return e("k-button",{key:s.name,ref:"fieldset-"+s.index,refInFor:!0,attrs:{disabled:t.disabled.includes(s.type),icon:s.icon||"box",text:s.name},on:{keydown:[function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"up",38,e.key,["Up","ArrowUp"])?null:t.navigate(s.index-1)},function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"down",40,e.key,["Down","ArrowDown"])?null:t.navigate(s.index+1)}],click:function(e){return t.add(s.type)}}})})),1)])})),e("p",{staticClass:"k-clipboard-hint",domProps:{innerHTML:t._s(t.$t("field.blocks.fieldsets.paste",{shortcut:t.shortcut}))}})],2)}),[],!1,null,null,null,null).exports;const so=G({inheritAttrs:!1,props:{fieldset:Object,content:Object},computed:{icon(){return this.fieldset.icon||"box"},label(){if(!this.fieldset.label||0===this.fieldset.label.length)return!1;if(this.fieldset.label===this.fieldset.name)return!1;const t=this.$helper.string.template(this.fieldset.label,this.content);return"…"!==t&&this.$helper.string.stripHTML(t)},name(){return this.fieldset.name}}},(function(){var t=this,e=t._self._c;return e("div",t._g({staticClass:"k-block-title"},t.$listeners),[e("k-icon",{staticClass:"k-block-icon",attrs:{type:t.icon}}),e("span",{staticClass:"k-block-name"},[t._v(" "+t._s(t.name)+" ")]),t.label?e("span",{staticClass:"k-block-label"},[t._v(" "+t._s(t.label)+" ")]):t._e()],1)}),[],!1,null,null,null,null).exports;const no=G({inheritAttrs:!1,props:{content:[Object,Array],fieldset:Object},methods:{field(t,e=null){let s=null;return Object.values(this.fieldset.tabs).forEach((e=>{e.fields[t]&&(s=e.fields[t])})),s||e},open(){this.$emit("open")},update(t){this.$emit("update",{...this.content,...t})}}},null,null,!1,null,null,null,null).exports,io={install(t){t.component("k-block",Xi),t.component("k-blocks",Zi),t.component("k-block-figure",Qi),t.component("k-block-options",to),t.component("k-block-selector",eo),t.component("k-block-title",so),t.component("k-block-type",no);const e=Object.assign({"./Types/Code.vue":Ai,"./Types/Default.vue":Ii,"./Types/Gallery.vue":Mi,"./Types/Heading.vue":Li,"./Types/Image.vue":Di,"./Types/Line.vue":Ni,"./Types/List.vue":Fi,"./Types/Markdown.vue":Yi,"./Types/Quote.vue":Hi,"./Types/Table.vue":Vi,"./Types/Text.vue":Ji,"./Types/Video.vue":Wi});for(const s in e){const n=s.match(/\/([a-zA-Z]*)\.vue/)[1].toLowerCase();let i=e[s].default;i.extends=no,t.component("k-block-type-"+n,i)}}},oo={inheritAttrs:!1,props:{column:{type:Object,default:()=>({})},field:Object,value:{}}};const ro=G({mixins:[oo],inheritAttrs:!1,props:{value:[Array,String]},computed:{bubbles(){var t,e;let s=this.value;const n=(null==(t=this.column)?void 0:t.options)||(null==(e=this.field)?void 0:e.options)||[];return"string"==typeof s&&(s=s.split(",")),s.map((t=>{"string"==typeof t&&(t={value:t,text:t});for(const e of n)e.value===t.value&&(t.text=e.text);return{back:"light",color:"black",...t}}))}}},(function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-bubbles-field-preview",class:t.$options.class},[e("k-bubbles",{attrs:{bubbles:t.bubbles}})],1)}),[],!1,null,null,null,null).exports;const lo=G({extends:ro,inheritAttrs:!1,class:"k-array-field-preview",computed:{bubbles(){return[{text:1===this.value.length?`1 ${this.$t("entry")}`:`${this.value.length} ${this.$t("entries")}`}]}}},null,null,!1,null,null,null,null).exports;const ao=G({mixins:[oo],inheritAttrs:!1,computed:{text(){return this.value}}},(function(){var t=this;return(0,t._self._c)("p",{staticClass:"k-text-field-preview",class:t.$options.class},[t._v(" "+t._s(t.column.before)+" "),t._t("default",(function(){return[t._v(t._s(t.text))]})),t._v(" "+t._s(t.column.after)+" ")],2)}),[],!1,null,null,null,null).exports;const uo=G({extends:ao,inheritAttrs:!1,props:{value:String},class:"k-date-field-preview",computed:{text(){var t,e,s,n,i,o;if("string"!=typeof this.value)return"";const r=this.$library.dayjs(this.value);if(!r)return"";let l=(null==(t=this.column)?void 0:t.display)||(null==(e=this.field)?void 0:e.display)||"YYYY-MM-DD",a=(null==(n=null==(s=this.column)?void 0:s.time)?void 0:n.display)||(null==(o=null==(i=this.field)?void 0:i.time)?void 0:o.display);return a&&(l+=" "+a),r.format(l)}}},null,null,!1,null,null,null,null).exports;const co=G({mixins:[oo],props:{value:[String,Object]},computed:{link(){return"object"==typeof this.value?this.value.href:this.value},text(){return"object"==typeof this.value?this.value.text:this.link}}},(function(){var t=this,e=t._self._c;return e("p",{staticClass:"k-url-field-preview",class:t.$options.class,attrs:{"data-link":t.link}},[t._v(" "+t._s(t.column.before)+" "),e("k-link",{attrs:{to:t.link},nativeOn:{click:function(t){t.stopPropagation()}}},[t._v(" "+t._s(t.text)+" ")]),t._v(" "+t._s(t.column.after)+" ")],1)}),[],!1,null,null,null,null).exports;const po=G({extends:co,class:"k-email-field-preview"},null,null,!1,null,null,null,null).exports;const ho=G({extends:ro,inheritAttrs:!1,class:"k-files-field-preview",computed:{bubbles(){return this.value.map((t=>({text:t.filename,link:t.link,image:t.image})))}}},null,null,!1,null,null,null,null).exports;const mo=G({mixins:[oo],inheritAttrs:!1,props:{value:Object}},(function(){var t=this;return(0,t._self._c)("k-status-icon",t._b({staticClass:"k-flag-field-preview"},"k-status-icon",t.value,!1))}),[],!1,null,null,null,null).exports;const fo=G({mixins:[oo],inheritAttrs:!1,props:{value:String},computed:{html(){return this.value}}},(function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-html-field-preview",class:t.$options.class},[t._v(" "+t._s(t.column.before)+" "),e("div",{domProps:{innerHTML:t._s(t.html)}}),t._v(" "+t._s(t.column.after)+" ")])}),[],!1,null,null,null,null).exports;const go=G({mixins:[oo],inheritAttrs:!1,props:{value:[Object]}},(function(){return(0,this._self._c)("k-item-image",{staticClass:"k-image-field-preview",attrs:{image:this.value,layout:"list"}})}),[],!1,null,null,null,null).exports;const ko=G({extends:ro,inheritAttrs:!1,class:"k-object-field-preview",props:{value:[Array,Object]},computed:{bubbles:()=>[{text:"{ ... }"}]}},null,null,!1,null,null,null,null).exports;const bo=G({extends:ro,inheritAttrs:!1,class:"k-pages-field-preview"},null,null,!1,null,null,null,null).exports;const vo=G({extends:ao,inheritAttrs:!1,props:{value:String},class:"k-time-field-preview",computed:{text(){const t=this.$library.dayjs.iso(this.value,"time");return(null==t?void 0:t.format(this.field.display))||""}}},null,null,!1,null,null,null,null).exports;const yo=G({props:{field:Object,value:Boolean,column:Object},computed:{text(){return!1!==this.column.text?this.field.text:null}}},(function(){var t=this;return(0,t._self._c)("k-input",{staticClass:"k-toggle-field-preview",attrs:{text:t.text,value:t.value,type:"toggle"},on:{input:function(e){return t.$emit("input",e)}}})}),[],!1,null,null,null,null).exports;const $o=G({extends:ro,inheritAttrs:!1,class:"k-users-field-preview",computed:{bubble(){return this.value.map((t=>({text:t.username,link:t.link,image:t.image})))}}},null,null,!1,null,null,null,null).exports,_o={install(t){t.component("k-array-field-preview",lo),t.component("k-bubbles-field-preview",ro),t.component("k-date-field-preview",uo),t.component("k-email-field-preview",po),t.component("k-files-field-preview",ho),t.component("k-flag-field-preview",mo),t.component("k-html-field-preview",fo),t.component("k-image-field-preview",go),t.component("k-object-field-preview",ko),t.component("k-pages-field-preview",bo),t.component("k-text-field-preview",ao),t.component("k-toggle-field-preview",yo),t.component("k-time-field-preview",vo),t.component("k-url-field-preview",co),t.component("k-users-field-preview",$o),t.component("k-list-field-preview",fo),t.component("k-writer-field-preview",fo),t.component("k-checkboxes-field-preview",ro),t.component("k-multiselect-field-preview",ro),t.component("k-radio-field-preview",ro),t.component("k-select-field-preview",ro),t.component("k-tags-field-preview",ro),t.component("k-toggles-field-preview",ro)}},wo={install(t){t.use(ut),t.use(pt),t.use(Vs),t.use(bn),t.use(Dn),t.use(si),t.use(di),t.use(Ci),t.use(io),t.use(_o),t.use(M)}},xo=(t,e)=>{localStorage.setItem("kirby$content$"+t,JSON.stringify(e))},So={namespaced:!0,state:{current:null,models:{},status:{enabled:!0}},getters:{exists:t=>e=>Object.prototype.hasOwnProperty.call(t.models,e),hasChanges:(t,e)=>t=>{const s=e.model(t).changes;return Object.keys(s).length>0},isCurrent:t=>e=>t.current===e,id:t=>e=>(e=e||t.current,window.panel.$language?e+"?language="+window.panel.$language.code:e),model:(t,e)=>s=>(s=s||t.current,!0===e.exists(s)?t.models[s]:{api:null,originals:{},values:{},changes:{}}),originals:(t,e)=>t=>q(e.model(t).originals),values:(t,e)=>t=>({...e.originals(t),...e.changes(t)}),changes:(t,e)=>t=>q(e.model(t).changes)},mutations:{CLEAR(t){Object.keys(t.models).forEach((e=>{t.models[e].changes={}})),Object.keys(localStorage).forEach((t=>{t.startsWith("kirby$content$")&&localStorage.removeItem(t)}))},CREATE(t,[e,s]){if(!s)return!1;let n=t.models[e]?t.models[e].changes:s.changes;Vue.set(t.models,e,{api:s.api,originals:s.originals,changes:n||{}})},CURRENT(t,e){t.current=e},MOVE(t,[e,s]){const n=q(t.models[e]);Vue.del(t.models,e),Vue.set(t.models,s,n);const i=localStorage.getItem("kirby$content$"+e);localStorage.removeItem("kirby$content$"+e),localStorage.setItem("kirby$content$"+s,i)},REMOVE(t,e){Vue.del(t.models,e),localStorage.removeItem("kirby$content$"+e)},REVERT(t,e){t.models[e]&&(Vue.set(t.models[e],"changes",{}),localStorage.removeItem("kirby$content$"+e))},STATUS(t,e){Vue.set(t.status,"enabled",e)},UPDATE(t,[e,s,n]){if(!t.models[e])return!1;void 0===n&&(n=null),n=q(n);const i=JSON.stringify(n);JSON.stringify(t.models[e].originals[s])==i?Vue.del(t.models[e].changes,s):Vue.set(t.models[e].changes,s,n),xo(e,{api:t.models[e].api,originals:t.models[e].originals,changes:t.models[e].changes})}},actions:{init(t){Object.keys(localStorage).filter((t=>t.startsWith("kirby$content$"))).map((t=>t.split("kirby$content$")[1])).forEach((e=>{const s=localStorage.getItem("kirby$content$"+e);t.commit("CREATE",[e,JSON.parse(s)])})),Object.keys(localStorage).filter((t=>t.startsWith("kirby$form$"))).map((t=>t.split("kirby$form$")[1])).forEach((e=>{const s=localStorage.getItem("kirby$form$"+e);let n=null;try{n=JSON.parse(s)}catch(o){}if(!n||!n.api)return localStorage.removeItem("kirby$form$"+e),!1;const i={api:n.api,originals:n.originals,changes:n.values};t.commit("CREATE",[e,i]),xo(e,i),localStorage.removeItem("kirby$form$"+e)}))},clear(t){t.commit("CLEAR")},create(t,e){const s=q(e.content);Array.isArray(e.ignore)&&e.ignore.forEach((t=>delete s[t])),e.id=t.getters.id(e.id);const n={api:e.api,originals:s,changes:{}};t.commit("CREATE",[e.id,n]),t.dispatch("current",e.id)},current(t,e){t.commit("CURRENT",e)},disable(t){t.commit("STATUS",!1)},enable(t){t.commit("STATUS",!0)},move(t,[e,s]){e=t.getters.id(e),s=t.getters.id(s),t.commit("MOVE",[e,s])},remove(t,e){t.commit("REMOVE",e),t.getters.isCurrent(e)&&t.commit("CURRENT",null)},revert(t,e){e=e||t.state.current,t.commit("REVERT",e)},async save(t,e){if(e=e||t.state.current,t.getters.isCurrent(e)&&!1===t.state.status.enabled)return!1;t.dispatch("disable");const s=t.getters.model(e),n={...s.originals,...s.changes};try{await Vue.$api.patch(s.api,n),t.commit("CREATE",[e,{...s,originals:n}]),t.dispatch("revert",e)}finally{t.dispatch("enable")}},update(t,[e,s,n]){if(n=n||t.state.current,null===e)for(const i in s)t.commit("UPDATE",[n,i,s[i]]);else t.commit("UPDATE",[n,e,s])}}},Co={namespaced:!0,state:{open:[]},mutations:{CLOSE(t,e){t.open=e?t.open.filter((t=>t.id!==e)):[]},GOTO(t,e){t.open=t.open.slice(0,t.open.findIndex((t=>t.id===e))+1)},OPEN(t,e){t.open.push(e)}},actions:{close(t,e){t.commit("CLOSE",e)},goto(t,e){t.commit("GOTO",e)},open(t,e){t.commit("OPEN",e)}}},Oo={timer:null,namespaced:!0,state:{type:null,message:null,details:null,timeout:null},mutations:{SET(t,e){t.type=e.type,t.message=e.message,t.details=e.details,t.timeout=e.timeout},UNSET(t){t.type=null,t.message=null,t.details=null,t.timeout=null}},actions:{close(t){clearTimeout(this.timer),t.commit("UNSET")},deprecated(t,e){console.warn("Deprecated: "+e)},error(t,e){let s=e;"string"==typeof e&&(s={message:e}),e instanceof Error&&(s={message:e.message},window.panel.$config.debug&&window.console.error(e)),t.dispatch("dialog",{component:"k-error-dialog",props:s},{root:!0}),t.dispatch("close")},open(t,e){t.dispatch("close"),t.commit("SET",e),e.timeout&&(this.timer=setTimeout((()=>{t.dispatch("close")}),e.timeout))},success(t,e){"string"==typeof e&&(e={message:e}),t.dispatch("open",{type:"success",timeout:4e3,...e})}}};Vue.use(E);const Ao=new E.Store({strict:!1,state:{dialog:null,drag:null,fatal:!1,isLoading:!1},mutations:{SET_DIALOG(t,e){t.dialog=e},SET_DRAG(t,e){t.drag=e},SET_FATAL(t,e){t.fatal=e},SET_LOADING(t,e){t.isLoading=e}},actions:{dialog(t,e){t.commit("SET_DIALOG",e)},drag(t,e){t.commit("SET_DRAG",e)},fatal(t,e){!1!==e?(console.error("The JSON response could not be parsed"),window.panel.$config.debug&&console.info(e.html),e.silent||t.commit("SET_FATAL",e.html)):t.commit("SET_FATAL",!1)},isLoading(t,e){t.commit("SET_LOADING",!0===e)},navigate(t){t.dispatch("dialog",null),t.dispatch("drawers/close")}},modules:{content:So,drawers:Co,notification:Oo}}),To={install(t){window.panel=window.panel||{},window.onunhandledrejection=t=>{t.preventDefault(),Ao.dispatch("notification/error",t.reason)},window.panel.deprecated=t=>{Ao.dispatch("notification/deprecated",t)},window.panel.error=t.config.errorHandler=t=>{Ao.dispatch("notification/error",t)}}},Io={install(t){const e=L(),s={$on:e.on,$off:e.off,$emit:e.emit,blur(t){s.$emit("blur",t)},click(t){s.$emit("click",t)},copy(t){s.$emit("copy",t)},dragenter(t){s.entered=t.target,s.prevent(t),s.$emit("dragenter",t)},dragleave(t){s.prevent(t),s.entered===t.target&&s.$emit("dragleave",t)},drop(t){s.prevent(t),s.$emit("drop",t)},entered:null,focus(t){s.$emit("focus",t)},keydown(e){let n=["keydown"];(e.metaKey||e.ctrlKey)&&n.push("cmd"),!0===e.altKey&&n.push("alt"),!0===e.shiftKey&&n.push("shift");let i=t.prototype.$helper.string.lcfirst(e.key);const o={escape:"esc",arrowUp:"up",arrowDown:"down",arrowLeft:"left",arrowRight:"right"};o[i]&&(i=o[i]),!1===["alt","control","shift","meta"].includes(i)&&n.push(i),s.$emit(n.join("."),e),s.$emit("keydown",e)},keyup(t){s.$emit("keyup",t)},online(t){s.$emit("online",t)},offline(t){s.$emit("offline",t)},paste(t){s.$emit("paste",t)},prevent(t){t.stopPropagation(),t.preventDefault()}};document.addEventListener("click",s.click,!1),document.addEventListener("copy",s.copy,!0),document.addEventListener("focus",s.focus,!0),document.addEventListener("paste",s.paste,!0),window.addEventListener("blur",s.blur,!1),window.addEventListener("dragenter",s.dragenter,!1),window.addEventListener("dragexit",s.prevent,!1),window.addEventListener("dragleave",s.dragleave,!1),window.addEventListener("drop",s.drop,!1),window.addEventListener("dragover",s.prevent,!1),window.addEventListener("keydown",s.keydown,!1),window.addEventListener("keyup",s.keyup,!1),window.addEventListener("offline",s.offline),window.addEventListener("online",s.online),t.prototype.$events=s}};const jo=async function(t){return{cancel:null,submit:null,props:{},...t}},Mo=async function(t,e={}){let s=null,n=null;"function"==typeof e?(s=e,e={}):(s=e.submit,n=e.cancel);let i=await this.$fiber.request("dialogs/"+t,{...e,type:"$dialog"});return"object"==typeof i&&(i.submit=s||null,i.cancel=n||null,i)};async function Eo(t,e={}){let s=null;if(s="object"==typeof t?await jo.call(this,t):await Mo.call(this,t,e),!s)return!1;if(!s.component||!1===this.$helper.isComponent(s.component))throw Error("The dialog component does not exist");return s.props=s.props||{},this.$store.dispatch("dialog",s),s}function Lo(t,e={}){return async s=>{const n=await this.$fiber.request("dropdowns/"+t,{...e,type:"$dropdown"});if(!n)return!1;if(!1===Array.isArray(n.options)||0===n.options.length)throw Error("The dropdown is empty");n.options.map((t=>(t.dialog&&(t.click=()=>{const e="string"==typeof t.dialog?t.dialog:t.dialog.url,s="object"==typeof t.dialog?t.dialog:{};return this.$dialog(e,s)}),t))),s(n.options)}}async function Bo(t,e,s={}){return await this.$fiber.request("search/"+t,{query:{query:e},type:"$search",...s})}const Do={install(t){const e=new class{constructor(t={}){this.options={base:"/",headers:()=>({}),onFatal:()=>{},onFinish:()=>{},onPushState:()=>{},onReplaceState:()=>{},onStart:()=>{},onSwap:()=>{},query:()=>({}),...t},this.state={}}init(t={},e={}){this.options={...this.options,...e},this.setState(t)}arrayToString(t){return!1===Array.isArray(t)?String(t):t.join(",")}body(t){return"object"==typeof t?JSON.stringify(t):t}async fetch(t,e){return fetch(t,e)}async go(t,e){try{const s=await this.request(t,e);return!1!==s&&this.setState(s,e)}catch(s){if(!0!==(null==e?void 0:e.silent))throw s}}query(t={},e={}){let s=new URLSearchParams(e);return"object"!=typeof t&&(t={}),Object.entries(t).forEach((([t,e])=>{null!==e&&s.set(t,e)})),Object.entries(this.options.query()).forEach((([t,e])=>{null!==(e=s.get(t)??e??null)&&s.set(t,e)})),s}redirect(t){window.location.href=t}reload(t={}){return this.go(window.location.href,{...t,replace:!0})}async request(t="",e={}){var s;const n=!!(e={globals:!1,method:"GET",only:[],query:{},silent:!1,type:"$view",...e}).globals&&this.arrayToString(e.globals),i=this.arrayToString(e.only);this.options.onStart(e);try{const r=this.url(t,e.query);if(new URL(r).origin!==location.origin)return this.redirect(r),!1;const l=await this.fetch(r,{method:e.method,body:this.body(e.body),credentials:"same-origin",cache:"no-store",headers:{...z(this.options.headers()),"content-type":"application/json","x-fiber":!0,"x-fiber-globals":n,"x-fiber-only":i,"x-fiber-referrer":(null==(s=this.state.$view)?void 0:s.path)||null,...z(e.headers??{})}});if(!1===l.headers.has("X-Fiber"))return this.redirect(l.url),!1;const a=await l.text();let u;try{u=JSON.parse(a)}catch(o){return this.options.onFatal({url:r,path:t,options:e,response:l,text:a}),!1}if(!u[e.type])throw Error(`The ${e.type} could not be loaded`);const c=u[e.type];if(c.error)throw Error(c.error);return"$view"===e.type?i.length?Y(this.state,u):u:c}finally{this.options.onFinish(e)}}async setState(t,e={}){return"object"==typeof t&&(this.state=q(t),!0===e.replace||this.url(this.state.$url).href===window.location.href?this.options.onReplaceState(this.state,e):this.options.onPushState(this.state,e),this.options.onSwap(this.state,e),this.state)}url(t="",e={}){return(t="string"==typeof t&&null===t.match(/^https?:\/\//)?new URL(this.options.base+t.replace(/^\//,"")):new URL(t)).search=this.query(e,t.search),t}};t.prototype.$fiber=window.panel.$fiber=e,t.prototype.$dialog=window.panel.$dialog=Eo,t.prototype.$dropdown=window.panel.$dropdown=Lo,t.prototype.$go=window.panel.$go=e.go.bind(e),t.prototype.$reload=window.panel.$reload=e.reload.bind(e),t.prototype.$request=window.panel.$request=e.request.bind(e),t.prototype.$search=window.panel.$search=Bo,t.prototype.$url=window.panel.$url=e.url.bind(e)}};const Po={read:function(t){if(!t)return null;if("string"==typeof t)return t;if(t instanceof ClipboardEvent){t.preventDefault();const e=t.clipboardData.getData("text/html")||t.clipboardData.getData("text/plain")||null;if(e)return e.replace(/\u00a0/g," ")}return null},write:function(t,e){if("string"!=typeof t&&(t=JSON.stringify(t,null,2)),e&&e instanceof ClipboardEvent)return e.preventDefault(),e.clipboardData.setData("text/plain",t),!0;const s=document.createElement("textarea");if(s.value=t,document.body.append(s),navigator.userAgent.match(/ipad|ipod|iphone/i)){s.contentEditable=!0,s.readOnly=!0;const t=document.createRange();t.selectNodeContents(s);const e=window.getSelection();e.removeAllRanges(),e.addRange(t),s.setSelectionRange(0,999999)}else s.select();return document.execCommand("copy"),s.remove(),!0}};function No(t){if("string"==typeof t){if("pattern"===(t=t.toLowerCase()))return"var(--color-gray-800) var(--bg-pattern)";if(!1===t.startsWith("#")&&!1===t.startsWith("var(")){const e="--color-"+t;if(window.getComputedStyle(document.documentElement).getPropertyValue(e))return`var(${e})`}return t}}function Ro(t,e=!1){if(!t.match("youtu"))return!1;let s=null;try{s=new URL(t)}catch(d){return!1}const n=s.pathname.split("/").filter((t=>""!==t)),i=n[0],o=n[1],r="https://"+(!0===e?"www.youtube-nocookie.com":s.host)+"/embed",l=t=>!!t&&null!==t.match(/^[a-zA-Z0-9_-]+$/);let a=s.searchParams,u=null;switch(n.join("/")){case"embed/videoseries":case"playlist":l(a.get("list"))&&(u=r+"/videoseries");break;case"watch":l(a.get("v"))&&(u=r+"/"+a.get("v"),a.has("t")&&a.set("start",a.get("t")),a.delete("v"),a.delete("t"));break;default:s.host.includes("youtu.be")&&l(i)?(u=!0===e?"https://www.youtube-nocookie.com/embed/"+i:"https://www.youtube.com/embed/"+i,a.has("t")&&a.set("start",a.get("t")),a.delete("t")):["embed","shorts"].includes(i)&&l(o)&&(u=r+"/"+o)}if(!u)return!1;const c=a.toString();return c.length&&(u+="?"+c),u}function Fo(t,e=!1){let s=null;try{s=new URL(t)}catch(a){return!1}const n=s.pathname.split("/").filter((t=>""!==t));let i=s.searchParams,o=null;switch(!0===e&&i.append("dnt",1),s.host){case"vimeo.com":case"www.vimeo.com":o=n[0];break;case"player.vimeo.com":o=n[1]}if(!o||!o.match(/^[0-9]*$/))return!1;let r="https://player.vimeo.com/video/"+o;const l=i.toString();return l.length&&(r+="?"+l),r}const qo={youtube:Ro,vimeo:Fo,video:function(t,e=!1){return t.includes("youtu")?Ro(t,e):!!t.includes("vimeo")&&Fo(t,e)}};const Yo={form:function(t){const e={};for(const s in t)e[s]=q(t[s].default);return e},isVisible:function(t,e){if("hidden"===t.type)return!1;if(!t.when)return!0;for(const s in t.when){const n=e[s.toLowerCase()],i=t.when[s];if((void 0!==n||""!==i&&i!==[])&&n!==i)return!1}return!0},subfields:function(t,e){let s={};return Object.keys(e).forEach((n=>{let i=e[n];i.section=t.name,i.endpoints={field:t.endpoints.field+"+"+n,section:t.endpoints.section,model:t.endpoints.model},s[n]=i})),s}},zo=t=>void 0!==Vue.options.components[t],Ho=t=>!!t.dataTransfer&&(!!t.dataTransfer.types&&(!0===t.dataTransfer.types.includes("Files")&&!1===t.dataTransfer.types.includes("text/plain")));const Uo={metaKey:function(){return window.navigator.userAgent.indexOf("Mac")>-1?"cmd":"ctrl"}},Vo=(t="3/2",e="100%",s=!0)=>{const n=String(t).split("/");if(2!==n.length)return e;const i=Number(n[0]),o=Number(n[1]);let r=100;return 0!==i&&0!==o&&(r=s?r/i*o:r/o*i,r=parseFloat(String(r)).toFixed(2)),r+"%"},Ko=t=>{var e=(t=t||{}).desc?-1:1,s=-e,n=/^0/,i=/\s+/g,o=/^\s+|\s+$/g,r=/[^\x00-\x80]/,l=/^0x[0-9a-f]+$/i,a=/(0x[\da-fA-F]+|(^[\+\-]?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?(?=\D|\s|$))|\d+)/g,u=/(^([\w ]+,?[\w ]+)?[\w ]+,?[\w ]+\d+:\d+(:\d+)?[\w ]?|^\d{1,4}[\/\-]\d{1,4}[\/\-]\d{1,4}|^\w+, \w+ \d+, \d{4})/,c=t.insensitive?function(t){return function(t){if(t.toLocaleLowerCase)return t.toLocaleLowerCase();return t.toLowerCase()}(""+t).replace(o,"")}:function(t){return(""+t).replace(o,"")};function d(t){return t.replace(a,"\0$1\0").replace(/\0$/,"").replace(/^\0/,"").split("\0")}function p(t,e){return(!t.match(n)||1===e)&&parseFloat(t)||t.replace(i," ").replace(o,"")||0}return function(t,n){var i=c(t),o=c(n);if(!i&&!o)return 0;if(!i&&o)return s;if(i&&!o)return e;var a=d(i),h=d(o),m=parseInt(i.match(l),16)||1!==a.length&&Date.parse(i),f=parseInt(o.match(l),16)||m&&o.match(u)&&Date.parse(o)||null;if(f){if(mf)return e}for(var g=a.length,k=h.length,b=0,v=Math.max(g,k);b0)return e;if(_<0)return s;if(b===v-1)return 0}else{if(y<$)return s;if(y>$)return e}}return 0}};RegExp.escape=function(t){return t.replace(new RegExp("[-/\\\\^$*+?.()[\\]{}]","gu"),"\\$&")};const Jo={"&":"&","<":"<",">":">",'"':""","'":"'","/":"/","`":"`","=":"="};function Go(t){return String(t).replace(/[&<>"'`=/]/g,(t=>Jo[t]))}function Wo(t,e={}){const s=(t,e={})=>{const n=Go(t.shift()),i=e[n]??null;return null===i?Object.prototype.hasOwnProperty.call(e,n)||"…":0===t.length?i:s(t,i)},n="[{]{1,2}[\\s]?",i="[\\s]?[}]{1,2}";return(t=t.replace(new RegExp(`${n}(.*?)${i}`,"gi"),((t,n)=>s(n.split("."),e)))).replace(new RegExp(`${n}.*${i}`,"gi"),"…")}function Xo(t){const e=String(t);return e.charAt(0).toUpperCase()+e.slice(1)}const Zo={camelToKebab:function(t){return t.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase()},escapeHTML:Go,hasEmoji:function(t){if("string"!=typeof t)return!1;const e=t.match(/(?:[\u2700-\u27bf]|(?:\ud83c[\udde6-\uddff]){2}|[\ud800-\udbff][\udc00-\udfff]|[\u0023-\u0039]\ufe0f?\u20e3|\u3299|\u3297|\u303d|\u3030|\u24c2|\ud83c[\udd70-\udd71]|\ud83c[\udd7e-\udd7f]|\ud83c\udd8e|\ud83c[\udd91-\udd9a]|\ud83c[\udde6-\uddff]|[\ud83c\ude01-\ude02]|\ud83c\ude1a|\ud83c\ude2f|[\ud83c\ude32-\ude3a]|[\ud83c\ude50-\ude51]|\u203c|\u2049|[\u25aa-\u25ab]|\u25b6|\u25c0|[\u25fb-\u25fe]|\u00a9|\u00ae|\u2122|\u2139|\ud83c\udc04|[\u2600-\u26FF]|\u2b05|\u2b06|\u2b07|\u2b1b|\u2b1c|\u2b50|\u2b55|\u231a|\u231b|\u2328|\u23cf|[\u23e9-\u23f3]|[\u23f8-\u23fa]|\ud83c\udccf|\u2934|\u2935|[\u2190-\u21ff])/i);return null!==e&&null!==e.length},lcfirst:function(t){const e=String(t);return e.charAt(0).toLowerCase()+e.slice(1)},pad:function(t,e=2){t=String(t);let s="";for(;s.length]+)>)/gi,"")},template:Wo,ucfirst:Xo,ucwords:function(t){return String(t).split(/ /g).map((t=>Xo(t))).join(" ")},unescapeHTML:function(t){for(const e in Jo)t=String(t).replaceAll(Jo[e],e);return t},uuid:function(){let t,e,s="";for(t=0;t<32;t++)e=16*Math.random()|0,8!=t&&12!=t&&16!=t&&20!=t||(s+="-"),s+=(12==t?4:16==t?3&e|8:e).toString(16);return s}},Qo=(t,e)=>{const s=Object.assign({url:"/",field:"file",method:"POST",attributes:{},complete:function(){},error:function(){},success:function(){},progress:function(){}},e),n=new FormData;n.append(s.field,t,t.name),s.attributes&&Object.keys(s.attributes).forEach((t=>{n.append(t,s.attributes[t])}));const i=new XMLHttpRequest,o=e=>{if(!e.lengthComputable||!s.progress)return;let n=Math.max(0,Math.min(100,e.loaded/e.total*100));s.progress(i,t,Math.ceil(n))};i.upload.addEventListener("loadstart",o),i.upload.addEventListener("progress",o),i.addEventListener("load",(e=>{let n=null;try{n=JSON.parse(e.target.response)}catch(o){n={status:"error",message:"The file could not be uploaded"}}"error"===n.status?s.error(i,t,n):(s.success(i,t,n),s.progress(i,t,100))})),i.addEventListener("error",(e=>{const n=JSON.parse(e.target.response);s.error(i,t,n),s.progress(i,t,100)})),i.open(s.method,s.url,!0),s.headers&&Object.keys(s.headers).forEach((t=>{const e=s.headers[t];i.setRequestHeader(t,e)})),i.send(n)};const tr={isUrl:function(t,e){if((t instanceof URL||t instanceof Location)&&(t=t.toString()),"string"!=typeof t)return!1;try{new URL(t,window.location)}catch(s){return!1}if(!0===e){return/^(?:(?:https?|ftp):\/\/)(?:\S+(?::\S*)?@)?(?:(?!10(?:\.\d{1,3}){3})(?!169\.254(?:\.\d{1,3}){2})(?!192\.168(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:localhost)|(?:(?:[a-z\u00a1-\uffff0-9]+-?)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]+-?)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})))(?::\d{2,5})?(?:\/[^\s]*)?$/i.test(t)}return!0}},er={install(t){Array.prototype.sortBy=function(e){const s=t.prototype.$helper.sort(),n=e.split(" "),i=n[0],o=n[1]||"asc";return this.sort(((t,e)=>{const n=String(t[i]).toLowerCase(),r=String(e[i]).toLowerCase();return"desc"===o?s(r,n):s(n,r)}))},t.prototype.$helper={clipboard:Po,clone:H.clone,color:No,embed:qo,isComponent:zo,isUploadEvent:Ho,debounce:tt,field:Yo,keyboard:Uo,object:H,pad:Zo.pad,ratio:Vo,slug:Zo.slug,sort:Ko,string:Zo,upload:Qo,url:tr,uuid:Zo.uuid},t.prototype.$esc=Zo.escapeHTML}},sr={install(t){t.$t=t.prototype.$t=window.panel.$t=(t,e,s=null)=>{if("string"!=typeof t)return;const n=window.panel.$translation.data[t]||s;return"string"!=typeof n?n:Wo(n,e)},t.directive("direction",{inserted(t,e,s){!0!==s.context.disabled?t.dir=s.context.$direction:t.dir=null}})}};B.extend(D),B.extend(((t,e,s)=>{s.interpret=(t,e="date")=>{const n={date:{"YYYY-MM-DD":!0,"YYYY-MM-D":!0,"YYYY-MM-":!0,"YYYY-MM":!0,"YYYY-M-DD":!0,"YYYY-M-D":!0,"YYYY-M-":!0,"YYYY-M":!0,"YYYY-":!0,YYYYMMDD:!0,"MMM DD YYYY":!1,"MMM D YYYY":!1,"MMM DD YY":!1,"MMM D YY":!1,"MMM YYYY":!0,"MMM DD":!1,"MMM D":!1,"MM YYYY":!0,"M YYYY":!0,"DD MMMM YYYY":!1,"DD MMMM YY":!1,"DD MMMM":!1,"D MMMM YYYY":!1,"D MMMM YY":!1,"D MMMM":!1,"DD MMM YYYY":!1,"D MMM YYYY":!1,"DD MMM YY":!1,"D MMM YY":!1,"DD MMM":!1,"D MMM":!1,"DD MM YYYY":!1,"DD M YYYY":!1,"D MM YYYY":!1,"D M YYYY":!1,"DD MM YY":!1,"D MM YY":!1,"DD M YY":!1,"D M YY":!1,YYYY:!0,MMMM:!0,MMM:!0,"DD MM":!1,"DD M":!1,"D MM":!1,"D M":!1,DD:!1,D:!1},time:{"HH:mm:ss a":!1,"HH:mm:ss":!1,"HH:mm a":!1,"HH:mm":!1,"HH a":!1,HH:!1}};if("string"==typeof t&&""!==t)for(const i in n[e]){const o=s(t,i,n[e][i]);if(!0===o.isValid())return o}return null}})),B.extend(((t,e,s)=>{const n=t=>"date"===t?"YYYY-MM-DD":"time"===t?"HH:mm:ss":"YYYY-MM-DD HH:mm:ss";e.prototype.toISO=function(t="datetime"){return this.format(n(t))},s.iso=function(t,e="datetime"){const i=s(t,n(e));return i&&i.isValid()?i:null}})),B.extend(((t,e)=>{e.prototype.merge=function(t,e="date"){let s=this.clone();if(!t||!t.isValid())return this;if("string"==typeof e){const t={date:["year","month","date"],time:["hour","minute","second"]};if(!1===Object.prototype.hasOwnProperty.call(t,e))throw new Error("Invalid merge unit alias");e=t[e]}for(const n of e)s=s.set(n,t.get(n));return s}})),B.extend(((t,e,s)=>{s.pattern=t=>new class{constructor(t,e){this.dayjs=t,this.pattern=e;const s={year:["YY","YYYY"],month:["M","MM","MMM","MMMM"],day:["D","DD"],hour:["h","hh","H","HH"],minute:["m","mm"],second:["s","ss"],meridiem:["a"]};this.parts=this.pattern.split(/\W/).map(((t,e)=>{const n=this.pattern.indexOf(t);return{index:e,unit:Object.keys(s)[Object.values(s).findIndex((e=>e.includes(t)))],start:n,end:n+(t.length-1)}}))}at(t,e=t){const s=this.parts.filter((s=>s.start<=t&&s.end>=e-1));return s[0]?s[0]:this.parts.filter((e=>e.start<=t)).pop()}format(t){return t&&t.isValid()?t.format(this.pattern):null}}(s,t)})),B.extend(((t,e)=>{e.prototype.round=function(t="date",e=1){const s=["second","minute","hour","date","month","year"];if("day"===t&&(t="date"),!1===s.includes(t))throw new Error("Invalid rounding unit");if(["date","month","year"].includes(t)&&1!==e||"hour"===t&&24%e!=0||["second","minute"].includes(t)&&60%e!=0)throw"Invalid rounding size for "+t;let n=this.clone();const i=s.indexOf(t),o=s.slice(0,i),r=o.pop();if(o.forEach((t=>n=n.startOf(t))),r){const e={month:12,date:n.daysInMonth(),hour:24,minute:60,second:60}[r];Math.round(n.get(r)/e)*e===e&&(n=n.add(1,"date"===t?"day":t)),n=n.startOf(t)}return n=n.set(t,Math.round(n.get(t)/e)*e),n}})),B.extend(((t,e,s)=>{e.prototype.validate=function(t,e,n="day"){if(!this.isValid())return!1;if(!t)return!0;t=s.iso(t);const i={min:"isAfter",max:"isBefore"}[e];return this.isSame(t,n)||this[i](t,n)}}));const nr={install(t){t.prototype.$library={autosize:P,dayjs:B}}},ir={install(t){const e={...t.options.components},s={section:ii};for(const[n,i]of Object.entries(window.panel.plugins.components))i.template||i.render||i.extends?("string"==typeof(null==i?void 0:i.extends)&&(e[i.extends]?i.extends=e[i.extends].extend({options:i,components:{...e,...i.components||{}}}):(window.console.warn(`Problem with plugin trying to register component "${n}": cannot extend non-existent component "${i.extends}"`),i.extends=null)),i.template&&(i.render=null),i.mixins&&(i.mixins=i.mixins.map((t=>"string"==typeof t?s[t]:t))),e[n]&&window.console.warn(`Plugin is replacing "${n}"`),t.component(n,i),e[n]=t.options.components[n]):Ao.dispatch("notification/error",`Neither template or render method provided nor extending a component when loading plugin component "${n}". The component has not been registered.`);for(const n of window.panel.plugins.use)t.use(n)}};Vue.config.productionTip=!1,Vue.config.devtools=!0;const or=new Vue({store:Ao,created(){window.panel.$vue=window.panel.app=this,window.panel.plugins.created.forEach((t=>t(this))),this.$store.dispatch("content/init")},render:()=>Vue.h(J)});Vue.use(To),Vue.use(er),Vue.use(nr),Vue.use(K,Ao),Vue.use(Io),Vue.use(sr),Vue.use(Do),Vue.use(N),Vue.use(wo),Vue.use(ir),or.$mount("#app"); diff --git a/kirby/panel/dist/js/index.min.js b/kirby/panel/dist/js/index.min.js new file mode 100644 index 0000000..a4158f3 --- /dev/null +++ b/kirby/panel/dist/js/index.min.js @@ -0,0 +1,2 @@ +const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./IndexView.min.js","./vendor.min.js","./DocsView.min.js","./Docs.min.js","./PlaygroundView.min.js","./Highlight.min.js"])))=>i.map(i=>d[i]); +import{v as t,I as e,P as i,S as s,F as n,N as o,s as a,l as r,w as l,a as c,b as u,c as d,d as p,e as h,t as m,f,g,h as k,i as b,k as v,D as y,j as $,E as w,m as x,n as _,o as C,T as S,u as O,p as M,r as A,q as I,x as D,y as j,z as E,A as L,B as T,C as B,G as q,H as P,J as N,V as z}from"./vendor.min.js";const F={},Y=function(t,e,i){let s=Promise.resolve();if(e&&e.length>0){const t=document.getElementsByTagName("link"),n=document.querySelector("meta[property=csp-nonce]"),o=(null==n?void 0:n.nonce)||(null==n?void 0:n.getAttribute("nonce"));s=Promise.allSettled(e.map((e=>{if(e=function(t,e){return new URL(t,e).href}(e,i),e in F)return;F[e]=!0;const s=e.endsWith(".css"),n=s?'[rel="stylesheet"]':"";if(!!i)for(let i=t.length-1;i>=0;i--){const n=t[i];if(n.href===e&&(!s||"stylesheet"===n.rel))return}else if(document.querySelector(`link[href="${e}"]${n}`))return;const a=document.createElement("link");return a.rel=s?"stylesheet":"modulepreload",s||(a.as="script"),a.crossOrigin="",a.href=e,o&&a.setAttribute("nonce",o),document.head.appendChild(a),s?new Promise(((t,i)=>{a.addEventListener("load",t),a.addEventListener("error",(()=>i(new Error(`Unable to preload CSS for ${e}`))))})):void 0})))}function n(t){const e=new Event("vite:preloadError",{cancelable:!0});if(e.payload=t,window.dispatchEvent(e),!e.defaultPrevented)throw t}return s.then((e=>{for(const t of e||[])"rejected"===t.status&&n(t.reason);return t().catch(n)}))},R={created(){this.$panel.events.subscribe();for(const t of this.$panel.plugins.created)t(this);this.$panel.events.on("popstate",(()=>{this.$panel.open(window.location.href)})),this.$panel.events.on("drop",(()=>{this.$panel.drag.stop()})),this.$store.dispatch("content/init")},destroyed(){this.$panel.events.unsubscribe()},render(t){if(this.$panel.view.component)return t(this.$panel.view.component,{key:this.$panel.view.component,props:this.$panel.view.props})}},U={props:{after:String}},H={props:{autocomplete:String}},V={props:{autofocus:Boolean}},K={props:{before:String}},W={props:{disabled:Boolean}},J={props:{font:String}},G={props:{help:String}},X={props:{id:{type:[Number,String],default(){return this._uid}}}},Z={props:{invalid:Boolean}},Q={props:{label:String}},tt={props:{layout:{type:String,default:"list"}}},et={props:{maxlength:Number}},it={props:{minlength:Number}},st={props:{name:[Number,String]}},nt={props:{options:{default:()=>[],type:Array}}},ot={props:{pattern:String}},at={props:{placeholder:[Number,String]}},rt={props:{required:Boolean}},lt={props:{spellcheck:{type:Boolean,default:!0}}};function ct(t,e,i,s,n,o,a,r){var l="function"==typeof t?t.options:t;return e&&(l.render=e,l.staticRenderFns=i,l._compiled=!0),{exports:t,options:l}}const ut={mixins:[tt],inheritAttrs:!1,props:{columns:{type:[Object,Array],default:()=>({})},fields:{type:Object,default:()=>({})},items:{type:Array,default:()=>[]},link:{type:Boolean,default:!0},sortable:Boolean,size:{type:String,default:"medium"},theme:String}};const dt=ct({mixins:[ut],props:{image:{type:[Object,Boolean],default:()=>({})}},emits:["change","hover","item","option","sort"],computed:{dragOptions(){return{sort:this.sortable,disabled:!1===this.sortable,draggable:".k-draggable-item"}},table(){return{columns:this.columns,fields:this.fields,rows:this.items,sortable:this.sortable}}},methods:{onDragStart(t,e){this.$panel.drag.start("text",e)},onOption(t,e,i){this.$emit("option",t,e,i)},imageOptions(t){let e=this.image,i=t.image;return!1!==e&&!1!==i&&("object"!=typeof e&&(e={}),"object"!=typeof i&&(i={}),{...i,...e})}}},(function(){var t=this,e=t._self._c;return"table"===t.layout?e("k-table",t._b({on:{change:function(e){return t.$emit("change",e)},sort:function(e){return t.$emit("sort",e)},option:t.onOption},scopedSlots:t._u([t.$scopedSlots.options?{key:"options",fn:function({row:e,rowIndex:i}){return[t._t("options",null,null,{item:e,index:i})]}}:null],null,!0)},"k-table",t.table,!1)):e("k-draggable",{staticClass:"k-items",class:"k-"+t.layout+"-items",attrs:{"data-layout":t.layout,"data-size":t.size,handle:!0,list:t.items,options:t.dragOptions},on:{change:function(e){return t.$emit("change",e)},end:function(e){return t.$emit("sort",t.items,e)}}},[t._l(t.items,(function(i,s){return[t._t("default",(function(){return[e("k-item",t._b({key:i.id??s,class:{"k-draggable-item":t.sortable&&i.sortable},attrs:{image:t.imageOptions(i),layout:t.layout,link:!!t.link&&i.link,sortable:t.sortable&&i.sortable,theme:i.theme??t.theme,width:i.column},on:{click:function(e){return t.$emit("item",i,s)},drag:function(e){return t.onDragStart(e,i.dragText)},option:function(e){return t.onOption(e,i,s)}},nativeOn:{mouseover:function(e){return t.$emit("hover",e,i,s)}},scopedSlots:t._u([{key:"options",fn:function(){return[t._t("options",null,null,{item:i,index:s})]},proxy:!0}],null,!0)},"k-item",i,!1))]}),null,{item:i,itemIndex:s})]}))],2)}),[]).exports;const pt=ct({mixins:[ut],props:{empty:{type:Object,default:()=>({})},help:String,pagination:{type:[Boolean,Object],default:!1}},emits:["action","change","empty","item","option","paginate","sort"],computed:{hasPagination(){return!1!==this.pagination&&(!0!==this.paginationOptions.hide&&!(this.pagination.total<=this.pagination.limit))},paginationOptions(){return{limit:10,details:!0,keys:!1,total:0,hide:!1,..."object"!=typeof this.pagination?{}:this.pagination}}},watch:{$props(){this.$forceUpdate()}},methods:{onEmpty(t){t.stopPropagation(),this.$emit("empty")},onOption(...t){this.$emit("action",...t),this.$emit("option",...t)}}},(function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-collection"},[0===t.items.length?e("k-empty",t._g(t._b({attrs:{layout:t.layout}},"k-empty",t.empty,!1),t.$listeners.empty?{click:t.onEmpty}:{})):e("k-items",t._b({on:{change:function(e){return t.$emit("change",e)},item:function(e){return t.$emit("item",e)},option:t.onOption,sort:function(e){return t.$emit("sort",e)}},scopedSlots:t._u([{key:"options",fn:function({item:e,index:i}){return[t._t("options",null,null,{item:e,index:i})]}}],null,!0)},"k-items",{columns:t.columns,fields:t.fields,items:t.items,layout:t.layout,link:t.link,size:t.size,sortable:t.sortable,theme:t.theme},!1)),t.help||t.hasPagination?e("footer",{staticClass:"k-collection-footer"},[e("k-text",{staticClass:"k-help k-collection-help",attrs:{html:t.help}}),t.hasPagination?e("k-pagination",t._b({on:{paginate:function(e){return t.$emit("paginate",e)}}},"k-pagination",t.paginationOptions,!1)):t._e()],1):t._e()],1)}),[]).exports;const ht=ct({mixins:[tt],props:{text:String,icon:String},emits:["click"],computed:{attrs(){const t={button:void 0!==this.$listeners.click,icon:this.icon,theme:"empty"};return"cardlets"!==this.layout&&"cards"!==this.layout||(t.align="center",t.height="var(--item-height-cardlet)"),t}}},(function(){var t=this;return(0,t._self._c)("k-box",t._b({staticClass:"k-empty",nativeOn:{click:function(e){return t.$emit("click",e)}}},"k-box",t.attrs,!1),[t._t("default",(function(){return[t._v(" "+t._s(t.text)+" ")]}))],2)}),[]).exports,mt={mixins:[tt],props:{image:[Object,Boolean],width:{type:String,default:"1/1"}}};const ft=ct({mixins:[mt],inheritAttrs:!1,computed:{attrs(){return{back:this.image.back??"gray-500",cover:!0,...this.image,ratio:"list"===this.layout?"auto":this.image.ratio,size:this.sizes}},sizes(){switch(this.width){case"1/2":case"2/4":return"(min-width: 30em) and (max-width: 65em) 59em, (min-width: 65em) 44em, 27em";case"1/3":return"(min-width: 30em) and (max-width: 65em) 59em, (min-width: 65em) 29.333em, 27em";case"1/4":return"(min-width: 30em) and (max-width: 65em) 59em, (min-width: 65em) 22em, 27em";case"2/3":return"(min-width: 30em) and (max-width: 65em) 59em, (min-width: 65em) 27em, 27em";case"3/4":return"(min-width: 30em) and (max-width: 65em) 59em, (min-width: 65em) 66em, 27em";default:return"(min-width: 30em) and (max-width: 65em) 59em, (min-width: 65em) 88em, 27em"}}}},(function(){var t=this,e=t._self._c;return t.image.src?e("k-image-frame",t._b({staticClass:"k-item-image"},"k-image-frame",t.attrs,!1)):e("k-icon-frame",t._b({staticClass:"k-item-image"},"k-icon-frame",t.attrs,!1))}),[]).exports;const gt=ct({mixins:[mt,tt],inheritAttrs:!1,props:{buttons:{type:Array,default:()=>[]},data:Object,info:String,link:{type:[Boolean,String,Function]},options:{type:[Array,Function,String]},sortable:Boolean,target:String,text:String,theme:String},emits:["action","click","drag","option"],computed:{hasFigure(){return!1!==this.image&&this.$helper.object.length(this.image)>0},title(){return this.$helper.string.stripHTML(this.$helper.string.unescapeHTML(this.text)).trim()}},methods:{onOption(t){this.$emit("action",t),this.$emit("option",t)}}},(function(){var t,e=this,i=e._self._c;return i("div",e._b({staticClass:"k-item",class:!!e.layout&&"k-"+e.layout+"-item",attrs:{"data-has-image":e.hasFigure,"data-layout":e.layout,"data-theme":e.theme},on:{click:function(t){return e.$emit("click",t)},dragstart:function(t){return e.$emit("drag",t)}}},"div",e.data,!1),[e._t("image",(function(){return[e.hasFigure?i("k-item-image",{attrs:{image:e.image,layout:e.layout,width:e.width}}):e._e()]})),e.sortable?i("k-sort-handle",{staticClass:"k-item-sort-handle",attrs:{tabindex:"-1"}}):e._e(),i("div",{staticClass:"k-item-content"},[i("h3",{staticClass:"k-item-title",attrs:{title:e.title}},[!1!==e.link?i("k-link",{attrs:{target:e.target,to:e.link}},[i("span",{domProps:{innerHTML:e._s(e.text??"–")}})]):i("span",{domProps:{innerHTML:e._s(e.text??"–")}})],1),e.info?i("p",{staticClass:"k-item-info",domProps:{innerHTML:e._s(e.info)}}):e._e()]),i("div",{staticClass:"k-item-options",attrs:{"data-only-option":!(null==(t=e.buttons)?void 0:t.length)||!e.options&&!e.$slots.options}},[e._l(e.buttons,(function(t,s){return i("k-button",e._b({key:"button-"+s},"k-button",t,!1))})),e._t("options",(function(){return[e.options?i("k-options-dropdown",{staticClass:"k-item-options-dropdown",attrs:{options:e.options},on:{option:e.onOption}}):e._e()]}))],2)],2)}),[]).exports,kt={install(t){t.component("k-collection",pt),t.component("k-empty",ht),t.component("k-item",gt),t.component("k-item-image",ft),t.component("k-items",dt)}};const bt=ct({},(function(){return(0,this._self._c)("div",{staticClass:"k-dialog-body"},[this._t("default")],2)}),[]).exports;function vt(t){return"object"==typeof t&&(null==t?void 0:t.constructor)===Object}function yt(t){return Object.keys(t??{}).length}function $t(t){return Object.keys(t).reduce(((e,i)=>(e[i.toLowerCase()]=t[i],e)),{})}const wt={clone:function(t){if(void 0!==t)return structuredClone(t)},isEmpty:function(t){return null==t||""===t||(!(!vt(t)||0!==yt(t))||0===t.length)},isObject:vt,length:yt,merge:function t(e,i={}){for(const s in i)i[s]instanceof Object&&Object.assign(i[s],t(e[s]??{},i[s]));return Object.assign(e??{},i),e},same:function(t,e){return JSON.stringify(t)===JSON.stringify(e)},toLowerKeys:$t},xt={props:{cancelButton:{default:!0,type:[Boolean,String,Object]},disabled:{default:!1,type:Boolean},icon:{default:"check",type:String},submitButton:{type:[Boolean,String,Object],default:!0},theme:{default:"positive",type:String}}};const _t=ct({mixins:[xt],emits:["cancel"],computed:{cancel(){return this.button(this.cancelButton,{click:()=>this.$emit("cancel"),class:"k-dialog-button-cancel",icon:"cancel",text:this.$t("cancel"),variant:"filled"})},submit(){return this.button(this.submitButton,{class:"k-dialog-button-submit",disabled:this.disabled||this.$panel.dialog.isLoading,icon:this.icon,text:this.$t("confirm"),theme:this.theme,type:"submit",variant:"filled"})}},methods:{button:(t,e)=>"string"==typeof t?{...e,text:t}:!1!==t&&(!1===vt(t)?e:{...e,...t})}},(function(){var t=this,e=t._self._c;return e("k-button-group",{staticClass:"k-dialog-buttons"},[t.cancel?e("k-button",t._b({},"k-button",t.cancel,!1)):t._e(),t.submit?e("k-button",t._b({attrs:{icon:t.$panel.dialog.isLoading?"loader":t.submit.icon}},"k-button",t.submit,!1)):t._e()],1)}),[]).exports,Ct={props:{empty:{default:()=>window.panel.$t("dialog.fields.empty"),type:String},fields:{default:()=>[],type:[Array,Object]},novalidate:{default:!0,type:Boolean},value:{default:()=>({}),type:Object}}};const St=ct({mixins:[Ct],emits:["input","submit"],computed:{hasFields(){return this.$helper.object.length(this.fields)>0}}},(function(){var t=this,e=t._self._c;return t.hasFields?e("k-fieldset",{staticClass:"k-dialog-fields",attrs:{novalidate:t.novalidate,fields:t.fields,value:t.value},on:{input:function(e){return t.$emit("input",e)},submit:function(e){return t.$emit("submit",e)}}}):e("k-box",{attrs:{theme:"info"}},[t._v(t._s(t.empty))])}),[]).exports;const Ot=ct({},(function(){return(0,this._self._c)("footer",{staticClass:"k-dialog-footer"},[this._t("default")],2)}),[]).exports;const Mt=ct({},(function(){var t=this,e=t._self._c;return"dialog"===t.$panel.notification.context?e("k-notification",{staticClass:"k-dialog-notification"}):t._e()}),[]).exports;const At=ct({props:{autofocus:{default:!0,type:Boolean},placeholder:{type:String},value:{type:String}},emits:["search"]},(function(){var t=this;return(0,t._self._c)("k-input",{staticClass:"k-dialog-search",attrs:{autofocus:t.autofocus,placeholder:t.placeholder,value:t.value,icon:"search",type:"search"},on:{input:function(e){return t.$emit("search",e)}}})}),[]).exports,It={props:{empty:{type:String,default:()=>window.panel.$t("dialog.text.empty")},text:{type:String}}};const Dt=ct({mixins:[It]},(function(){var t=this,e=t._self._c;return t.text?e("k-text",{attrs:{html:t.text}}):e("k-box",{attrs:{theme:"info"}},[t._v(t._s(t.empty))])}),[]).exports,jt={install(t){t.component("k-dialog-body",bt),t.component("k-dialog-buttons",_t),t.component("k-dialog-fields",St),t.component("k-dialog-footer",Ot),t.component("k-dialog-notification",Mt),t.component("k-dialog-search",At),t.component("k-dialog-text",Dt)}},Et={mixins:[xt],props:{size:{default:"default",type:String},visible:{default:!1,type:Boolean}},emits:["cancel","close","input","submit","success"],methods:{cancel(){this.$emit("cancel")},close(){this.$emit("close")},error(t){this.$panel.notification.error(t)},focus(t){this.$panel.dialog.focus(t)},input(t){this.$emit("input",t)},open(){this.$panel.dialog.open(this)},submit(){this.$emit("submit",this.value)},success(t){this.$emit("success",t)}}};const Lt=ct({mixins:[Et]},(function(){var t=this,e=t._self._c;return t.visible?e("portal",{attrs:{to:"dialog"}},[e("form",{staticClass:"k-dialog",class:t.$vnode.data.staticClass,attrs:{"data-has-footer":t.cancelButton||t.submitButton,"data-size":t.size,method:"dialog"},on:{click:function(t){t.stopPropagation()},submit:function(e){return e.preventDefault(),t.$emit("submit")}}},[t._t("header",(function(){return[e("k-dialog-notification")]})),t.$slots.default?e("k-dialog-body",[t._t("default")],2):t._e(),t._t("footer",(function(){return[t.cancelButton||t.submitButton?e("k-dialog-footer",[e("k-dialog-buttons",{attrs:{"cancel-button":t.cancelButton,disabled:t.disabled,icon:t.icon,"submit-button":t.submitButton,theme:t.theme},on:{cancel:function(e){return t.$emit("cancel")}}})],1):t._e()]}))],2)]):t._e()}),[]).exports;const Tt=ct({mixins:[Et],props:{cancelButton:{default:!1},changes:{type:Array},loading:{type:Boolean},size:{default:"medium"},submitButton:{default:!1}},computed:{ids(){return Object.keys(this.store).filter((t=>{var e;return this.$helper.object.length(null==(e=this.store[t])?void 0:e.changes)>0}))},store(){return this.$store.state.content.models}},watch:{ids:{handler(t){this.$panel.dialog.refresh({method:"POST",body:{ids:t}})},immediate:!0}}},(function(){var t=this,e=t._self._c;return e("k-dialog",t._b({staticClass:"k-changes-dialog"},"k-dialog",t.$props,!1),[!1===t.loading?[e("k-headline",[t._v(t._s(t.$t("lock.unsaved")))]),t.changes.length?e("k-items",{attrs:{items:t.changes,layout:"list"}}):e("k-empty",{attrs:{icon:"edit-line"}},[t._v(t._s(t.$t("lock.unsaved.empty")))])]:[e("k-icon",{attrs:{type:"loader"}})]],2)}),[]).exports;const Bt=ct({mixins:[Et,Ct],props:{fields:{default:()=>({href:{label:window.panel.$t("email"),type:"email",icon:"email"},title:{label:window.panel.$t("title"),type:"text",icon:"title"}})},size:{default:"medium"},submitButton:{default:()=>window.panel.$t("insert")}},data(){return{values:{href:"",title:null,...this.value}}},methods:{submit(){this.$emit("submit",this.values)}}},(function(){var t=this;return(0,t._self._c)("k-form-dialog",t._b({attrs:{value:t.values},on:{cancel:function(e){return t.$emit("cancel")},input:function(e){t.values=e},submit:t.submit}},"k-form-dialog",t.$props,!1))}),[]).exports;const qt=ct({mixins:[Et],props:{details:[Object,Array],message:String,size:{default:"medium",type:String}},emits:["cancel"],computed:{detailsList(){return Array.fromObject(this.details)}}},(function(){var t=this,e=t._self._c;return e("k-dialog",{ref:"dialog",staticClass:"k-error-dialog",attrs:{"cancel-button":!1,"submit-button":!1,size:t.size,visible:t.visible},on:{cancel:function(e){return t.$emit("cancel")}}},[e("k-text",[t._v(t._s(t.message))]),t.detailsList.length?e("dl",{staticClass:"k-error-details"},[t._l(t.detailsList,(function(i,s){return[e("dt",{key:"detail-label-"+s},[t._v(" "+t._s(i.label)+" ")]),e("dd",{key:"detail-message-"+s},["object"==typeof i.message?[e("ul",t._l(i.message,(function(i,s){return e("li",{key:s},[t._v(" "+t._s(i)+" ")])})),0)]:[t._v(" "+t._s(i.message)+" ")]],2)]}))],2):t._e()],1)}),[]).exports;const Pt=ct({},(function(){var t=this;return(0,t._self._c)(t.$panel.dialog.component,t._g(t._b({key:t.$panel.dialog.timestamp,tag:"component",attrs:{visible:!0}},"component",t.$panel.dialog.props,!1),t.$panel.dialog.listeners()))}),[]).exports,Nt=(t,e)=>{let i=null;return(...s)=>{clearTimeout(i),i=setTimeout((()=>t.apply(void 0,s)),e)}},zt={props:{delay:{default:200,type:Number},hasSearch:{default:!0,type:Boolean}},data:()=>({query:""}),watch:{query(){!1!==this.hasSearch&&this.search()}},created(){this.search=Nt(this.search,this.delay)},methods:{async search(){console.warn("Search mixin: Please implement a `search` method.")}}},Ft={props:{endpoint:String,empty:Object,fetchParams:Object,item:{type:Function,default:t=>t},max:Number,multiple:{type:Boolean,default:!0},size:{type:String,default:"medium"},value:{type:Array,default:()=>[]}}};const Yt=ct({mixins:[Et,zt,Ft],emits:["cancel","fetched","submit"],data(){return{models:[],selected:this.value.reduce(((t,e)=>({...t,[e]:{id:e}})),{}),pagination:{limit:20,page:1,total:0}}},computed:{items(){return this.models.map(this.item)}},watch:{fetchParams(t,e){!1===this.$helper.object.same(t,e)&&(this.pagination.page=1,this.fetch())}},mounted(){this.fetch()},methods:{async fetch(){const t={page:this.pagination.page,search:this.query,...this.fetchParams};try{this.$panel.dialog.isLoading=!0;const e=await this.$api.get(this.endpoint,t);this.models=e.data,this.pagination=e.pagination,this.$emit("fetched",e)}catch(e){this.$panel.error(e),this.models=[]}finally{this.$panel.dialog.isLoading=!1}},isSelected(t){return void 0!==this.selected[t.id]},paginate(t){this.pagination.page=t.page,this.pagination.limit=t.limit,this.fetch()},submit(){this.$emit("submit",Object.values(this.selected))},async search(){this.pagination.page=0,await this.fetch()},toggle(t){if(!1!==this.multiple&&1!==this.max||(this.selected={}),this.isSelected(t))return Vue.del(this.selected,t.id);this.max&&this.max<=this.$helper.object.length(this.selected)||Vue.set(this.selected,t.id,t)}}},(function(){var t=this,e=t._self._c;return e("k-dialog",t._b({staticClass:"k-models-dialog",on:{cancel:function(e){return t.$emit("cancel")},submit:t.submit}},"k-dialog",t.$props,!1),[t._t("header"),t.hasSearch?e("k-dialog-search",{attrs:{value:t.query},on:{search:function(e){t.query=e}}}):t._e(),e("k-collection",{attrs:{empty:{...t.empty,text:t.$panel.dialog.isLoading?t.$t("loading"):t.empty.text},items:t.items,link:!1,pagination:{details:!0,dropdown:!1,align:"center",...t.pagination},sortable:!1,layout:"list"},on:{item:t.toggle,paginate:t.paginate},scopedSlots:t._u([{key:"options",fn:function({item:i}){return[e("k-choice-input",{attrs:{checked:t.isSelected(i),type:t.multiple&&1!==t.max?"checkbox":"radio",title:t.isSelected(i)?t.$t("remove"):t.$t("select")},on:{click:function(e){return e.stopPropagation(),t.toggle(i)}}}),t._t("options",null,null,{item:i})]}}],null,!0)})],2)}),[]).exports;const Rt=ct({mixins:[Et,Ft],props:{empty:{type:Object,default:()=>({icon:"image",text:window.panel.$t("dialog.files.empty")})}}},(function(){var t=this;return(0,t._self._c)("k-models-dialog",t._b({on:{cancel:function(e){return t.$emit("cancel")},submit:function(e){return t.$emit("submit",e)}}},"k-models-dialog",t.$props,!1))}),[]).exports;const Ut=ct({mixins:[Et,Ct],props:{size:{default:"medium"},submitButton:{default:()=>window.panel.$t("save")},text:{type:String}},emits:["cancel","input","submit"]},(function(){var t=this,e=t._self._c;return e("k-dialog",t._b({ref:"dialog",on:{cancel:function(e){return t.$emit("cancel")},submit:function(e){return t.$emit("submit",t.value)}}},"k-dialog",t.$props,!1),[t._t("default",(function(){return[t.text?e("k-dialog-text",{attrs:{text:t.text}}):t._e(),e("k-dialog-fields",{attrs:{fields:t.fields,novalidate:t.novalidate,value:t.value},on:{input:function(e){return t.$emit("input",e)},submit:function(e){return t.$emit("submit",e)}}})]}))],2)}),[]).exports;const Ht=ct({extends:Ut,watch:{"value.name"(t){this.fields.code.disabled||this.onNameChanges(t)},"value.code"(t){this.fields.code.disabled||(this.value.code=this.$helper.slug(t,[this.$panel.system.ascii]),this.onCodeChanges(this.value.code))}},methods:{onCodeChanges(t){if(!t)return this.value.locale=null;if(t.length>=2)if(-1!==t.indexOf("-")){let e=t.split("-"),i=[e[0],e[1].toUpperCase()];this.value.locale=i.join("_")}else{let e=this.$panel.system.locales??[];this.value.locale=null==e?void 0:e[t]}},onNameChanges(t){this.value.code=this.$helper.slug(t,[this.value.rules,this.$panel.system.ascii]).substr(0,2)}}},null,null).exports;const Vt=ct({mixins:[{mixins:[Et],props:{license:Object,size:{default:"large"}}}]},(function(){var t=this,e=t._self._c;return e("k-dialog",t._b({ref:"dialog",staticClass:"k-license-dialog",on:{cancel:function(e){return t.$emit("cancel")},submit:function(e){return t.$emit("submit")}}},"k-dialog",t.$props,!1),[e("k-bar",{staticStyle:{"margin-bottom":"var(--spacing-2)"}},[e("h2",{staticClass:"k-headline"},[t._v(" "+t._s(t.$t("license"))+" ")])]),e("div",{staticClass:"k-table"},[e("table",{staticStyle:{"table-layout":"auto"}},[e("tbody",[e("tr",[e("th",{attrs:{"data-mobile":"true"}},[t._v(t._s(t.$t("type")))]),e("td",{attrs:{"data-mobile":"true"}},[t._v(t._s(t.license.type))])]),t.license.code?e("tr",[e("th",{attrs:{"data-mobile":"true"}},[t._v(t._s(t.$t("license.code")))]),e("td",{staticClass:"k-text",attrs:{"data-mobile":"true"}},[e("code",[t._v(t._s(t.license.code))])])]):t._e(),t.license.info?e("tr",[e("th",{attrs:{"data-mobile":"true"}},[t._v(t._s(t.$t("status")))]),e("td",{attrs:{"data-mobile":"true","data-theme":t.license.theme}},[e("p",{staticClass:"k-license-dialog-status"},[e("k-icon",{attrs:{type:t.license.icon}}),t._v(" "+t._s(t.license.info)+" ")],1)])]):t._e()])])])],1)}),[]).exports;const Kt=ct({mixins:[Et,Ct],props:{fields:{default:()=>({href:{label:window.panel.$t("link"),type:"link",placeholder:window.panel.$t("url.placeholder"),icon:"url"},title:{label:window.panel.$t("title"),type:"text",icon:"title"},target:{label:window.panel.$t("open.newWindow"),type:"toggle",text:[window.panel.$t("no"),window.panel.$t("yes")]}})},size:{default:"medium"},submitButton:{default:()=>window.panel.$t("insert")}},data(){return{values:{href:"",title:null,...this.value,target:Boolean(this.value.target??!1)}}},methods:{submit(){let t="/@/$1/";this.values.href.startsWith("page://")&&window.panel.language.code&&!1===window.panel.language.default&&(t="/"+window.panel.language.code+t);const e=this.values.href.replace(/(file|page):\/\//,t);this.$emit("submit",{...this.values,href:e,target:this.values.target?"_blank":null})}}},(function(){var t=this;return(0,t._self._c)("k-form-dialog",t._b({attrs:{value:t.values},on:{cancel:function(e){return t.$emit("cancel")},input:function(e){t.values=e},submit:t.submit}},"k-form-dialog",t.$props,!1))}),[]).exports;const Wt=ct({mixins:[Ut],props:{blueprints:{type:Array},size:{default:"medium",type:String},submitButton:{type:[String,Boolean],default:()=>window.panel.$t("save")},template:{type:String}},computed:{templates(){return this.blueprints.map((t=>({text:t.title,value:t.name})))}},methods:{pick(t){this.$panel.dialog.reload({query:{...this.$panel.dialog.query,slug:this.value.slug,template:t,title:this.value.title}})}}},(function(){var t=this,e=t._self._c;return e("k-form-dialog",t._b({ref:"dialog",staticClass:"k-page-create-dialog",on:{cancel:function(e){return t.$emit("cancel")},submit:function(e){return t.$emit("submit",t.value)}}},"k-form-dialog",t.$props,!1),[t.templates.length>1?e("k-select-field",{staticClass:"k-page-template-switch",attrs:{empty:!1,label:t.$t("template"),options:t.templates,required:!0,value:t.template},on:{input:function(e){return t.pick(e)}}}):t._e(),e("k-dialog-fields",{attrs:{fields:t.fields,novalidate:t.novalidate,value:t.value},on:{input:function(e){return t.$emit("input",e)},submit:function(e){return t.$emit("submit",e)}}})],1)}),[]).exports;const Jt=ct({mixins:[Et],props:{value:{default:()=>({}),type:Object}},emits:["cancel","input","submit"],methods:{select(t){this.$emit("input",{...this.value,parent:t.value})}}},(function(){var t=this,e=t._self._c;return e("k-dialog",t._b({ref:"dialog",staticClass:"k-page-move-dialog",attrs:{"submit-button":{icon:"parent",text:t.$t("move")},size:"medium"},on:{cancel:function(e){return t.$emit("cancel")},submit:function(e){return t.$emit("submit",t.value)}}},"k-dialog",t.$props,!1),[e("k-headline",[t._v(t._s(t.$t("page.move")))]),e("div",{staticClass:"k-page-move-parent",attrs:{tabindex:"0","data-autofocus":""}},[e("k-page-tree",{attrs:{current:t.value.parent,move:t.value.move,identifier:"id"},on:{select:t.select}})],1)],1)}),[]).exports;const Gt=ct({mixins:[Et,Ft],props:{empty:{type:Object,default:()=>({icon:"page",text:window.panel.$t("dialog.pages.empty")})}},data:()=>({model:null,parent:null})},(function(){var t=this,e=t._self._c;return e("k-models-dialog",t._b({attrs:{"fetch-params":{parent:t.parent}},on:{cancel:function(e){return t.$emit("cancel")},fetched:function(e){t.model=e.model},submit:function(e){return t.$emit("submit",e)}},scopedSlots:t._u([t.model?{key:"header",fn:function(){return[e("header",{staticClass:"k-pages-dialog-navbar"},[e("k-button",{attrs:{disabled:!t.model.id,title:t.$t("back"),icon:"angle-left"},on:{click:function(e){t.parent=t.model.parent}}}),e("k-headline",[t._v(t._s(t.model.title))])],1)]},proxy:!0}:null,t.model?{key:"options",fn:function({item:i}){return[e("k-button",{staticClass:"k-pages-dialog-option",attrs:{disabled:!i.hasChildren,title:t.$t("open"),icon:"angle-right"},on:{click:function(e){e.stopPropagation(),t.parent=i.id}}})]}}:null],null,!0)},"k-models-dialog",t.$props,!1))}),[]).exports;const Xt=ct({mixins:[{mixins:[Et,It]}]},(function(){var t=this,e=t._self._c;return e("k-dialog",t._b({ref:"dialog",on:{cancel:function(e){return t.$emit("cancel")},submit:function(e){return t.$emit("submit")}}},"k-dialog",t.$props,!1),[t._t("default",(function(){return[e("k-dialog-text",{attrs:{text:t.text}})]}))],2)}),[]).exports;const Zt=ct({mixins:[Xt],props:{icon:{default:"trash"},submitButton:{default:()=>window.panel.$t("delete")},theme:{default:"negative"}}},(function(){var t=this;return(0,t._self._c)("k-text-dialog",t._b({ref:"dialog",on:{cancel:function(e){return t.$emit("cancel")},submit:function(e){return t.$emit("submit")}}},"k-text-dialog",t.$props,!1),[t._t("default")],2)}),[]).exports;const Qt=ct({mixins:[Et],props:{type:String},emits:["cancel"],data:()=>({results:null,pagination:{}}),methods:{focus(){var t;null==(t=this.$refs.search)||t.focus()},navigate(t){t&&(this.$go(t.link),this.close())},async search({type:t,query:e}){const i=await this.$panel.search(t,e);i&&(this.results=i.results,this.pagination=i.pagination)}}},(function(){var t=this,e=t._self._c;return e("k-dialog",{staticClass:"k-search-dialog",attrs:{"cancel-button":!1,"submit-button":!1,visible:!0,role:"search",size:"medium"},on:{cancel:function(e){return t.$emit("cancel")},submit:t.submit}},[e("k-search-bar",{ref:"search",attrs:{"default-type":t.type??t.$panel.view.search,"is-loading":t.$panel.searcher.isLoading,pagination:t.pagination,results:t.results,types:t.$panel.searches},on:{close:t.close,more:function(e){return t.$go("search",{query:e})},navigate:t.navigate,search:t.search}})],1)}),[]).exports;const te=ct({mixins:[{mixins:[Et,Ct]}],props:{fields:null,qr:{type:String,required:!0},size:{default:"large"},submitButton:{default:()=>({text:window.panel.$t("activate"),icon:"lock",theme:"notice"})}},emits:["cancel","input","submit"]},(function(){var t=this,e=t._self._c;return e("k-dialog",t._b({ref:"dialog",on:{cancel:function(e){return t.$emit("cancel")},submit:function(e){return t.$emit("submit")}}},"k-dialog",t.$props,!1),[e("k-dialog-text",{staticClass:"k-totp-dialog-intro",attrs:{text:t.$t("login.totp.enable.intro")}}),e("div",{staticClass:"k-totp-dialog-grid"},[e("div",{staticClass:"k-totp-qrcode"},[e("k-info-field",{attrs:{label:t.$t("login.totp.enable.qr.label"),text:t.qr,help:t.$t("login.totp.enable.qr.help",{secret:t.value.secret}),theme:"passive"}})],1),e("k-dialog-fields",{staticClass:"k-totp-dialog-fields",attrs:{fields:{info:{label:t.$t("login.totp.enable.confirm.headline"),type:"info",text:t.$t("login.totp.enable.confirm.text"),theme:"none"},confirm:{label:t.$t("login.totp.enable.confirm.label"),type:"text",counter:!1,font:"monospace",required:!0,placeholder:t.$t("login.code.placeholder.totp"),help:t.$t("login.totp.enable.confirm.help")},secret:{type:"hidden"}},novalidate:!0,value:t.value},on:{input:function(e){return t.$emit("input",e)},submit:function(e){return t.$emit("submit",e)}}})],1)],1)}),[]).exports;const ee=ct({mixins:[Et],props:{submitButton:{type:[String,Boolean,Object],default:()=>({icon:"upload",text:window.panel.$t("upload")})}}},(function(){var t=this,e=t._self._c;return e("k-dialog",t._b({ref:"dialog",staticClass:"k-upload-dialog",attrs:{disabled:t.disabled||0===t.$panel.upload.files.length},on:{cancel:function(e){return t.$emit("cancel")},submit:function(e){return t.$emit("submit")}}},"k-dialog",t.$props,!1),[e("k-dropzone",{on:{drop:function(e){return t.$panel.upload.select(e)}}},[0===t.$panel.upload.files.length?e("k-empty",{attrs:{icon:"upload",layout:"cards"},on:{click:function(e){return t.$panel.upload.pick()}}},[t._v(" "+t._s(t.$t("files.empty"))+" ")]):e("k-upload-items",{attrs:{items:t.$panel.upload.files},on:{remove:e=>{t.$panel.upload.remove(e.id)},rename:(t,e)=>{t.name=e}}})],1)],1)}),[]).exports;const ie=ct({extends:ee,props:{original:Object,submitButton:{type:[String,Boolean,Object],default:()=>({icon:"upload",text:window.panel.$t("replace")})}},computed:{file(){return this.$panel.upload.files[0]}}},(function(){var t,e,i,s,n=this,o=n._self._c;return o("k-dialog",n._b({ref:"dialog",staticClass:"k-upload-dialog k-upload-replace-dialog",on:{cancel:function(t){return n.$emit("cancel")},submit:function(t){return n.$emit("submit")}}},"k-dialog",n.$props,!1),[o("ul",{staticClass:"k-upload-items"},[o("li",{staticClass:"k-upload-original"},[o("k-upload-item-preview",{attrs:{color:null==(t=n.original.image)?void 0:t.color,icon:null==(e=n.original.image)?void 0:e.icon,url:n.original.url,type:n.original.mime}})],1),o("li",[n._v("←")]),o("k-upload-item",n._b({attrs:{color:null==(i=n.original.image)?void 0:i.color,editable:!1,icon:null==(s=n.original.image)?void 0:s.icon,name:n.$helper.file.name(n.original.filename),removable:!1}},"k-upload-item",n.file,!1))],1)])}),[]).exports;const se=ct({mixins:[Et,Ft],props:{empty:{type:Object,default:()=>({icon:"users",text:window.panel.$t("dialog.users.empty")})},item:{type:Function,default:t=>({...t,key:t.email,info:t.info!==t.text?t.info:null})}}},(function(){var t=this;return(0,t._self._c)("k-models-dialog",t._b({on:{cancel:function(e){return t.$emit("cancel")},submit:function(e){return t.$emit("submit",e)}}},"k-models-dialog",t.$props,!1))}),[]).exports,ne={install(t){t.use(jt),t.component("k-dialog",Lt),t.component("k-changes-dialog",Tt),t.component("k-email-dialog",Bt),t.component("k-error-dialog",qt),t.component("k-fiber-dialog",Pt),t.component("k-files-dialog",Rt),t.component("k-form-dialog",Ut),t.component("k-license-dialog",Vt),t.component("k-link-dialog",Kt),t.component("k-language-dialog",Ht),t.component("k-models-dialog",Yt),t.component("k-page-create-dialog",Wt),t.component("k-page-move-dialog",Jt),t.component("k-pages-dialog",Gt),t.component("k-remove-dialog",Zt),t.component("k-search-dialog",Qt),t.component("k-text-dialog",Xt),t.component("k-totp-dialog",te),t.component("k-upload-dialog",ee),t.component("k-upload-replace-dialog",ie),t.component("k-users-dialog",se)}};const oe=ct({},(function(){return(0,this._self._c)("div",{staticClass:"k-drawer-body scroll-y-auto"},[this._t("default")],2)}),[]).exports,ae={props:{empty:{type:String,default:()=>window.panel.$t("drawer.fields.empty")},fields:Object,novalidate:{type:Boolean,default:!0},value:Object}};const re=ct({mixins:[ae],emits:["input","submit"],computed:{hasFields(){return this.$helper.object.length(this.fields)>0}}},(function(){var t=this,e=t._self._c;return t.hasFields?e("k-fieldset",{staticClass:"k-drawer-fields",attrs:{novalidate:t.novalidate,fields:t.fields,value:t.value},on:{input:function(e){return t.$emit("input",e)},submit:function(e){return t.$emit("submit",e)}}}):e("k-box",{attrs:{theme:"info"}},[t._v(t._s(t.empty))])}),[]).exports,le={props:{breadcrumb:{default:()=>[],type:Array},tab:{type:String},tabs:{default:()=>({}),type:Object}}};const ce=ct({mixins:[le],emits:["crumb","tab"]},(function(){var t=this,e=t._self._c;return e("header",{staticClass:"k-drawer-header"},[e("nav",{staticClass:"k-breadcrumb k-drawer-breadcrumb"},[e("ol",t._l(t.breadcrumb,(function(i,s){return e("li",{key:i.id},[e("k-button",{staticClass:"k-breadcrumb-link",attrs:{icon:i.props.icon,text:i.props.title,current:s===t.breadcrumb.length-1,variant:"dimmed"},on:{click:function(e){return t.$emit("crumb",i.id)}}})],1)})),0)]),e("k-drawer-tabs",{attrs:{tab:t.tab,tabs:t.tabs},on:{open:function(e){return t.$emit("tab",e)}}}),e("nav",{staticClass:"k-drawer-options"},[t._t("default"),e("k-button",{staticClass:"k-drawer-option",attrs:{icon:"check",type:"submit"}})],2)],1)}),[]).exports;const ue=ct({},(function(){var t=this,e=t._self._c;return"drawer"===t.$panel.notification.context?e("k-notification",{staticClass:"k-drawer-notification"}):t._e()}),[]).exports;const de=ct({mixins:[{props:{tab:{type:String},tabs:{default:()=>({}),type:[Array,Object]}}}],emits:["open"],computed:{hasTabs(){return this.$helper.object.length(this.tabs)>1}}},(function(){var t=this,e=t._self._c;return t.hasTabs?e("nav",{staticClass:"k-drawer-tabs"},t._l(t.tabs,(function(i){return e("k-button",{key:i.name,staticClass:"k-drawer-tab",attrs:{current:t.tab===i.name,text:i.label},on:{click:function(e){return t.$emit("open",i.name)}}})})),1):t._e()}),[]).exports,pe={props:{empty:{type:String,default:()=>window.panel.$t("drawer.text.empty")},text:{type:String}}};const he=ct({mixins:[pe]},(function(){var t=this,e=t._self._c;return t.text?e("k-text",{attrs:{html:t.text}}):e("k-box",{attrs:{theme:"info"}},[t._v(t._s(t.empty))])}),[]).exports,me={install(t){t.component("k-drawer-body",oe),t.component("k-drawer-fields",re),t.component("k-drawer-header",ce),t.component("k-drawer-notification",ue),t.component("k-drawer-tabs",de),t.component("k-drawer-text",he)}},fe={mixins:[le],props:{disabled:{default:!1,type:Boolean},icon:String,id:String,options:{type:Array},title:String,visible:{default:!1,type:Boolean}}};const ge=ct({mixins:[fe],emits:["cancel","crumb","submit","tab"]},(function(){var t=this,e=t._self._c;return t.visible?e("portal",{attrs:{to:"drawer"}},[e("form",{staticClass:"k-drawer",class:t.$vnode.data.staticClass,attrs:{"aria-disabled":t.disabled,method:"dialog"},on:{submit:function(e){return e.preventDefault(),t.$emit("submit")}}},[e("k-drawer-notification"),e("k-drawer-header",{attrs:{breadcrumb:t.breadcrumb,tab:t.tab,tabs:t.tabs},on:{crumb:function(e){return t.$emit("crumb",e)},tab:function(e){return t.$emit("tab",e)}}},[t._t("options",(function(){return[t._l(t.options,(function(i,s){return[i.dropdown?[e("k-button",t._b({key:"btn-"+s,staticClass:"k-drawer-option",on:{click:function(e){t.$refs["dropdown-"+s][0].toggle()}}},"k-button",i,!1)),e("k-dropdown-content",{key:"dropdown-"+s,ref:"dropdown-"+s,refInFor:!0,attrs:{options:i.dropdown,"align-x":"end",theme:"light"}})]:e("k-button",t._b({key:s,staticClass:"k-drawer-option"},"k-button",i,!1))]}))]}))],2),e("k-drawer-body",[t._t("default")],2)],1)]):t._e()}),[]).exports,ke={props:{hidden:{type:Boolean},next:{type:Object},prev:{type:Object}}};const be=ct({mixins:[fe,ae,ke],emits:["cancel","crumb","input","next","prev","remove","show","submit","tab"]},(function(){var t=this,e=t._self._c;return e("k-form-drawer",t._b({ref:"drawer",staticClass:"k-block-drawer",on:{cancel:function(e){return t.$emit("cancel",e)},crumb:function(e){return t.$emit("crumb",e)},input:function(e){return t.$emit("input",e)},submit:function(e){return t.$emit("submit",e)},tab:function(e){return t.$emit("tab",e)}},scopedSlots:t._u([{key:"options",fn:function(){return[t.hidden?e("k-button",{staticClass:"k-drawer-option",attrs:{icon:"hidden"},on:{click:function(e){return t.$emit("show")}}}):t._e(),e("k-button",{staticClass:"k-drawer-option",attrs:{disabled:!t.prev,icon:"angle-left"},on:{click:function(e){return t.$emit("prev")}}}),e("k-button",{staticClass:"k-drawer-option",attrs:{disabled:!t.next,icon:"angle-right"},on:{click:function(e){return t.$emit("next")}}}),e("k-button",{staticClass:"k-drawer-option",attrs:{icon:"trash"},on:{click:function(e){return t.$emit("remove")}}})]},proxy:!0}])},"k-form-drawer",t.$props,!1))}),[]).exports;const ve=ct({methods:{isCurrent(t){return this.$panel.drawer.id===t}}},(function(){var t=this,e=t._self._c;return e("div",t._l(t.$panel.drawer.history.milestones,(function(i){return e(i.component,t._g(t._b({key:i.id,tag:"component",attrs:{breadcrumb:t.$panel.drawer.breadcrumb,disabled:!1===t.isCurrent(i.id),visible:!0}},"component",t.isCurrent(i.id)?t.$panel.drawer.props:i.props,!1),t.isCurrent(i.id)?t.$panel.drawer.listeners():i.on))})),1)}),[]).exports;const ye=ct({mixins:[fe,ae],emits:["cancel","crumb","input","submit","tab"]},(function(){var t=this,e=t._self._c;return e("k-drawer",t._b({ref:"drawer",staticClass:"k-form-drawer",on:{cancel:function(e){return t.$emit("cancel")},crumb:function(e){return t.$emit("crumb",e)},submit:function(e){return t.$emit("submit",t.value)},tab:function(e){return t.$emit("tab",e)}}},"k-drawer",t.$props,!1),[t._t("options",null,{slot:"options"}),e("k-drawer-fields",{attrs:{fields:t.fields,value:t.value},on:{input:function(e){return t.$emit("input",e)},submit:function(e){return t.$emit("submit",e)}}})],2)}),[]).exports;const $e=ct({mixins:[fe,ae,{props:{next:{type:Object},prev:{type:Object}}}],emits:["cancel","crumb","input","next","prev","remove","submit","tab"]},(function(){var t=this,e=t._self._c;return e("k-form-drawer",t._b({ref:"drawer",staticClass:"k-structure-drawer",on:{cancel:function(e){return t.$emit("cancel",e)},crumb:function(e){return t.$emit("crumb",e)},input:function(e){return t.$emit("input",e)},submit:function(e){return t.$emit("submit",e)},tab:function(e){return t.$emit("tab",e)}},scopedSlots:t._u([{key:"options",fn:function(){return[e("k-button",{staticClass:"k-drawer-option",attrs:{disabled:!t.prev,icon:"angle-left"},on:{click:function(e){return t.$emit("prev")}}}),e("k-button",{staticClass:"k-drawer-option",attrs:{disabled:!t.next,icon:"angle-right"},on:{click:function(e){return t.$emit("next")}}}),e("k-button",{staticClass:"k-drawer-option",attrs:{icon:"trash"},on:{click:function(e){return t.$emit("remove")}}})]},proxy:!0}])},"k-form-drawer",t.$props,!1))}),[]).exports;const we=ct({mixins:[fe,pe],emits:["cancel","crumb","input","submit","tab"]},(function(){var t=this,e=t._self._c;return e("k-drawer",t._b({ref:"drawer",staticClass:"k-text-drawer",on:{cancel:function(e){return t.$emit("cancel")},crumb:function(e){return t.$emit("crumb",e)},submit:function(e){return t.$emit("submit",t.value)},tab:function(e){return t.$emit("tab",e)}}},"k-drawer",t.$props,!1),[t._t("options",null,{slot:"options"}),e("k-dialog-text",{attrs:{text:t.text}})],2)}),[]).exports,xe={install(t){t.use(me),t.component("k-drawer",ge),t.component("k-block-drawer",be),t.component("k-fiber-drawer",ve),t.component("k-form-drawer",ye),t.component("k-structure-drawer",$e),t.component("k-text-drawer",we)}};const _e=ct({mounted(){window.panel.deprecated(" will be removed in a future version. Since Kirby 4.0, you don't need it anymore as wrapper element. Use `` as standalone instead.")}},(function(){return(0,this._self._c)("span",{staticClass:"k-dropdown",on:{click:function(t){t.stopPropagation()}}},[this._t("default")],2)}),[]).exports;let Ce=null;const Se=ct({props:{align:{type:String},alignX:{type:String,default:"start"},alignY:{type:String,default:"bottom"},disabled:{type:Boolean,default:!1},navigate:{default:!0,type:Boolean},options:[Array,Function,String],theme:{type:String,default:"dark"}},emits:["action","close","open"],data(){return{axis:{x:this.alignX,y:this.alignY},position:{x:0,y:0},isOpen:!1,items:[],opener:null}},mounted(){this.align&&window.panel.deprecated(": `align` prop will be removed in a future version. Use the `alignX` prop instead.")},methods:{close(){var t;null==(t=this.$refs.dropdown)||t.close()},async fetchOptions(t){return this.options?"string"==typeof this.options?this.$dropdown(this.options)(t):"function"==typeof this.options?this.options(t):Array.isArray(this.options)?t(this.options):void 0:t(this.items)},focus(t=0){this.$refs.navigate.focus(t)},onClick(){this.close()},onClose(){this.resetPosition(),this.isOpen=Ce=!1,this.$emit("close"),window.removeEventListener("resize",this.setPosition)},async onOpen(){this.isOpen=!0;const t=window.scrollY;Ce=this,await this.$nextTick(),this.$el&&this.opener&&(window.addEventListener("resize",this.setPosition),await this.setPosition(),window.scrollTo(0,t),this.$emit("open"))},onOptionClick(t){return this.close(),"function"==typeof t.click?t.click.call(this):"string"==typeof t.click?this.$emit("action",t.click):void(t.click&&(t.click.name&&this.$emit(t.click.name,t.click.payload),t.click.global&&this.$events.emit(t.click.global,t.click.payload)))},open(t){var e,i;if(!0===this.disabled)return!1;Ce&&Ce!==this&&Ce.close(),this.opener=t??(null==(e=window.event)?void 0:e.target.closest("button"))??(null==(i=window.event)?void 0:i.target),this.fetchOptions((t=>{this.items=t,this.onOpen()}))},async setPosition(){this.axis={x:this.alignX??this.align,y:this.alignY},"right"===this.axis.x?this.axis.x="end":"left"===this.axis.x&&(this.axis.x="start"),"rtl"===this.$panel.direction&&("start"===this.axis.x?this.axis.x="end":"end"===this.axis.x&&(this.axis.x="start")),this.opener.$el&&(this.opener=this.opener.$el);const t=this.opener.getBoundingClientRect();this.position.x=t.left+window.scrollX+t.width,this.position.y=t.top+window.scrollY+t.height,!0!==this.$el.open&&this.$el.showModal(),await this.$nextTick();const e=this.$el.getBoundingClientRect(),i=10;"end"===this.axis.x?t.left-e.widthwindow.innerWidth-i&&e.width+ie.top&&(this.axis.y="bottom"):t.top+e.height>window.innerHeight-i&&e.height+i!0===t.default));t.push(this.item(e)),t.push("-");const i=this.languages.filter((t=>!1===t.default));for(const s of i)t.push(this.item(s));return t}},methods:{change(t){this.$reload({query:{language:t.code}})},item(t){return{click:()=>this.change(t),current:t.code===this.language.code,text:t.name}}}},(function(){var t=this,e=t._self._c;return t.languages.length>1?e("div",{staticClass:"k-languages-dropdown"},[e("k-button",{attrs:{dropdown:!0,text:t.code,icon:"translate",responsive:"text",size:"sm",variant:"filled"},on:{click:function(e){return t.$refs.languages.toggle()}}}),e("k-dropdown-content",{ref:"languages",attrs:{options:t.options}})],1):t._e()}),[]).exports;const Ae=ct({props:{align:{type:String,default:"right"},disabled:{type:Boolean},icon:{type:String,default:"dots"},options:{type:[Array,Function,String],default:()=>[]},text:{type:[Boolean,String],default:!0},theme:{type:String,default:"dark"},size:String,variant:String},emits:["action","option"],computed:{hasSingleOption(){return Array.isArray(this.options)&&1===this.options.length}},methods:{onAction(t,e,i){"function"==typeof t?t.call(this):(this.$emit("action",t,e,i),this.$emit("option",t,e,i))},toggle(t=this.$el){this.$refs.options.toggle(t)}}},(function(){var t=this,e=t._self._c;return t.hasSingleOption?e("k-button",{staticClass:"k-options-dropdown-toggle",attrs:{disabled:t.disabled,icon:t.options[0].icon??t.icon,size:t.options[0].size??t.size,title:t.options[0].title??t.options[0].tooltip??t.options[0].text,variant:t.options[0].variant??t.variant},on:{click:function(e){return t.onAction(t.options[0].option??t.options[0].click,t.options[0],0)}}},[!0===t.text?[t._v(" "+t._s(t.options[0].text)+" ")]:!1!==t.text?[t._v(" "+t._s(t.text)+" ")]:t._e()],2):t.options.length?e("div",{staticClass:"k-options-dropdown"},[e("k-button",{staticClass:"k-options-dropdown-toggle",attrs:{disabled:t.disabled,dropdown:!0,icon:t.icon,size:t.size,text:!0!==t.text&&!1!==t.text?t.text:null,title:t.$t("options"),variant:t.variant},on:{click:function(e){return t.$refs.options.toggle()}}}),e("k-dropdown-content",{ref:"options",staticClass:"k-options-dropdown-content",attrs:{"align-x":t.align,options:t.options},on:{action:t.onAction}})],1):t._e()}),[]).exports,Ie={mixins:[V,W,X,st,rt]},De={mixins:[Ie],inheritAttrs:!1,emits:["input"],methods:{focus(){this.$el.focus()}}},je={mixins:[V,W,nt,rt],props:{ignore:{default:()=>[],type:Array},max:Number,min:Number,search:{default:!0,type:[Object,Boolean]}}},Ee={mixins:[Ie,je],props:{create:{type:[Boolean,Object],default:!1},multiple:{type:Boolean,default:!0},value:{type:[Array,String],default:()=>[]}},emits:["create","escape","input"]};const Le=ct({mixins:[De,Ee],data(){return{display:this.search.display??!0,query:""}},computed:{choices(){let t=this.filteredOptions;return!0!==this.display&&(t=t.slice(0,this.display)),t.map((t=>({...t,disabled:t.disabled||this.isFull&&!1===this.value.includes(t.value),text:this.highlight(t.text)})))},filteredOptions(){if(!(this.query.length<(this.search.min??0)))return this.$helper.array.search(this.options,this.query,{field:"text"})},isFull(){return this.max&&this.value.length>=this.max},placeholder(){return this.search.placeholder?this.search.placeholder:this.options.length>0?this.$t("filter")+"…":this.$t("enter")+"…"},showCreate(){var t;if(!1===this.create)return!1;if(this.isFull)return!1;if(0===this.query.trim().length)return!1;if(!0===this.ignore.includes(this.query))return!1;if(!0===(null==(t=this.create.ignore)?void 0:t.includes(this.query)))return!1;return 0===this.options.filter((t=>t.text===this.query||t.value===this.query)).length},showEmpty(){return!1===this.create&&0===this.filteredOptions.length}},watch:{value:{handler(){this.$emit("invalid",this.$v.$invalid,this.$v)},immediate:!0}},methods:{add(){this.showCreate&&this.$emit("create",this.query)},enter(t){var e;null==(e=t.target)||e.click()},escape(){0===this.query.length?this.$emit("escape"):this.query=""},focus(){var t;this.$refs.search?this.$refs.search.focus():null==(t=this.$refs.options)||t.focus()},highlight(t){if(t=this.$helper.string.stripHTML(t),this.query.length>0){const e=new RegExp(`(${RegExp.escape(this.query)})`,"ig");return t.replace(e,"$1")}return t},input(t){this.$emit("input",t)}},validations(){return{value:{required:!this.required||t.required,minLength:!this.min||t.minLength(this.min),maxLength:!this.max||t.maxLength(this.max)}}}},(function(){var t=this,e=t._self._c;return e("k-navigate",{staticClass:"k-picklist-input",attrs:{element:"nav",axis:"y",select:"input[type=search], label, .k-picklist-input-body button"},on:{prev:function(e){return t.$emit("escape")}}},[t.search?e("header",{staticClass:"k-picklist-input-header"},[e("div",{staticClass:"k-picklist-input-search"},[e("k-search-input",{ref:"search",attrs:{autofocus:t.autofocus,disabled:t.disabled,placeholder:t.placeholder,value:t.query},on:{input:function(e){t.query=e}},nativeOn:{keydown:[function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"escape",void 0,e.key,void 0)?null:(e.preventDefault(),t.escape.apply(null,arguments))},function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"enter",13,e.key,"Enter")?null:(e.preventDefault(),t.add.apply(null,arguments))}]}}),t.showCreate?e("k-button",{staticClass:"k-picklist-input-create",attrs:{icon:"add",size:"xs"},on:{click:t.add}}):t._e()],1)]):t._e(),t.filteredOptions.length?[e("div",{staticClass:"k-picklist-input-body"},[e(t.multiple?"k-checkboxes-input":"k-radio-input",{ref:"options",tag:"component",staticClass:"k-picklist-input-options",attrs:{disabled:t.disabled,options:t.choices,value:t.value},on:{input:t.input},nativeOn:{keydown:function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"enter",13,e.key,"Enter")?null:(e.preventDefault(),t.enter.apply(null,arguments))}}}),!0!==t.display&&t.filteredOptions.length>t.display?e("k-button",{staticClass:"k-picklist-input-more",attrs:{icon:"angle-down"},on:{click:function(e){t.display=!0}}},[t._v(" "+t._s(t.$t("options.all",{count:t.filteredOptions.length}))+" ")]):t._e()],1)]:t.showEmpty?[e("div",{staticClass:"k-picklist-input-body"},[e("p",{staticClass:"k-picklist-input-empty"},[t._v(" "+t._s(t.$t("options.none"))+" ")])])]:t._e()],2)}),[]).exports;const Te=ct({mixins:[Ee],emits:["create","input"],methods:{close(){this.$refs.dropdown.close()},add(t){this.$emit("create",t)},input(t){this.$emit("input",t)},open(t){this.$refs.dropdown.open(t)},toggle(){this.$refs.dropdown.toggle()}}},(function(){var t=this,e=t._self._c;return e("k-dropdown-content",{ref:"dropdown",staticClass:"k-picklist-dropdown",attrs:{"align-x":"start",disabled:t.disabled,navigate:!1},nativeOn:{click:function(t){t.stopPropagation()}}},[e("k-picklist-input",t._b({on:{create:t.add,input:t.input,escape:function(e){return t.$refs.dropdown.close()}},nativeOn:{click:function(t){t.stopPropagation()}}},"k-picklist-input",t.$props,!1))],1)}),[]).exports,Be={install(t){t.component("k-dropdown",_e),t.component("k-dropdown-content",Se),t.component("k-dropdown-item",Oe),t.component("k-languages-dropdown",Me),t.component("k-options-dropdown",Ae),t.component("k-picklist-dropdown",Te)}};const qe=ct({props:{html:{type:Boolean,default:!1},limit:{type:Number,default:10},skip:{type:Array,default:()=>[]},options:Array,query:String},emits:["leave","search","select"],data:()=>({matches:[],selected:{text:null}}),mounted(){window.panel.deprecated(" will be removed in a future version.")},methods:{close(){this.$refs.dropdown.close()},onSelect(t){this.$emit("select",t),this.$refs.dropdown.close()},search(t){const e=this.options.filter((t=>-1!==this.skip.indexOf(t.value)));this.matches=this.$helper.array.search(e,t,{field:"text",limit:this.limit}),this.$emit("search",t,this.matches),this.$refs.dropdown.open()}}},(function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-autocomplete"},[t._t("default"),e("k-dropdown-content",{ref:"dropdown",attrs:{autofocus:!0},on:{leave:function(e){return t.$emit("leave")}}},t._l(t.matches,(function(i,s){return e("k-dropdown-item",t._b({key:s,nativeOn:{mousedown:function(e){return t.onSelect(i)},keydown:[function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"tab",9,e.key,"Tab")?null:(e.preventDefault(),t.onSelect(i))},function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"enter",13,e.key,"Enter")?null:(e.preventDefault(),t.onSelect(i))},function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"left",37,e.key,["Left","ArrowLeft"])||"button"in e&&0!==e.button?null:(e.preventDefault(),t.close.apply(null,arguments))},function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"backspace",void 0,e.key,void 0)?null:(e.preventDefault(),t.close.apply(null,arguments))},function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"delete",[8,46],e.key,["Backspace","Delete","Del"])?null:(e.preventDefault(),t.close.apply(null,arguments))}]}},"k-dropdown-item",i,!1),[e("span",{domProps:{innerHTML:t._s(t.html?i.text:t.$esc(i.text))}})])})),1),t._v(" "+t._s(t.query)+" ")],2)}),[]).exports;const Pe=ct({props:{count:Number,min:Number,max:Number,required:{type:Boolean,default:!1}},computed:{valid(){return!1===this.required&&0===this.count||(!0!==this.required||0!==this.count)&&(!(this.min&&this.countthis.max))}}},(function(){var t=this,e=t._self._c;return e("span",{staticClass:"k-counter",attrs:{"data-invalid":!t.valid}},[e("span",[t._v(t._s(t.count))]),t.min||t.max?e("span",{staticClass:"k-counter-rules"},[t.min&&t.max?[t._v(t._s(t.min)+"–"+t._s(t.max))]:t.min?[t._v("≥ "+t._s(t.min))]:t.max?[t._v("≤ "+t._s(t.max))]:t._e()],2):t._e()])}),[]).exports;const Ne=ct({props:{disabled:Boolean,config:Object,fields:{type:[Array,Object],default:()=>[]},novalidate:{type:Boolean,default:!1},value:{type:Object,default:()=>({})}},emits:["focus","input","submit"],data:()=>({errors:{}}),methods:{focus(t){var e,i;null==(i=null==(e=this.$refs.fields)?void 0:e.focus)||i.call(e,t)},onFocus(t,e,i){this.$emit("focus",t,e,i)},onInput(t,e,i){this.$emit("input",t,e,i)},onInvalid(t){this.$emit("invalid",t)},onSubmit(){this.$emit("submit",this.value)},submit(){this.$refs.submitter.click()}}},(function(){var t=this,e=t._self._c;return e("form",{ref:"form",staticClass:"k-form",attrs:{method:"POST",autocomplete:"off",novalidate:""},on:{submit:function(e){return e.preventDefault(),t.onSubmit.apply(null,arguments)}}},[t._t("header"),t._t("default",(function(){return[e("k-fieldset",{ref:"fields",attrs:{disabled:t.disabled,fields:t.fields,novalidate:t.novalidate,value:t.value},on:{focus:t.onFocus,input:t.onInput,invalid:t.onInvalid,submit:t.onSubmit}})]})),t._t("footer"),e("input",{ref:"submitter",staticClass:"k-form-submitter",attrs:{type:"submit"}})],2)}),[]).exports;const ze=ct({props:{lock:[Boolean,Object]},data:()=>({isLoading:null,isLocking:null}),computed:{api(){return[this.$panel.view.path+"/lock",null,null,!0]},buttons(){return"unlock"===this.mode?[{icon:"check",text:this.$t("lock.isUnlocked"),click:()=>this.resolve()},{icon:"download",text:this.$t("download"),click:()=>this.download()}]:"lock"===this.mode?[{icon:this.lock.data.unlockable?"unlock":"loader",text:this.$t("lock.isLocked",{email:this.$esc(this.lock.data.email)}),title:this.$t("lock.unlock"),disabled:!this.lock.data.unlockable,click:()=>this.unlock()}]:"changes"===this.mode?[{icon:"undo",text:this.$t("revert"),click:()=>this.revert()},{icon:"check",text:this.$t("save"),click:()=>this.save()}]:[]},disabled(){return"unlock"!==this.mode&&("lock"===this.mode?!this.lock.data.unlockable:"changes"===this.mode&&this.isDisabled)},hasChanges(){return this.$store.getters["content/hasChanges"]()},isDisabled(){return!1===this.$store.state.content.status.enabled},isLocked(){return"lock"===this.lockState},isUnlocked(){return"unlock"===this.lockState},mode(){return null!==this.lockState?this.lockState:!0===this.hasChanges?"changes":null},lockState(){return this.supportsLocking&&this.lock?this.lock.state:null},supportsLocking(){return!1!==this.lock},theme(){return"lock"===this.mode?"negative":"unlock"===this.mode?"info":"changes"===this.mode?"notice":null}},watch:{hasChanges:{handler(t,e){!0===this.supportsLocking&&!1===this.isLocked&&!1===this.isUnlocked&&(!0===t?(this.locking(),this.isLocking=setInterval(this.locking,3e4)):e&&(clearInterval(this.isLocking),this.locking(!1)))},immediate:!0},isLocked(t){!1===t&&this.$events.emit("model.reload")}},mounted(){this.supportsLocking&&(this.isLoading=setInterval(this.check,1e4)),this.$events.on("view.save",this.save)},destroyed(){clearInterval(this.isLoading),clearInterval(this.isLocking),this.$events.off("view.save",this.save)},methods:{async check(){if(!1===this.$panel.isOffline){const{lock:t}=await this.$api.get(...this.api);Vue.set(this.$panel.view.props,"lock",t)}},download(){let t="";const e=this.$store.getters["content/changes"]();for(const s in e){const i=e[s];t+=s+": \n\n","object"==typeof i&&Object.keys(i).length||Array.isArray(i)&&i.length?t+=JSON.stringify(i,null,2):t+=i,t+="\n\n----\n\n"}let i=document.createElement("a");i.setAttribute("href","data:text/plain;charset=utf-8,"+encodeURIComponent(t)),i.setAttribute("download",this.$panel.view.path+".txt"),i.style.display="none",document.body.appendChild(i),i.click(),document.body.removeChild(i)},async locking(t=!0){if(!0!==this.$panel.isOffline)if(!0===t)try{await this.$api.patch(...this.api)}catch{clearInterval(this.isLocking),this.$store.dispatch("content/revert")}else clearInterval(this.isLocking),await this.$api.delete(...this.api)},async resolve(){await this.unlock(!1),this.$store.dispatch("content/revert")},revert(){this.$panel.dialog.open({component:"k-remove-dialog",props:{submitButton:{icon:"undo",text:this.$t("revert")},text:this.$t("revert.confirm")},on:{submit:()=>{this.$store.dispatch("content/revert"),this.$panel.dialog.close()}}})},async save(t){var e;null==(e=null==t?void 0:t.preventDefault)||e.call(t),await this.$store.dispatch("content/save"),this.$events.emit("model.update"),this.$panel.notification.success()},async unlock(t=!0){const e=[this.$panel.view.path+"/unlock",null,null,!0];!0!==t?(await this.$api.delete(...e),this.$reload({silent:!0})):this.$panel.dialog.open({component:"k-remove-dialog",props:{submitButton:{icon:"unlock",text:this.$t("lock.unlock")},text:this.$t("lock.unlock.submit",{email:this.$esc(this.lock.data.email)})},on:{submit:async()=>{await this.$api.patch(...e),this.$panel.dialog.close(),this.$reload({silent:!0})}}})}}},(function(){var t=this,e=t._self._c;return t.buttons.length>0?e("k-button-group",{staticClass:"k-form-buttons",attrs:{layout:"collapsed"}},t._l(t.buttons,(function(i){return e("k-button",t._b({key:i.icon,attrs:{size:"sm",variant:"filled",disabled:t.isDisabled,responsive:!0,theme:t.theme}},"k-button",i,!1))})),1):t._e()}),[]).exports,Fe={mixins:[W,G,X,Q,st,rt],props:{counter:[Boolean,Object],endpoints:Object,input:[String,Number],translate:Boolean,type:String}};const Ye=ct({mixins:[Fe],inheritAttrs:!1,emits:["blur","focus"]},(function(){var t=this,e=t._self._c;return e("div",{class:["k-field",`k-field-name-${t.name}`,`k-field-type-${t.type}`],attrs:{"data-disabled":t.disabled,"data-translate":t.translate},on:{focusin:function(e){return t.$emit("focus",e)},focusout:function(e){return t.$emit("blur",e)}}},[t._t("header",(function(){return[e("header",{staticClass:"k-field-header"},[t._t("label",(function(){return[e("k-label",{attrs:{input:t.input,required:t.required,title:t.label,type:"field"}},[t._v(" "+t._s(t.label)+" ")])]})),t._t("options"),t._t("counter",(function(){return[t.counter?e("k-counter",t._b({staticClass:"k-field-counter",attrs:{required:t.required}},"k-counter",t.counter,!1)):t._e()]}))],2)]})),t._t("default"),t._t("footer",(function(){return[t.help||t.$slots.help?e("footer",{staticClass:"k-field-footer"},[t._t("help",(function(){return[t.help?e("k-text",{staticClass:"k-help k-field-help",attrs:{html:t.help}}):t._e()]}))],2):t._e()]}))],2)}),[]).exports;const Re=ct({props:{config:Object,disabled:Boolean,fields:{type:[Array,Object],default:()=>({})},novalidate:{type:Boolean,default:!1},value:{type:Object,default:()=>({})}},emits:["focus","input","invalid","submit"],data:()=>({errors:{}}),methods:{focus(t){if(t)return void(this.hasField(t)&&"function"==typeof this.$refs[t][0].focus&&this.$refs[t][0].focus());const e=Object.keys(this.$refs)[0];this.focus(e)},hasFieldType(t){return this.$helper.isComponent(`k-${t}-field`)},hasField(t){var e;return null==(e=this.$refs[t])?void 0:e[0]},onInvalid(t,e,i,s){this.errors[s]=e,this.$emit("invalid",this.errors)},onInput(t,e,i){const s=this.value;this.$set(s,i,t),this.$emit("input",s,e,i)},hasErrors(){return this.$helper.object.length(this.errors)>0}}},(function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-fieldset"},[e("k-grid",{attrs:{variant:"fields"}},[t._l(t.fields,(function(i,s){return[t.$helper.field.isVisible(i,t.value)?e("k-column",{key:i.signature,attrs:{width:i.width}},[t.hasFieldType(i.type)?e("k-"+i.type+"-field",t._b({ref:s,refInFor:!0,tag:"component",attrs:{disabled:t.disabled||i.disabled,"form-data":t.value,name:s,novalidate:t.novalidate,value:t.value[s]},on:{input:function(e){return t.onInput(e,i,s)},focus:function(e){return t.$emit("focus",e,i,s)},invalid:(e,n)=>t.onInvalid(e,n,i,s),submit:function(e){return t.$emit("submit",e,i,s)}}},"component",i,!1)):e("k-box",{attrs:{theme:"negative"}},[e("k-text",{attrs:{size:"small"}},[t._v(" "+t._s(t.$t("error.field.type.missing",{name:s,type:i.type}))+" ")])],1)],1):t._e()]}))],2)],1)}),[]).exports,Ue={mixins:[U,K,W,Z],inheritAttrs:!1,props:{autofocus:Boolean,type:String,icon:[String,Boolean],novalidate:{type:Boolean,default:!1},value:{type:[String,Boolean,Number,Object,Array],default:null}}};const He=ct({mixins:[Ue],data(){return{isInvalid:this.invalid,listeners:{...this.$listeners,invalid:(t,e)=>{this.isInvalid=t,this.$emit("invalid",t,e)}}}},computed:{inputProps(){return{...this.$props,...this.$attrs}}},watch:{invalid(){this.isInvalid=this.invalid}},methods:{blur(t){(null==t?void 0:t.relatedTarget)&&!1===this.$el.contains(t.relatedTarget)&&this.trigger(null,"blur")},focus(t){this.trigger(t,"focus")},select(t){this.trigger(t,"select")},trigger(t,e){var i,s,n;if("INPUT"===(null==(i=null==t?void 0:t.target)?void 0:i.tagName)&&"function"==typeof(null==(s=null==t?void 0:t.target)?void 0:s[e]))return void t.target[e]();if("function"==typeof(null==(n=this.$refs.input)?void 0:n[e]))return void this.$refs.input[e]();const o=this.$el.querySelector("input, select, textarea");"function"==typeof(null==o?void 0:o[e])&&o[e]()}}},(function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-input",attrs:{"data-disabled":t.disabled,"data-invalid":!t.novalidate&&t.isInvalid,"data-type":t.type}},[t.$slots.before||t.before?e("span",{staticClass:"k-input-description k-input-before",on:{click:t.focus}},[t._t("before",(function(){return[t._v(t._s(t.before))]}))],2):t._e(),e("span",{staticClass:"k-input-element",on:{click:function(e){return e.stopPropagation(),t.focus.apply(null,arguments)}}},[t._t("default",(function(){return[e("k-"+t.type+"-input",t._g(t._b({ref:"input",tag:"component",attrs:{value:t.value}},"component",t.inputProps,!1),t.listeners))]}))],2),t.$slots.after||t.after?e("span",{staticClass:"k-input-description k-input-after",on:{click:t.focus}},[t._t("after",(function(){return[t._v(t._s(t.after))]}))],2):t._e(),t.$slots.icon||t.icon?e("span",{staticClass:"k-input-icon",on:{click:t.focus}},[t._t("icon",(function(){return[e("k-icon",{attrs:{type:t.icon}})]}))],2):t._e()])}),[]).exports;const Ve=ct({props:{accept:{type:String,default:"*"},attributes:{type:Object},max:{type:Number},method:{type:String,default:"POST"},multiple:{type:Boolean,default:!0},url:{type:String}},emits:["success"],methods:{open(t){window.panel.deprecated(" will be removed in a future version. Use `$panel.upload.open()` instead."),this.$panel.upload.pick(this.params(t))},params(t){return{...this.$props,...t??{},on:{complete:(t,e)=>{this.$emit("success",t,e)}}}},select(t){this.$panel.upload.select(t.target.files)},drop(t,e){window.panel.deprecated(" will be removed in a future version. Use `$panel.upload.select()` instead."),this.$panel.upload.open(t,this.params(e))},upload(t,e){window.panel.deprecated(" will be removed in a future version. Use `$panel.upload.select()` instead."),this.$panel.upload.select(t,this.params(e)),this.$panel.upload.start()}},render:()=>""},null,null).exports,Ke={props:{content:{default:()=>({}),type:[Array,Object]},fieldset:{default:()=>({}),type:Object}}};const We=ct({mixins:[Ke],inheritAttrs:!1,computed:{icon(){return this.fieldset.icon??"box"},label(){if(!this.fieldset.label||0===this.fieldset.label.length)return!1;if(this.fieldset.label===this.name)return!1;let t=this.$helper.string.template(this.fieldset.label,this.content);return"…"!==t&&(t=this.$helper.string.stripHTML(t),this.$helper.string.unescapeHTML(t))},name(){return this.fieldset.name??this.fieldset.label}}},(function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-block-title"},[e("k-icon",{staticClass:"k-block-icon",attrs:{type:t.icon}}),t.name?e("span",{staticClass:"k-block-name"},[t._v(" "+t._s(t.name)+" ")]):t._e(),t.label?e("span",{staticClass:"k-block-label"},[t._v(" "+t._s(t.label)+" ")]):t._e()],1)}),[]).exports,Je={mixins:[Ke,W],props:{endpoints:{default:()=>({}),type:[Array,Object]},id:String}};const Ge=ct({mixins:[Je],inheritAttrs:!1,methods:{field(t,e=null){let i=null;for(const s of Object.values(this.fieldset.tabs??{}))s.fields[t]&&(i=s.fields[t]);return i??e},open(){this.$emit("open")},update(t){this.$emit("update",{...this.content,...t})}}},(function(){var t=this;return(0,t._self._c)("k-block-title",{attrs:{content:t.content,fieldset:t.fieldset},nativeOn:{dblclick:function(e){return t.$emit("open")}}})}),[]).exports,Xe={props:{isBatched:Boolean,isFull:Boolean,isHidden:Boolean,isMergable:Boolean}};const Ze=ct({mixins:[Xe],props:{isEditable:Boolean,isSplitable:Boolean},emits:["chooseToAppend","chooseToConvert","chooseToPrepend","copy","duplicate","hide","merge","open","paste","remove","removeSelected","show","split","sortDown","sortUp"],computed:{buttons(){return this.isBatched?[{icon:"template",title:this.$t("copy"),click:()=>this.$emit("copy")},{when:this.isMergable,icon:"merge",title:this.$t("merge"),click:()=>this.$emit("merge")},{icon:"trash",title:this.$t("remove"),click:()=>this.$emit("removeSelected")}]:[{when:this.isEditable,icon:"edit",title:this.$t("edit"),click:()=>this.$emit("open")},{icon:"add",title:this.$t("insert.after"),disabled:this.isFull,click:()=>this.$emit("chooseToAppend")},{icon:"trash",title:this.$t("delete"),click:()=>this.$emit("remove")},{icon:"sort",title:this.$t("sort.drag"),class:"k-sort-handle",key:t=>this.sort(t)},{icon:"dots",title:this.$t("more"),dropdown:[{icon:"angle-up",label:this.$t("insert.before"),disabled:this.isFull,click:()=>this.$emit("chooseToPrepend")},{icon:"angle-down",label:this.$t("insert.after"),disabled:this.isFull,click:()=>this.$emit("chooseToAppend")},"-",{when:this.isEditable,icon:"edit",label:this.$t("edit"),click:()=>this.$emit("open")},{icon:"refresh",label:this.$t("field.blocks.changeType"),click:()=>this.$emit("chooseToConvert")},{when:this.isSplitable,icon:"split",label:this.$t("split"),click:()=>this.$emit("split")},"-",{icon:"template",label:this.$t("copy"),click:()=>this.$emit("copy")},{icon:"download",label:this.$t("paste.after"),disabled:this.isFull,click:()=>this.$emit("paste")},"-",{icon:this.isHidden?"preview":"hidden",label:this.isHidden?this.$t("show"):this.$t("hide"),click:()=>this.$emit(this.isHidden?"show":"hide")},{icon:"copy",label:this.$t("duplicate"),click:()=>this.$emit("duplicate")},"-",{icon:"trash",label:this.$t("delete"),click:()=>this.$emit("remove")}]}]}},methods:{open(){this.$refs.options.open()},sort(t){switch(t.key){case"ArrowUp":t.preventDefault(),this.$emit("sortUp");break;case"ArrowDown":t.preventDefault(),this.$emit("sortDown")}}}},(function(){return(0,this._self._c)("k-toolbar",{staticClass:"k-block-options",attrs:{buttons:this.buttons},nativeOn:{mousedown:function(t){t.preventDefault()}}})}),[]).exports;const Qe=ct({mixins:[Je,Xe],inheritAttrs:!1,props:{attrs:{default:()=>({}),type:[Array,Object]},isLastSelected:Boolean,isSelected:Boolean,name:String,next:Object,prev:Object,type:String},emits:["append","chooseToAppend","chooseToConvert","chooseToPrepend","close","copy","duplicate","focus","hide","merge","open","paste","prepend","remove","selectDown","selectUp","show","sortDown","sortUp","split","submit","update"],computed:{className(){let t=["k-block-type-"+this.type];return this.fieldset.preview&&this.fieldset.preview!==this.type&&t.push("k-block-type-"+this.fieldset.preview),!1===this.wysiwyg&&t.push("k-block-type-default"),t},containerType(){const t=this.fieldset.preview;return!1!==t&&(t&&this.$helper.isComponent("k-block-type-"+t)?t:!!this.$helper.isComponent("k-block-type-"+this.type)&&this.type)},customComponent(){return this.wysiwyg?this.wysiwygComponent:"k-block-type-default"},isDisabled(){return!0===this.disabled||!0===this.fieldset.disabled},isEditable(){return!1!==this.fieldset.editable},listeners(){return{append:t=>this.$emit("append",t),chooseToAppend:t=>this.$emit("chooseToAppend",t),chooseToConvert:t=>this.$emit("chooseToConvert",t),chooseToPrepend:t=>this.$emit("chooseToPrepend",t),close:()=>this.$emit("close"),copy:()=>this.$emit("copy"),duplicate:()=>this.$emit("duplicate"),focus:()=>this.$emit("focus"),hide:()=>this.$emit("hide"),merge:()=>this.$emit("merge"),open:t=>this.open(t),paste:()=>this.$emit("paste"),prepend:t=>this.$emit("prepend",t),remove:()=>this.remove(),removeSelected:()=>this.$emit("removeSelected"),show:()=>this.$emit("show"),sortDown:()=>this.$emit("sortDown"),sortUp:()=>this.$emit("sortUp"),split:t=>this.$emit("split",t),update:t=>this.$emit("update",t)}},tabs(){const t=this.fieldset.tabs??{};for(const[e,i]of Object.entries(t))for(const[s]of Object.entries(i.fields??{}))t[e].fields[s].section=this.name,t[e].fields[s].endpoints={field:this.endpoints.field+"/fieldsets/"+this.type+"/fields/"+s,section:this.endpoints.section,model:this.endpoints.model};return t},wysiwyg(){return!1!==this.wysiwygComponent},wysiwygComponent(){return!!this.containerType&&"k-block-type-"+this.containerType}},methods:{backspace(t){if(t.target.matches("[contenteditable], input, textarea"))return!1;t.preventDefault(),this.remove()},close(){this.$panel.drawer.close(this.id)},focus(){var t,e;"function"==typeof(null==(t=this.$refs.editor)?void 0:t.focus)?this.$refs.editor.focus():null==(e=this.$refs.container)||e.focus()},goTo(t){var e;t&&(null==(e=t.$refs.container)||e.focus(),t.open(null,!0))},isSplitable(){var t;return!0!==this.isFull&&(!!this.$refs.editor&&((this.$refs.editor.isSplitable??!0)&&"function"==typeof(null==(t=this.$refs.editor)?void 0:t.split)))},onClose(){this.$emit("close"),this.focus()},onFocus(t){this.disabled||this.$emit("focus",t)},onFocusIn(t){var e,i;this.disabled||(null==(i=null==(e=this.$refs.options)?void 0:e.$el)?void 0:i.contains(t.target))||this.$emit("focus",t)},onInput(t){this.$emit("update",t)},open(t,e=!1){!this.isEditable||this.isBatched||this.isDisabled||(this.$panel.drawer.open({component:"k-block-drawer",id:this.id,tab:t,on:{close:this.onClose,input:this.onInput,next:()=>this.goTo(this.next),prev:()=>this.goTo(this.prev),remove:this.remove,show:this.show,submit:this.submit},props:{hidden:this.isHidden,icon:this.fieldset.icon??"box",next:this.next,prev:this.prev,tabs:this.tabs,title:this.fieldset.name,value:this.content},replace:e}),this.$emit("open"))},remove(){if(this.isBatched)return this.$emit("removeSelected");this.$panel.dialog.open({component:"k-remove-dialog",props:{text:this.$t("field.blocks.delete.confirm")},on:{submit:()=>{this.$panel.dialog.close(),this.close(),this.$emit("remove",this.id)}}})},show(){this.$emit("show")},submit(){this.close(),this.$emit("submit")}}},(function(){var t=this,e=t._self._c;return e("div",{ref:"container",staticClass:"k-block-container",class:["k-block-container-fieldset-"+t.type,t.containerType?"k-block-container-type-"+t.containerType:""],attrs:{"data-batched":t.isBatched,"data-disabled":t.isDisabled,"data-hidden":t.isHidden,"data-id":t.id,"data-last-selected":t.isLastSelected,"data-selected":t.isSelected,"data-translate":t.fieldset.translate,tabindex:t.isDisabled?null:0},on:{keydown:[function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"j",void 0,e.key,void 0)?null:e.ctrlKey?(e.preventDefault(),e.stopPropagation(),t.$emit("merge")):null},function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"down",40,e.key,["Down","ArrowDown"])?null:e.ctrlKey&&e.altKey?(e.preventDefault(),e.stopPropagation(),t.$emit("selectDown")):null},function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"up",38,e.key,["Up","ArrowUp"])?null:e.ctrlKey&&e.altKey?(e.preventDefault(),e.stopPropagation(),t.$emit("selectUp")):null},function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"down",40,e.key,["Down","ArrowDown"])?null:e.ctrlKey&&e.shiftKey?(e.preventDefault(),e.stopPropagation(),t.$emit("sortDown")):null},function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"up",38,e.key,["Up","ArrowUp"])?null:e.ctrlKey&&e.shiftKey?(e.preventDefault(),e.stopPropagation(),t.$emit("sortUp")):null},function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"backspace",void 0,e.key,void 0)?null:e.ctrlKey?(e.stopPropagation(),t.backspace.apply(null,arguments)):null}],focus:function(e){return e.stopPropagation(),t.onFocus.apply(null,arguments)},focusin:function(e){return e.stopPropagation(),t.onFocusIn.apply(null,arguments)}}},[e("div",{staticClass:"k-block",class:t.className,attrs:{"data-disabled":t.isDisabled}},[e(t.customComponent,t._g(t._b({ref:"editor",tag:"component",attrs:{tabs:t.tabs}},"component",t.$props,!1),t.listeners))],1),t.isDisabled?t._e():e("k-block-options",t._g(t._b({ref:"options"},"k-block-options",{isBatched:t.isBatched,isEditable:t.isEditable,isFull:t.isFull,isHidden:t.isHidden,isMergable:t.isMergable,isSplitable:t.isSplitable()},!1),{...t.listeners,split:()=>t.$refs.editor.split(),open:()=>{"function"==typeof t.$refs.editor.open?t.$refs.editor.open():t.open()}}))],1)}),[]).exports,ti={mixins:[V,W,X],props:{empty:String,endpoints:Object,fieldsets:Object,fieldsetGroups:Object,group:String,max:{type:Number,default:null},value:{type:Array,default:()=>[]}},emits:["input"]};const ei=ct({mixins:[ti],inheritAttrs:!1,data(){return{blocks:this.value??[],isEditing:!1,isMultiSelectKey:!1,selected:[]}},computed:{draggableOptions(){return{id:this.id,handle:".k-sort-handle",list:this.blocks,move:this.move,delay:10,data:{fieldsets:this.fieldsets,isFull:this.isFull},options:{group:this.group}}},hasFieldsets(){return this.$helper.object.length(this.fieldsets)>0},isEmpty(){return 0===this.blocks.length},isFull(){return null!==this.max&&this.blocks.length>=this.max},isMergable(){if(this.selected.length<2)return!1;const t=this.selected.map((t=>this.find(t)));return!(new Set(t.map((t=>t.type))).size>1)&&"function"==typeof this.ref(t[0]).$refs.editor.merge}},watch:{value(){this.blocks=this.value}},mounted(){!0===this.$props.autofocus&&setTimeout(this.focus,100),this.$events.on("blur",this.onBlur),this.$events.on("click",this.onClickGlobal),this.$events.on("copy",this.onCopy),this.$events.on("keydown",this.onKey),this.$events.on("keyup",this.onKey),this.$events.on("paste",this.onPaste)},destroyed(){this.$events.off("blur",this.onBlur),this.$events.off("click",this.onClickGlobal),this.$events.off("copy",this.onCopy),this.$events.off("keydown",this.onKey),this.$events.off("keyup",this.onKey),this.$events.off("paste",this.onPaste)},methods:{async add(t="text",e){const i=await this.$api.get(this.endpoints.field+"/fieldsets/"+t);this.blocks.splice(e,0,i),this.save(),await this.$nextTick(),this.focusOrOpen(i)},choose(t){if(1===this.$helper.object.length(this.fieldsets))return this.add(Object.values(this.fieldsets)[0].type,t);this.$panel.dialog.open({component:"k-block-selector",props:{fieldsetGroups:this.fieldsetGroups,fieldsets:this.fieldsets},on:{submit:e=>{this.add(e,t),this.$panel.dialog.close()},paste:e=>{this.paste(e,t)}}})},chooseToConvert(t){this.$panel.dialog.open({component:"k-block-selector",props:{disabledFieldsets:[t.type],fieldsetGroups:this.fieldsetGroups,fieldsets:this.fieldsets,headline:this.$t("field.blocks.changeType")},on:{submit:e=>{this.convert(e,t),this.$panel.dialog.close()},paste:this.paste}})},copy(t){if(0===this.blocks.length)return!1;if(0===this.selected.length)return!1;let e=[];for(const i of this.blocks)this.selected.includes(i.id)&&e.push(i);if(0===e.length)return!1;this.$helper.clipboard.write(e,t),this.selected=e.map((t=>t.id)),this.$panel.notification.success({message:this.$t("copy.success",{count:e.length}),icon:"template"})},copyAll(){this.selectAll(),this.copy(),this.deselectAll()},async convert(t,e){var i;const s=this.findIndex(e.id);if(-1===s)return!1;const n=t=>{let e={};for(const i of Object.values((null==t?void 0:t.tabs)??{}))e={...e,...i.fields};return e},o=this.blocks[s],a=await this.$api.get(this.endpoints.field+"/fieldsets/"+t),r=this.fieldsets[o.type],l=this.fieldsets[t];if(!l)return!1;let c=a.content;const u=n(l),d=n(r);for(const[p,h]of Object.entries(u)){const t=d[p];(null==t?void 0:t.type)===h.type&&(null==(i=null==o?void 0:o.content)?void 0:i[p])&&(c[p]=o.content[p])}this.blocks[s]={...a,id:o.id,content:c},this.save()},deselect(t){const e=this.selected.findIndex((e=>e===t.id));-1!==e&&this.selected.splice(e,1)},deselectAll(){this.selected=[]},async duplicate(t,e){const i={...structuredClone(t),id:this.$helper.uuid()};this.blocks.splice(e+1,0,i),this.save()},fieldset(t){return this.fieldsets[t.type]??{icon:"box",name:t.type,tabs:{content:{fields:{}}},type:t.type}},find(t){return this.blocks.find((e=>e.id===t))},findIndex(t){return this.blocks.findIndex((e=>e.id===t))},focus(t){const e=this.ref(t);this.selected=[(null==t?void 0:t.id)??this.blocks[0]],null==e||e.focus(),null==e||e.$el.scrollIntoView({block:"nearest"})},focusOrOpen(t){this.fieldsets[t.type].wysiwyg?this.focus(t):this.open(t)},hide(t){Vue.set(t,"isHidden",!0),this.save()},isInputEvent(){const t=document.querySelector(":focus");return null==t?void 0:t.matches("input, textarea, [contenteditable], .k-writer")},isLastSelected(t){const[e]=this.selected.slice(-1);return e&&t.id===e},isOnlyInstance:()=>1===document.querySelectorAll(".k-blocks").length,isSelected(t){return this.selected.includes(t.id)},async merge(){if(this.isMergable){const t=this.selected.map((t=>this.find(t)));this.ref(t[0]).$refs.editor.merge(t);for(const e of t.slice(1))this.remove(e);await this.$nextTick(),this.focus(t[0])}},move(t){if(t.from!==t.to){const e=t.draggedContext.element,i=t.relatedContext.component.componentData||t.relatedContext.component.$parent.componentData;if(!1===Object.keys(i.fieldsets).includes(e.type))return!1;if(!0===i.isFull)return!1}return!0},onBlur(){0===this.selected.length&&(this.isMultiSelectKey=!1)},onClickBlock(t,e){e&&this.isMultiSelectKey&&this.onKey(e),this.isMultiSelectKey&&(e.preventDefault(),e.stopPropagation(),this.isSelected(t)?this.deselect(t):this.select(t))},onClickGlobal(t){var e;if("function"==typeof t.target.closest&&(t.target.closest(".k-dialog")||t.target.closest(".k-drawer")))return;const i=document.querySelector(".k-overlay:last-of-type");!1!==this.$el.contains(t.target)||!1!==(null==i?void 0:i.contains(t.target))?i&&!1===(null==(e=this.$el.closest(".k-layout-column"))?void 0:e.contains(t.target))&&this.deselectAll():this.deselectAll()},onCopy(t){return!1!==this.$el.contains(t.target)&&!0!==this.isEditing&&!0!==this.$panel.dialog.isOpen&&!0!==this.isInputEvent(t)&&this.copy(t)},onFocus(t){!1===this.isMultiSelectKey&&(this.selected=[t.id])},async onKey(t){if(this.isMultiSelectKey=t.metaKey||t.ctrlKey||t.altKey,"Escape"===t.code&&this.selected.length>1){const t=this.find(this.selected[0]);await this.$nextTick(),this.focus(t)}},onPaste(t){return!0!==this.isInputEvent(t)&&(!0!==this.isEditing&&!0!==this.$panel.dialog.isOpen&&((0!==this.selected.length||!1!==this.$el.contains(t.target))&&this.paste(t)))},open(t){var e;null==(e=this.$refs["block-"+t.id])||e[0].open()},async paste(t,e){const i=this.$helper.clipboard.read(t);let s=await this.$api.post(this.endpoints.field+"/paste",{html:i});if(void 0===e){let t=this.selected[this.selected.length-1];-1===(e=this.findIndex(t))&&(e=this.blocks.length),e++}if(this.max){const t=this.max-this.blocks.length;s=s.slice(0,t)}this.blocks.splice(e,0,...s),this.save(),this.$panel.notification.success({message:this.$t("paste.success",{count:s.length}),icon:"download"})},pasteboard(){this.$panel.dialog.open({component:"k-block-pasteboard",on:{paste:this.paste}})},prevNext(t){var e;if(this.blocks[t])return null==(e=this.$refs["block-"+this.blocks[t].id])?void 0:e[0]},ref(t){var e,i;return null==(i=this.$refs["block-"+((null==t?void 0:t.id)??(null==(e=this.blocks[0])?void 0:e.id))])?void 0:i[0]},remove(t){const e=this.findIndex(t.id);-1!==e&&(this.deselect(t),this.$delete(this.blocks,e),this.save())},removeAll(){this.$panel.dialog.open({component:"k-remove-dialog",props:{text:this.$t("field.blocks.delete.confirm.all"),submitButton:this.$t("delete.all")},on:{submit:()=>{this.selected=[],this.blocks=[],this.save(),this.$panel.dialog.close()}}})},removeSelected(){this.$panel.dialog.open({component:"k-remove-dialog",props:{text:this.$t("field.blocks.delete.confirm.selected")},on:{submit:()=>{for(const t of this.selected){const e=this.findIndex(t);-1!==e&&this.$delete(this.blocks,e)}this.deselectAll(),this.save(),this.$panel.dialog.close()}}})},save(){this.$emit("input",this.blocks)},select(t){!1===this.isSelected(t)&&this.selected.push(t.id)},selectDown(){const t=this.selected[this.selected.length-1],e=this.findIndex(t)+1;e=0&&this.select(this.blocks[e])},selectAll(){this.selected=Object.values(this.blocks).map((t=>t.id))},show(t){Vue.set(t,"isHidden",!1),this.save()},async sort(t,e,i){if(i<0)return;let s=structuredClone(this.blocks);s.splice(e,1),s.splice(i,0,t),this.blocks=s,this.save(),await this.$nextTick(),this.focus(t)},async split(t,e,i){const s=structuredClone(t);s.content={...s.content,...i[0]};const n=await this.$api.get(this.endpoints.field+"/fieldsets/"+t.type);n.content={...n.content,...s.content,...i[1]},this.blocks.splice(e,1,s,n),this.save(),await this.$nextTick(),this.focus(n)},update(t,e){const i=this.findIndex(t.id);if(-1!==i)for(const s in e)Vue.set(this.blocks[i].content,s,e[s]);this.save()}}},(function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-blocks",attrs:{"data-disabled":t.disabled,"data-empty":0===t.blocks.length}},[t.hasFieldsets?[e("k-draggable",t._b({staticClass:"k-blocks-list",attrs:{"data-multi-select-key":t.isMultiSelectKey},on:{sort:t.save},scopedSlots:t._u([0===t.blocks.length?{key:"footer",fn:function(){return[e("k-empty",{staticClass:"k-blocks-empty",attrs:{icon:"box"},on:{click:function(e){return t.choose(t.blocks.length)}}},[t._v(" "+t._s(t.empty??t.$t("field.blocks.empty"))+" ")])]},proxy:!0}:null],null,!0)},"k-draggable",t.draggableOptions,!1),t._l(t.blocks,(function(i,s){return e("k-block",t._b({key:i.id,ref:"block-"+i.id,refInFor:!0,on:{append:function(e){return t.add(e,s+1)},chooseToAppend:function(e){return t.choose(s+1)},chooseToConvert:function(e){return t.chooseToConvert(i)},chooseToPrepend:function(e){return t.choose(s)},close:function(e){t.isEditing=!1},copy:function(e){return t.copy()},duplicate:function(e){return t.duplicate(i,s)},focus:function(e){return t.onFocus(i)},hide:function(e){return t.hide(i)},merge:function(e){return t.merge()},open:function(e){t.isEditing=!0},paste:function(e){return t.pasteboard()},prepend:function(e){return t.add(e,s)},remove:function(e){return t.remove(i)},removeSelected:t.removeSelected,show:function(e){return t.show(i)},selectDown:t.selectDown,selectUp:t.selectUp,sortDown:function(e){return t.sort(i,s,s+1)},sortUp:function(e){return t.sort(i,s,s-1)},split:function(e){return t.split(i,s,e)},update:function(e){return t.update(i,e)}},nativeOn:{click:function(e){return t.onClickBlock(i,e)}}},"k-block",{...i,disabled:t.disabled,endpoints:t.endpoints,fieldset:t.fieldset(i),isBatched:t.isSelected(i)&&t.selected.length>1,isFull:t.isFull,isHidden:!0===i.isHidden,isLastSelected:t.isLastSelected(i),isMergable:t.isMergable,isSelected:t.isSelected(i),next:t.prevNext(s+1),prev:t.prevNext(s-1)},!1))})),1)]:e("k-empty",{attrs:{icon:"box"}},[t._v(" "+t._s(t.$t("field.blocks.fieldsets.empty"))+" ")])],2)}),[]).exports;const ii=ct({inheritAttrs:!1,emits:["close","paste","submit"],computed:{shortcut(){return this.$helper.keyboard.metaKey()+"+v"}},methods:{paste(t){this.$emit("close"),this.$emit("paste",t)}}},(function(){var t=this,e=t._self._c;return e("k-dialog",{ref:"dialog",staticClass:"k-block-importer",attrs:{"cancel-button":!1,"submit-button":!1,visible:!0,size:"large"},on:{cancel:function(e){return t.$emit("cancel")},submit:function(e){return t.$emit("submit")}}},[e("label",{attrs:{for:"pasteboard"},domProps:{innerHTML:t._s(t.$t("field.blocks.fieldsets.paste",{shortcut:t.shortcut}))}}),e("textarea",{attrs:{id:"pasteboard"},on:{paste:function(e){return e.preventDefault(),t.paste.apply(null,arguments)}}})])}),[]).exports;const si=ct({inheritAttrs:!1,props:{disabledFieldsets:{default:()=>[],type:Array},fieldsets:{type:Object},fieldsetGroups:{type:Object},headline:{type:String},size:{type:String,default:"medium"},value:{default:null,type:String}},emits:["cancel","input","paste","submit"],data:()=>({selected:null}),computed:{groups(){const t={};let e=0;const i=this.fieldsetGroups??{blocks:{label:this.$t("field.blocks.fieldsets.label"),sets:Object.keys(this.fieldsets)}};for(const s in i){const n=i[s];n.open=!1!==n.open,n.fieldsets=n.sets.filter((t=>this.fieldsets[t])).map((t=>(e++,{...this.fieldsets[t],index:e}))),0!==n.fieldsets.length&&(t[s]=n)}return t},shortcut(){return this.$helper.keyboard.metaKey()+"+v"}},mounted(){this.$events.on("paste",this.paste)},destroyed(){this.$events.off("paste",this.paste)},methods:{paste(t){this.$emit("paste",t),this.close()}}},(function(){var t=this,e=t._self._c;return e("k-dialog",{staticClass:"k-block-selector",attrs:{"cancel-button":!1,size:t.size,"submit-button":!1,visible:!0},on:{cancel:function(e){return t.$emit("cancel")},submit:function(e){return t.$emit("submit",t.value)}}},[t.headline?e("k-headline",[t._v(" "+t._s(t.headline)+" ")]):t._e(),t._l(t.groups,(function(i,s){return e("details",{key:s,attrs:{open:i.open}},[e("summary",[t._v(t._s(i.label))]),e("k-navigate",{staticClass:"k-block-types"},t._l(i.fieldsets,(function(i){return e("k-button",{key:i.name,attrs:{disabled:t.disabledFieldsets.includes(i.type),icon:i.icon??"box",text:i.name,size:"lg"},on:{click:function(e){return t.$emit("submit",i.type)}},nativeOn:{focus:function(e){return t.$emit("input",i.type)}}})})),1)],1)})),e("p",{staticClass:"k-clipboard-hint",domProps:{innerHTML:t._s(t.$t("field.blocks.fieldsets.paste",{shortcut:t.shortcut}))}})],2)}),[]).exports;const ni=ct({props:{value:String}},(function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-block-background-dropdown"},[e("k-button",{attrs:{dropdown:!0,size:"xs",variant:"filled"},on:{click:function(e){return t.$refs.dropdown.toggle()}}},[e("k-color-frame",{attrs:{color:t.value,ratio:"1/1"}})],1),e("k-dropdown-content",{ref:"dropdown",attrs:{"align-x":"end",options:[{text:t.$t("field.blocks.figure.back.plain"),click:"var(--color-white)"},{text:t.$t("field.blocks.figure.back.pattern.light"),click:"var(--pattern-light)"},{text:t.$t("field.blocks.figure.back.pattern.dark"),click:"var(--pattern)"}]},on:{action:function(e){return t.$emit("input",e)}}})],1)}),[]).exports;const oi=ct({inheritAttrs:!1,props:{back:String,caption:String,captionMarks:{default:!0,type:[Boolean,Array]},disabled:Boolean,isEmpty:Boolean,emptyIcon:String,emptyText:String},emits:["open","update"]},(function(){var t=this,e=t._self._c;return e("figure",{staticClass:"k-block-figure",style:{"--block-figure-back":t.back},attrs:{"data-empty":t.isEmpty}},[t.isEmpty?e("k-button",{staticClass:"k-block-figure-empty",attrs:{disabled:t.disabled,icon:t.emptyIcon,text:t.emptyText},on:{click:function(e){return t.$emit("open")}}}):e("span",{staticClass:"k-block-figure-container",attrs:{"data-disabled":t.disabled},on:{dblclick:function(e){return t.$emit("open")}}},[t._t("default")],2),t.caption?e("k-block-figure-caption",{attrs:{disabled:t.disabled,marks:t.captionMarks,value:t.caption},on:{input:function(e){return t.$emit("update",{caption:e})}}}):t._e()],1)}),[]).exports;const ai=ct({props:{disabled:Boolean,marks:[Array,Boolean],value:String}},(function(){var t=this,e=t._self._c;return e("figcaption",{staticClass:"k-block-figure-caption"},[e("k-writer",{attrs:{disabled:t.disabled,inline:!0,marks:t.marks,spellcheck:!1,value:t.value},on:{input:function(e){return t.$emit("input",e)}}})],1)}),[]).exports;const ri=ct({extends:Ge,computed:{placeholder(){return this.field("code",{}).placeholder},languages(){return this.field("language",{options:[]}).options}},methods:{focus(){this.$refs.code.focus()}}},(function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-block-type-code-editor"},[e("k-input",{ref:"code",attrs:{buttons:!1,disabled:t.disabled,placeholder:t.placeholder,spellcheck:!1,value:t.content.code,font:"monospace",type:"textarea"},on:{input:function(e){return t.update({code:e})}}}),t.languages.length?e("div",{staticClass:"k-block-type-code-editor-language"},[e("k-input",{ref:"language",attrs:{disabled:t.disabled,empty:!1,options:t.languages,value:t.content.language,icon:"code",type:"select"},on:{input:function(e){return t.update({language:e})}}})],1):t._e()],1)}),[]).exports;const li=ct({extends:Ge,props:{tabs:Object},data(){return{collapsed:this.state(),tab:Object.keys(this.tabs)[0]}},computed:{fields(){var t;return null==(t=this.tabs[this.tab])?void 0:t.fields},values(){return Object.assign({},this.content)}},methods:{open(){this.$emit("open",this.tab)},state(t){const e=`kirby.fieldsBlock.${this.endpoints.field}.${this.id}`;if(void 0===t)return JSON.parse(sessionStorage.getItem(e));sessionStorage.setItem(e,t)},toggle(){this.collapsed=!this.collapsed,this.state(this.collapsed)}}},(function(){var t=this,e=t._self._c;return e("div",{attrs:{"data-collapsed":t.collapsed},on:{dblclick:function(e){!t.fieldset.wysiwyg&&t.$emit("open")}}},[e("header",{staticClass:"k-block-type-fields-header"},[e("k-block-title",{attrs:{content:t.values,fieldset:t.fieldset},nativeOn:{click:function(e){return t.toggle.apply(null,arguments)}}}),t.collapsed?t._e():e("k-drawer-tabs",{attrs:{tab:t.tab,tabs:t.fieldset.tabs},on:{open:function(e){t.tab=e}}})],1),t.collapsed?t._e():e("k-form",{ref:"form",staticClass:"k-block-type-fields-form",attrs:{autofocus:!0,disabled:t.disabled||!t.fieldset.wysiwyg,fields:t.fields,value:t.values},on:{input:function(e){return t.$emit("update",e)}}})],1)}),[]).exports;const ci=ct({extends:Ge,data(){return{back:this.onBack()??"white"}},computed:{captionMarks(){return this.field("caption",{marks:!0}).marks},crop(){return this.content.crop},isEmpty(){var t;return!(null==(t=this.content.images)?void 0:t.length)},ratio(){return this.content.ratio}},methods:{onBack(t){const e=`kirby.galleryBlock.${this.endpoints.field}.${this.id}`;if(void 0===t)return sessionStorage.getItem(e);this.back=t,sessionStorage.setItem(e,t)}}},(function(){var t=this,e=t._self._c;return e("figure",{staticClass:"k-block-type-gallery-figure",style:{"--block-back":t.back},attrs:{"data-empty":t.isEmpty}},[e("ul",{on:{dblclick:t.open}},[t.isEmpty?t._l(3,(function(i){return e("li",{key:i,staticClass:"k-block-type-gallery-placeholder"},[e("k-image-frame",{staticClass:"k-block-type-gallery-frame",attrs:{ratio:t.ratio}})],1)})):[t._l(t.content.images,(function(i){return e("li",{key:i.id},[e("k-image-frame",{staticClass:"k-block-type-gallery-frame",attrs:{ratio:t.ratio,cover:t.crop,src:i.url,srcset:i.image.srcset,alt:i.alt}})],1)})),e("k-block-background-dropdown",{attrs:{value:t.back},on:{input:t.onBack}})]],2),t.content.caption?e("k-block-figure-caption",{attrs:{disabled:t.disabled,marks:t.captionMarks,value:t.content.caption},on:{input:function(e){return t.$emit("update",{caption:e})}}}):t._e()],1)}),[]).exports;const ui=ct({extends:Ge,inheritAttrs:!1,emits:["append","open","split","update"],computed:{isSplitable(){return this.content.text.length>0&&!1===this.$refs.input.isCursorAtStart&&!1===this.$refs.input.isCursorAtEnd},keys(){return{Enter:()=>!0===this.$refs.input.isCursorAtEnd?this.$emit("append","text"):this.split(),"Mod-Enter":this.split}},levels(){return this.field("level",{options:[]}).options},textField(){return this.field("text",{marks:!0})}},methods:{focus(){this.$refs.input.focus()},merge(t){this.update({text:t.map((t=>t.content.text)).join(" ")})},split(){var t,e;const i=null==(e=(t=this.$refs.input).getSplitContent)?void 0:e.call(t);i&&this.$emit("split",[{text:i[0]},{level:"h"+Math.min(parseInt(this.content.level.slice(1))+1,6),text:i[1]}])}}},(function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-block-type-heading-input",attrs:{"data-level":t.content.level}},[e("k-writer",t._b({ref:"input",attrs:{disabled:t.disabled,inline:!0,keys:t.keys,value:t.content.text},on:{input:function(e){return t.update({text:e})}}},"k-writer",t.textField,!1)),t.levels.length>1?e("k-input",{ref:"level",staticClass:"k-block-type-heading-level",attrs:{disabled:t.disabled,empty:!1,options:t.levels,value:t.content.level,type:"select"},on:{input:function(e){return t.update({level:e})}}}):t._e()],1)}),[]).exports;const di=ct({extends:Ge,data(){return{back:this.onBack()??"white"}},computed:{captionMarks(){return this.field("caption",{marks:!0}).marks},crop(){return this.content.crop??!1},src(){var t,e;return"web"===this.content.location?this.content.src:!!(null==(e=null==(t=this.content.image)?void 0:t[0])?void 0:e.url)&&this.content.image[0].url},ratio(){return this.content.ratio??!1}},methods:{onBack(t){const e=`kirby.imageBlock.${this.endpoints.field}.${this.id}`;if(void 0===t)return sessionStorage.getItem(e);this.back=t,sessionStorage.setItem(e,t)}}},(function(){var t=this,e=t._self._c;return e("k-block-figure",{attrs:{back:t.back,caption:t.content.caption,"caption-marks":t.captionMarks,"empty-text":t.$t("field.blocks.image.placeholder")+" …",disabled:t.disabled,"is-empty":!t.src,"empty-icon":"image"},on:{open:t.open,update:t.update}},[t.src?[t.ratio?e("k-image-frame",{attrs:{ratio:t.ratio,cover:t.crop,alt:t.content.alt,src:t.src}}):e("img",{staticClass:"k-block-type-image-auto",attrs:{alt:t.content.alt,src:t.src}}),e("k-block-background-dropdown",{attrs:{value:t.back},on:{input:t.onBack}})]:t._e()],2)}),[]).exports;const pi=ct({},(function(){return this._self._c,this._m(0)}),[function(){var t=this._self._c;return t("div",[t("hr")])}]).exports;const hi=ct({extends:Ge,emits:["open","split","update"],computed:{isSplitable(){return this.content.text.length>0&&!1===this.input().isCursorAtStart&&!1===this.input().isCursorAtEnd},keys(){return{"Mod-Enter":this.split}},marks(){return this.field("text",{}).marks}},methods:{focus(){this.$refs.input.focus()},input(){return this.$refs.input.$refs.input.$refs.input},merge(t){this.update({text:t.map((t=>t.content.text)).join("").replaceAll("

    ","")})},split(){var t,e;const i=null==(e=(t=this.input()).getSplitContent)?void 0:e.call(t);i&&this.$emit("split",[{text:i[0].replace(/(
  • <\/p><\/li><\/ul>)$/,"

")},{text:i[1].replace(/^(
  • <\/p><\/li>)/,"

      ")}])}}},(function(){var t=this;return(0,t._self._c)("k-input",{ref:"input",staticClass:"k-block-type-list-input",attrs:{disabled:t.disabled,keys:t.keys,marks:t.marks,value:t.content.text,type:"list"},on:{input:function(e){return t.update({text:e})}}})}),[]).exports;const mi=ct({extends:Ge,computed:{placeholder(){return this.field("text",{}).placeholder}},methods:{focus(){this.$refs.input.focus()}}},(function(){var t=this;return(0,t._self._c)("k-input",{ref:"input",staticClass:"k-block-type-markdown-input",attrs:{buttons:!1,disabled:t.disabled,placeholder:t.placeholder,spellcheck:!1,value:t.content.text,font:"monospace",type:"textarea"},on:{input:function(e){return t.update({text:e})}}})}),[]).exports;const fi=ct({extends:Ge,computed:{citationField(){return this.field("citation",{})},textField(){return this.field("text",{})}},methods:{focus(){this.$refs.text.focus()}}},(function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-block-type-quote-editor"},[e("k-writer",{ref:"text",staticClass:"k-block-type-quote-text",attrs:{disabled:t.disabled,inline:t.textField.inline??!1,marks:t.textField.marks,placeholder:t.textField.placeholder,value:t.content.text},on:{input:function(e){return t.update({text:e})}}}),e("k-writer",{ref:"citation",staticClass:"k-block-type-quote-citation",attrs:{disabled:t.disabled,inline:t.citationField.inline??!0,marks:t.citationField.marks,placeholder:t.citationField.placeholder,value:t.content.citation},on:{input:function(e){return t.update({citation:e})}}})],1)}),[]).exports;const gi=ct({extends:Ge,inheritAttrs:!1,computed:{columns(){return this.table.columns??this.fields},fields(){return this.table.fields??{}},rows(){return this.content.rows??[]},table(){let t=null;for(const e of Object.values(this.fieldset.tabs??{}))e.fields.rows&&(t=e.fields.rows);return t??{}}}},(function(){var t=this;return(0,t._self._c)("k-table",{staticClass:"k-block-type-table-preview",attrs:{columns:t.columns,empty:t.$t("field.structure.empty"),rows:t.rows},nativeOn:{dblclick:function(e){return t.open.apply(null,arguments)}}})}),[]).exports;const ki=ct({extends:Ge,emits:["open","split","update"],computed:{component(){const t="k-"+this.textField.type+"-input";return this.$helper.isComponent(t)?t:"k-writer-input"},isSplitable(){return this.content.text.length>0&&!1===this.input().isCursorAtStart&&!1===this.input().isCursorAtEnd},keys(){const t={"Mod-Enter":this.split};return!0===this.textField.inline&&(t.Enter=this.split),t},textField(){return this.field("text",{})}},methods:{focus(){this.$refs.input.focus()},input(){return this.$refs.input.$refs.input},merge(t){this.update({text:t.map((t=>t.content.text)).join(this.textField.inline?" ":"")})},split(){var t,e;const i=null==(e=(t=this.input()).getSplitContent)?void 0:e.call(t);i&&("writer"===this.textField.type&&(i[0]=i[0].replace(/(

      <\/p>)$/,""),i[1]=i[1].replace(/^(

      <\/p>)/,"")),this.$emit("split",i.map((t=>({text:t})))))}}},(function(){var t=this;return(0,t._self._c)(t.component,t._b({ref:"input",tag:"component",staticClass:"k-block-type-text-input",attrs:{disabled:t.disabled,keys:t.keys,value:t.content.text},on:{input:function(e){return t.update({text:e})}}},"component",t.textField,!1))}),[]).exports;const bi=ct({extends:Ge,computed:{captionMarks(){return this.field("caption",{marks:!0}).marks},location(){return this.content.location},poster(){var t,e;return null==(e=null==(t=this.content.poster)?void 0:t[0])?void 0:e.url},video(){var t,e;return"kirby"===this.content.location?null==(e=null==(t=this.content.video)?void 0:t[0])?void 0:e.url:this.$helper.embed.video(this.content.url??"",!0)}}},(function(){var t=this,e=t._self._c;return e("k-block-figure",{staticClass:"k-block-type-video-figure",attrs:{caption:t.content.caption,"caption-marks":t.captionMarks,disabled:t.disabled,"empty-text":t.$t("field.blocks.video.placeholder")+" …","is-empty":!t.video,"empty-icon":"video"},on:{open:t.open,update:t.update}},[e("k-frame",{attrs:{ratio:"16/9"}},[t.video?["kirby"==t.location?e("video",{attrs:{src:t.video,poster:t.poster,controls:""}}):e("iframe",{attrs:{src:t.video,referrerpolicy:"strict-origin-when-cross-origin"}})]:t._e()],2)],1)}),[]).exports,vi={install(t){t.component("k-block",Qe),t.component("k-blocks",ei),t.component("k-block-options",Ze),t.component("k-block-pasteboard",ii),t.component("k-block-selector",si),t.component("k-block-background-dropdown",ni),t.component("k-block-figure",oi),t.component("k-block-figure-caption",ai),t.component("k-block-title",We),t.component("k-block-type-code",ri),t.component("k-block-type-default",Ge),t.component("k-block-type-fields",li),t.component("k-block-type-gallery",ci),t.component("k-block-type-heading",ui),t.component("k-block-type-image",di),t.component("k-block-type-line",pi),t.component("k-block-type-list",hi),t.component("k-block-type-markdown",mi),t.component("k-block-type-quote",fi),t.component("k-block-type-table",gi),t.component("k-block-type-text",ki),t.component("k-block-type-video",bi)}};const yi=ct({mixins:[Fe,ti],inheritAttrs:!1,data:()=>({opened:[]}),computed:{hasFieldsets(){return this.$helper.object.length(this.fieldsets)>0},isEmpty(){return 0===this.value.length},isFull(){return this.max&&this.value.length>=this.max},options(){return[{click:()=>this.$refs.blocks.copyAll(),disabled:this.isEmpty,icon:"template",text:this.$t("copy.all")},{click:()=>this.$refs.blocks.pasteboard(),disabled:this.isFull,icon:"download",text:this.$t("paste")},"-",{click:()=>this.$refs.blocks.removeAll(),disabled:this.isEmpty,icon:"trash",text:this.$t("delete.all")}]}},methods:{focus(){this.$refs.blocks.focus()}}},(function(){var t=this,e=t._self._c;return e("k-field",t._b({staticClass:"k-blocks-field",scopedSlots:t._u([!t.disabled&&t.hasFieldsets?{key:"options",fn:function(){return[e("k-button-group",{attrs:{layout:"collapsed"}},[e("k-button",{staticClass:"input-focus",attrs:{autofocus:t.autofocus,disabled:t.isFull,responsive:!0,text:t.$t("add"),icon:"add",variant:"filled",size:"xs"},on:{click:function(e){return t.$refs.blocks.choose(t.value.length)}}}),e("k-button",{attrs:{icon:"dots",variant:"filled",size:"xs"},on:{click:function(e){return t.$refs.options.toggle()}}}),e("k-dropdown-content",{ref:"options",attrs:{options:t.options,"align-x":"end"}})],1)]},proxy:!0}:null],null,!0)},"k-field",t.$props,!1),[e("k-blocks",t._g(t._b({ref:"blocks",on:{close:function(e){t.opened=e},open:function(e){t.opened=e}}},"k-blocks",t.$props,!1),t.$listeners)),t.disabled||t.isEmpty||t.isFull||!t.hasFieldsets?t._e():e("footer",[e("k-button",{attrs:{title:t.$t("add"),icon:"add",size:"xs",variant:"filled"},on:{click:function(e){return t.$refs.blocks.choose(t.value.length)}}})],1)],1)}),[]).exports,$i={mixins:[Ie,nt],props:{columns:{default:1,type:Number},max:Number,min:Number,theme:String,value:{type:Array,default:()=>[]}}};const wi=ct({mixins:[De,$i],data:()=>({selected:[]}),computed:{choices(){return this.options.map(((t,e)=>({autofocus:this.autofocus&&0===e,checked:this.selected.includes(t.value),disabled:this.disabled||t.disabled,id:`${this.id}-${e}`,info:t.info,label:t.text,name:this.name??this.id,type:"checkbox",value:t.value})))}},watch:{value:{handler(t){this.selected=Array.isArray(t)?t:[],this.validate()},immediate:!0}},methods:{focus(){var t;null==(t=this.$el.querySelector("input"))||t.focus()},input(t,e){if(!0===e)this.selected.push(t);else{const e=this.selected.indexOf(t);-1!==e&&this.selected.splice(e,1)}this.$emit("input",this.selected)},select(){this.focus()},validate(){this.$emit("invalid",this.$v.$invalid,this.$v)}},validations(){return{selected:{required:!this.required||t.required,min:!this.min||t.minLength(this.min),max:!this.max||t.maxLength(this.max)}}}},(function(){var t=this,e=t._self._c;return e("ul",{staticClass:"k-checkboxes-input k-grid",style:{"--columns":t.columns},attrs:{"data-variant":"choices"}},t._l(t.choices,(function(i,s){return e("li",{key:s},[e("k-choice-input",t._b({on:{input:function(e){return t.input(i.value,e)}}},"k-choice-input",i,!1))],1)})),0)}),[]).exports,xi={props:{counter:{type:Boolean,default:!0}},computed:{counterOptions(){const t=this.counterValue??this.value;return!(!1===this.counter||this.disabled||!t)&&{count:Array.isArray(t)?t.length:String(t).length,min:this.$props.min??this.$props.minlength,max:this.$props.max??this.$props.maxlength}},counterValue:()=>null}};const _i=ct({mixins:[Fe,Ue,$i,xi],inheritAttrs:!1,methods:{focus(){this.$refs.input.focus()}}},(function(){var t,e=this,i=e._self._c;return i("k-field",e._b({staticClass:"k-checkboxes-field",attrs:{input:e.id+"-0",counter:e.counterOptions}},"k-field",e.$props,!1),[(null==(t=e.options)?void 0:t.length)?i("k-checkboxes-input",e._g(e._b({ref:"input"},"k-checkboxes-input",e.$props,!1),e.$listeners)):i("k-empty",{attrs:{text:e.$t("options.none"),icon:"checklist"}})],1)}),[]).exports,Ci={mixins:[Ie,H,J,et,it,ot,at,lt],props:{ariaLabel:String,type:{default:"text",type:String},value:{type:String}}};const Si=ct({mixins:[De,Ci]},(function(){var t=this;return(0,t._self._c)("input",t._b({directives:[{name:"direction",rawName:"v-direction"}],staticClass:"k-string-input",attrs:{"aria-label":t.ariaLabel,"data-font":t.font},on:{input:function(e){return t.$emit("input",e.target.value)}}},"input",{autocomplete:t.autocomplete,autofocus:t.autofocus,disabled:t.disabled,id:t.id,maxlength:t.maxlength,minlength:t.minlength,name:t.name,pattern:t.pattern,placeholder:t.placeholder,required:t.required,spellcheck:t.spellcheck,type:t.type,value:t.value},!1))}),[]).exports,Oi={mixins:[Ci],props:{autocomplete:null,font:null,maxlength:null,minlength:null,pattern:null,spellcheck:null,alpha:{type:Boolean,default:!0},format:{type:String,default:"hex",validator:t=>["hex","rgb","hsl"].includes(t)}}};const Mi=ct({mixins:[Si,Oi],watch:{value(){this.validate()}},mounted(){this.validate()},methods:{convert(t){if(!t)return t;try{return this.$library.colors.toString(t,this.format,this.alpha)}catch{const e=document.createElement("div");return e.style.color=t,document.body.append(e),t=window.getComputedStyle(e).color,e.remove(),this.$library.colors.toString(t,this.format,this.alpha)}},convertAndEmit(t){this.emit(this.convert(t))},emit(t){this.$emit("input",t)},onBlur(){this.convertAndEmit(this.value)},onPaste(t){t instanceof ClipboardEvent&&(t=this.$helper.clipboard.read(t,!0)),this.convertAndEmit(t)},async onSave(){var t;this.convertAndEmit(this.value),await this.$nextTick(),null==(t=this.$el.form)||t.requestSubmit()},validate(){let t="";null===this.$library.colors.parse(this.value)&&(t=this.$t("error.validation.color",{format:this.format})),this.$el.setCustomValidity(t)}}},(function(){var t=this;return(0,t._self._c)("k-string-input",t._b({staticClass:"k-colorname-input",attrs:{spellcheck:!1,autocomplete:"off",type:"text"},on:{input:function(e){return t.$emit("input",e)}},nativeOn:{blur:function(e){return t.onBlur.apply(null,arguments)},paste:function(e){return t.onPaste.apply(null,arguments)},keydown:[function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"s",void 0,e.key,void 0)?null:e.metaKey?(e.stopPropagation(),e.preventDefault(),t.onSave.apply(null,arguments)):null},function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"enter",13,e.key,"Enter")?null:t.onSave.apply(null,arguments)}]}},"k-string-input",t.$props,!1))}),[]).exports;const Ai=ct({mixins:[Fe,Ue,Oi],inheritAttrs:!1,props:{icon:{type:String,default:"pipette"},mode:{type:String,default:"picker",validator:t=>["picker","input","options"].includes(t)},options:{type:Array,default:()=>[]}},computed:{convertedOptions(){return this.options.map((t=>({...t,value:this.convert(t.value)})))},currentOption(){return this.convertedOptions.find((t=>t.value===this.value))}},methods:{convert(t){return this.$library.colors.toString(t,this.format,this.alpha)}}},(function(){var t,e=this,i=e._self._c;return i("k-field",e._b({staticClass:"k-color-field",attrs:{input:e.id}},"k-field",e.$props,!1),["options"===e.mode?i("k-coloroptions-input",e._b({staticClass:"k-color-field-options",attrs:{options:e.convertedOptions},on:{input:function(t){return e.$emit("input",t)}}},"k-coloroptions-input",e.$props,!1)):i("k-input",e._b({attrs:{type:"color"},scopedSlots:e._u([{key:"before",fn:function(){return["picker"===e.mode?[i("button",{staticClass:"k-color-field-picker-toggle",attrs:{disabled:e.disabled,type:"button"},on:{click:function(t){return e.$refs.picker.toggle()}}},[i("k-color-frame",{attrs:{color:e.value}})],1),i("k-dropdown-content",{ref:"picker",staticClass:"k-color-field-picker"},[i("k-colorpicker-input",e._b({ref:"color",attrs:{options:e.convertedOptions},on:{input:function(t){return e.$emit("input",t)}},nativeOn:{click:function(t){t.stopPropagation()}}},"k-colorpicker-input",e.$props,!1))],1)]:i("k-color-frame",{attrs:{color:e.value}})]},proxy:!0},{key:"default",fn:function(){return[i("k-colorname-input",e._b({on:{input:function(t){return e.$emit("input",t)}}},"k-colorname-input",e.$props,!1))]},proxy:!0},(null==(t=e.currentOption)?void 0:t.text)?{key:"after",fn:function(){return[i("span",{domProps:{innerHTML:e._s(e.currentOption.text)}})]},proxy:!0}:null,"picker"===e.mode?{key:"icon",fn:function(){return[i("k-button",{staticClass:"k-input-icon-button",attrs:{icon:e.icon},on:{click:function(t){return t.stopPropagation(),e.$refs.picker.toggle()}}})]},proxy:!0}:null],null,!0)},"k-input",e.$props,!1))],1)}),[]).exports,Ii={props:{max:String,min:String,value:String}},Di={mixins:[Ie,Ii],props:{display:{type:String,default:"DD.MM.YYYY"},step:{type:Object,default:()=>({size:1,unit:"day"})},type:{type:String,default:"date"}}};const ji=ct({mixins:[De,Di],emits:["input","focus","submit"],data:()=>({dt:null,formatted:null}),computed:{inputType:()=>"date",pattern(){return this.$library.dayjs.pattern(this.display)},rounding(){return{...this.$options.props.step.default(),...this.step}}},watch:{value:{handler(t,e){if(t!==e){const e=this.toDatetime(t);this.commit(e)}},immediate:!0}},methods:{async alter(t){let e=this.parse()??this.round(this.$library.dayjs()),i=this.rounding.unit,s=this.rounding.size;const n=this.selection();null!==n&&("meridiem"===n.unit?(t="pm"===e.format("a")?"subtract":"add",i="hour",s=12):(i=n.unit,i!==this.rounding.unit&&(s=1))),e=e[t](s,i).round(this.rounding.unit,this.rounding.size),this.commit(e),this.emit(e),await this.$nextTick(),this.select(n)},commit(t){this.dt=t,this.formatted=this.pattern.format(t),this.validate(),this.$emit("invalid",this.$v.$invalid,this.$v)},emit(t){this.$emit("input",this.toISO(t))},onArrowDown(){this.alter("subtract")},onArrowUp(){this.alter("add")},onBlur(){const t=this.parse();this.commit(t),this.emit(t)},async onEnter(){this.onBlur(),await this.$nextTick(),this.$emit("submit")},onInput(t){const e=this.parse(),i=this.pattern.format(e);if(!t||i==t)return this.commit(e),this.emit(e)},async onTab(t){if(""==this.$el.value)return;this.onBlur(),await this.$nextTick();const e=this.selection();if(this.$el&&e.start===this.$el.selectionStart&&e.end===this.$el.selectionEnd-1)if(t.shiftKey){if(0===e.index)return;this.selectPrev(e.index)}else{if(e.index===this.pattern.parts.length-1)return;this.selectNext(e.index)}else{if(this.$el&&this.$el.selectionStart==e.end+1&&e.index==this.pattern.parts.length-1)return;if(this.$el&&this.$el.selectionEnd-1>e.end){const t=this.pattern.at(this.$el.selectionEnd,this.$el.selectionEnd);this.select(this.pattern.parts[t.index])}else this.select(this.pattern.parts[e.index])}t.preventDefault()},parse(){const t=this.$library.dayjs.interpret(this.$el.value,this.inputType);return this.round(t)},round(t){return null==t?void 0:t.round(this.rounding.unit,this.rounding.size)},select(t){var e;t??(t=this.selection()),null==(e=this.$el)||e.setSelectionRange(t.start,t.end+1)},selectFirst(){this.select(this.pattern.parts[0])},selectLast(){this.select(this.pattern.parts[this.pattern.parts.length-1])},selectNext(t){this.select(this.pattern.parts[t+1])},selectPrev(t){this.select(this.pattern.parts[t-1])},selection(){return this.pattern.at(this.$el.selectionStart,this.$el.selectionEnd)},toDatetime(t){return this.round(this.$library.dayjs.iso(t,this.inputType))},toISO(t){return null==t?void 0:t.toISO(this.inputType)},validate(){var t,e,i;const s=[];this.required&&!this.dt&&s.push(this.$t("error.validation.required")),this.min&&!1===(null==(t=this.dt)?void 0:t.validate(this.min,"min",this.rounding.unit))&&s.push(this.$t("error.validation.date.after",{date:this.min})),this.max&&!1===(null==(e=this.dt)?void 0:e.validate(this.max,"max",this.rounding.unit))&&s.push(this.$t("error.validation.date.before",{date:this.max})),null==(i=this.$el)||i.setCustomValidity(s.join(", "))}},validations(){return{value:{min:!this.dt||!this.min||(()=>this.dt.validate(this.min,"min",this.rounding.unit)),max:!this.dt||!this.max||(()=>this.dt.validate(this.max,"max",this.rounding.unit)),required:!this.required||(()=>!!this.dt)}}}},(function(){var t=this;return(0,t._self._c)("input",{directives:[{name:"direction",rawName:"v-direction"}],class:`k-text-input k-${t.type}-input`,attrs:{id:t.id,autofocus:t.autofocus,disabled:t.disabled,placeholder:t.display,required:t.required,autocomplete:"off",spellcheck:"false",type:"text"},domProps:{value:t.formatted},on:{blur:t.onBlur,focus:function(e){return t.$emit("focus")},input:function(e){return t.onInput(e.target.value)},keydown:[function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"down",40,e.key,["Down","ArrowDown"])?null:(e.stopPropagation(),e.preventDefault(),t.onArrowDown.apply(null,arguments))},function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"up",38,e.key,["Up","ArrowUp"])?null:(e.stopPropagation(),e.preventDefault(),t.onArrowUp.apply(null,arguments))},function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"enter",13,e.key,"Enter")?null:(e.stopPropagation(),e.preventDefault(),t.onEnter.apply(null,arguments))},function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"s",void 0,e.key,void 0)?null:e.metaKey?(e.stopPropagation(),e.preventDefault(),t.onEnter.apply(null,arguments)):null},function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"s",void 0,e.key,void 0)?null:e.ctrlKey?(e.stopPropagation(),e.preventDefault(),t.onEnter.apply(null,arguments)):null},function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"tab",9,e.key,"Tab")?null:t.onTab.apply(null,arguments)}]}})}),[]).exports;const Ei=ct({mixins:[Fe,Ue,Di],inheritAttrs:!1,props:{calendar:{type:Boolean,default:!0},icon:{type:String,default:"calendar"},time:{type:[Boolean,Object],default:()=>({})},times:{type:Boolean,default:!0}},emits:["input","submit"],data(){return{isInvalid:!1,iso:this.toIso(this.value)}},computed:{isEmpty(){return this.time?!this.iso.date||!this.iso.time:!this.iso.date}},watch:{value(t,e){t!==e&&(this.iso=this.toIso(t))}},methods:{focus(){this.$refs.dateInput.focus()},now(){const t=this.$library.dayjs();return{date:t.toISO("date"),time:this.time?t.toISO("time"):"00:00:00"}},onInput(){if(this.isEmpty)return this.$emit("input","");const t=this.$library.dayjs.iso(this.iso.date+" "+this.iso.time);(t||null!==this.iso.date&&null!==this.iso.time)&&this.$emit("input",(null==t?void 0:t.toISO())??"")},onDateInput(t){t&&!this.iso.time&&(this.iso.time=this.now().time),this.iso.date=t,this.onInput()},onDateInvalid(t){this.isInvalid=t},onTimeInput(t){t&&!this.iso.date&&(this.iso.date=this.now().date),this.iso.time=t,this.onInput()},onTimesInput(t){var e;null==(e=this.$refs.times)||e.close(),this.onTimeInput(t+":00")},toIso(t){const e=this.$library.dayjs.iso(t);return{date:(null==e?void 0:e.toISO("date"))??null,time:(null==e?void 0:e.toISO("time"))??null}}}},(function(){var t=this,e=t._self._c;return e("k-field",t._b({staticClass:"k-date-field",attrs:{input:t.id}},"k-field",t.$props,!1),[e("div",{ref:"body",staticClass:"k-date-field-body",attrs:{"data-has-time":Boolean(t.time),"data-invalid":!t.novalidate&&t.isInvalid}},[e("k-input",t._b({ref:"dateInput",attrs:{type:"date"},on:{invalid:t.onDateInvalid,input:t.onDateInput,submit:function(e){return t.$emit("submit")}},scopedSlots:t._u([t.calendar?{key:"icon",fn:function(){return[e("k-button",{staticClass:"k-input-icon-button",attrs:{disabled:t.disabled,icon:t.icon,title:t.$t("date.select")},on:{click:function(e){return t.$refs.calendar.toggle()}}}),e("k-dropdown-content",{ref:"calendar",attrs:{"align-x":"end"}},[e("k-calendar",{attrs:{value:t.iso.date,min:t.min,max:t.max},on:{input:t.onDateInput}})],1)]},proxy:!0}:null],null,!0)},"k-input",t.$props,!1)),t.time?e("k-input",{ref:"timeInput",attrs:{disabled:t.disabled,display:t.time.display,required:t.required,step:t.time.step,value:t.iso.time,icon:t.time.icon,type:"time"},on:{input:t.onTimeInput,submit:function(e){return t.$emit("submit")}},scopedSlots:t._u([t.times?{key:"icon",fn:function(){return[e("k-button",{staticClass:"k-input-icon-button",attrs:{disabled:t.disabled,icon:t.time.icon??"clock",title:t.$t("time.select")},on:{click:function(e){return t.$refs.times.toggle()}}}),e("k-dropdown-content",{ref:"times",attrs:{"align-x":"end"}},[e("k-timeoptions-input",{attrs:{display:t.time.display,value:t.value},on:{input:t.onTimesInput}})],1)]},proxy:!0}:null],null,!0)}):t._e()],1)])}),[]).exports,Li={mixins:[Ie,J,et,it,ot,at,lt],props:{autocomplete:{type:[Boolean,String],default:"off"},preselect:Boolean,type:{type:String,default:"text"},value:String}};const Ti=ct({mixins:[De,Li],data(){return{listeners:{...this.$listeners,input:t=>this.onInput(t.target.value)}}},watch:{value(){this.onInvalid()}},mounted(){this.onInvalid(),this.$props.autofocus&&this.focus(),this.$props.preselect&&this.select()},methods:{onInput(t){this.$emit("input",t)},onInvalid(){this.$emit("invalid",this.$v.$invalid,this.$v)},select(){this.$refs.input.select()}},validations(){return{value:{required:!this.required||t.required,minLength:!this.minlength||t.minLength(this.minlength),maxLength:!this.maxlength||t.maxLength(this.maxlength),email:"email"!==this.type||t.email,url:"url"!==this.type||t.url,pattern:!this.pattern||(t=>!this.required&&!t||!this.$refs.input.validity.patternMismatch)}}}},(function(){var t=this;return(0,t._self._c)("input",t._g(t._b({directives:[{name:"direction",rawName:"v-direction"}],ref:"input",staticClass:"k-text-input",attrs:{"data-font":t.font}},"input",{autocomplete:t.autocomplete,autofocus:t.autofocus,disabled:t.disabled,id:t.id,minlength:t.minlength,name:t.name,pattern:t.pattern,placeholder:t.placeholder,required:t.required,spellcheck:t.spellcheck,type:t.type,value:t.value},!1),t.listeners))}),[]).exports,Bi={mixins:[Li],props:{autocomplete:{type:String,default:"email"},placeholder:{type:String,default:()=>window.panel.$t("email.placeholder")},type:{type:String,default:"email"}}};const qi=ct({extends:Ti,mixins:[Bi]},null,null).exports;const Pi=ct({mixins:[Fe,Ue,Bi],inheritAttrs:!1,props:{link:{type:Boolean,default:!0},icon:{type:String,default:"email"}},computed:{mailto(){var t;return(null==(t=this.value)?void 0:t.length)>0?"mailto:"+this.value:null}},methods:{focus(){this.$refs.input.focus()}}},(function(){var t=this,e=t._self._c;return e("k-field",t._b({staticClass:"k-email-field",attrs:{input:t.id}},"k-field",t.$props,!1),[e("k-input",t._g(t._b({ref:"input",attrs:{type:"email"},scopedSlots:t._u([{key:"icon",fn:function(){return[t.link?e("k-button",{staticClass:"k-input-icon-button",attrs:{icon:t.icon,link:t.mailto,title:t.$t("open"),tabindex:"-1",target:"_blank"}}):t._e()]},proxy:!0}])},"k-input",t.$props,!1),t.$listeners))],1)}),[]).exports;const Ni=ct({type:"model",mixins:[Fe,V,tt],inheritAttrs:!1,props:{empty:String,info:String,link:Boolean,max:Number,multiple:Boolean,parent:String,search:Boolean,size:String,text:String,value:{type:Array,default:()=>[]}},emits:["change","input"],data(){return{selected:this.value}},computed:{buttons(){return[{autofocus:this.autofocus,text:this.$t("select"),icon:"checklist",responsive:!0,click:()=>this.open()}]},collection(){return{empty:this.emptyProps,items:this.selected,layout:this.layout,link:this.link,size:this.size,sortable:!this.disabled&&this.selected.length>1,theme:this.disabled?"disabled":null}},hasDropzone:()=>!1,isInvalid(){return this.required&&0===this.selected.length||this.min&&this.selected.lengththis.max},more(){return!this.max||this.max>this.selected.length}},watch:{value(t){this.selected=t}},methods:{drop(){},focus(){},onInput(){this.$emit("input",this.selected)},open(){if(this.disabled)return!1;this.$panel.dialog.open({component:`k-${this.$options.type}-dialog`,props:{endpoint:this.endpoints.field,hasSearch:this.search,max:this.max,multiple:this.multiple,value:this.selected.map((t=>t.id))},on:{submit:t=>{this.select(t),this.$panel.dialog.close()}}})},remove(t){this.selected.splice(t,1),this.onInput()},removeById(t){this.selected=this.selected.filter((e=>e.id!==t)),this.onInput()},select(t){if(0===t.length)return this.selected=[],void this.onInput();this.selected=this.selected.filter((e=>t.find((t=>t.id===e.id))));for(const e of t)this.selected.find((t=>e.id===t.id))||this.selected.push(e);this.onInput()}}},(function(){var t=this,e=t._self._c;return e("k-field",t._b({class:`k-models-field k-${t.$options.type}-field`,scopedSlots:t._u([t.disabled?null:{key:"options",fn:function(){return[e("k-button-group",{ref:"buttons",staticClass:"k-field-options",attrs:{buttons:t.buttons,layout:"collapsed",size:"xs",variant:"filled"}})]},proxy:!0}],null,!0)},"k-field",t.$props,!1),[e("k-dropzone",{attrs:{disabled:!t.hasDropzone},on:{drop:t.drop}},[e("k-collection",t._b({on:{empty:t.open,sort:t.onInput,sortChange:function(e){return t.$emit("change",e)}},scopedSlots:t._u([t.disabled?null:{key:"options",fn:function({index:i}){return[e("k-button",{attrs:{title:t.$t("remove"),icon:"remove"},on:{click:function(e){return t.remove(i)}}})]}}],null,!0)},"k-collection",t.collection,!1))],1)],1)}),[]).exports;const zi=ct({extends:Ni,type:"files",props:{uploads:[Boolean,Object,Array]},computed:{buttons(){const t=Ni.computed.buttons.call(this);return this.hasDropzone&&t.unshift({autofocus:this.autofocus,text:this.$t("upload"),responsive:!0,icon:"upload",click:()=>this.$panel.upload.pick(this.uploadOptions)}),t},emptyProps(){return{icon:"image",text:this.empty??(this.multiple&&1!==this.max?this.$t("field.files.empty"):this.$t("field.files.empty.single"))}},hasDropzone(){return!this.disabled&&this.more&&this.uploads},uploadOptions(){return{accept:this.uploads.accept,max:this.max,multiple:this.multiple,preview:this.uploads.preview,url:this.$panel.urls.api+"/"+this.endpoints.field+"/upload",on:{done:t=>{!1===this.multiple&&(this.selected=[]);for(const e of t)void 0===this.selected.find((t=>t.id===e.id))&&this.selected.push(e);this.onInput(),this.$events.emit("file.upload"),this.$events.emit("model.update")}}}}},mounted(){this.$events.on("file.delete",this.removeById)},destroyed(){this.$events.off("file.delete",this.removeById)},methods:{drop(t){return!1!==this.uploads&&this.$panel.upload.open(t,this.uploadOptions)}}},null,null).exports;const Fi=ct({},(function(){return(0,this._self._c)("div",{staticClass:"k-field k-gap-field"})}),[]).exports;const Yi=ct({mixins:[G,Q],inheritAttrs:!1},(function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-headline-field"},[e("k-headline",{staticClass:"h2"},[t._v(" "+t._s(t.label)+" ")]),t.help?e("footer",{staticClass:"k-field-footer"},[e("k-text",{staticClass:"k-help k-field-help",attrs:{html:t.help}})],1):t._e()],1)}),[]).exports;const Ri=ct({mixins:[G,Q],props:{icon:String,text:String,theme:{type:String,default:"info"}}},(function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-field k-info-field"},[t.label?e("k-headline",[t._v(t._s(t.label))]):t._e(),e("k-box",{attrs:{icon:t.icon,theme:t.theme}},[e("k-text",{attrs:{html:t.text}})],1),t.help?e("footer",{staticClass:"k-field-footer"},[e("k-text",{staticClass:"k-help k-field-help",attrs:{html:t.help}})],1):t._e()],1)}),[]).exports,Ui={props:{endpoints:Object,fieldsetGroups:Object,fieldsets:Object,id:String,isSelected:Boolean}};const Hi=ct({mixins:[Ui],props:{blocks:Array,width:{type:String,default:"1/1"}},emits:["input"]},(function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-column k-layout-column",style:{"--width":t.width},attrs:{id:t.id,tabindex:"0"},on:{dblclick:function(e){return t.$refs.blocks.choose(t.blocks.length)}}},[e("k-blocks",t._b({ref:"blocks",on:{input:function(e){return t.$emit("input",e)}},nativeOn:{dblclick:function(t){t.stopPropagation()}}},"k-blocks",{endpoints:t.endpoints,fieldsets:t.fieldsets,fieldsetGroups:t.fieldsetGroups,group:"layout",value:t.blocks},!1))],1)}),[]).exports,Vi={mixins:[Ui,W],props:{columns:Array,layouts:{type:Array,default:()=>[["1/1"]]},settings:Object}};const Ki=ct({mixins:[Vi],props:{attrs:[Array,Object]},emits:["append","change","copy","duplicate","prepend","remove","select","updateAttrs","updateColumn"],computed:{options(){return[{click:()=>this.$emit("prepend"),icon:"angle-up",text:this.$t("insert.before")},{click:()=>this.$emit("append"),icon:"angle-down",text:this.$t("insert.after")},"-",{click:()=>this.openSettings(),icon:"settings",text:this.$t("settings"),when:!1===this.$helper.object.isEmpty(this.settings)},{click:()=>this.$emit("duplicate"),icon:"copy",text:this.$t("duplicate")},{click:()=>this.$emit("change"),disabled:1===this.layouts.length,icon:"dashboard",text:this.$t("field.layout.change")},"-",{click:()=>this.$emit("copy"),icon:"template",text:this.$t("copy")},{click:()=>this.$emit("paste"),icon:"download",text:this.$t("paste.after")},"-",{click:()=>this.remove(),icon:"trash",text:this.$t("field.layout.delete")}]},tabs(){let t=this.settings.tabs;for(const[e,i]of Object.entries(t))for(const s in i.fields)t[e].fields[s].endpoints={field:this.endpoints.field+"/fields/"+s,section:this.endpoints.section,model:this.endpoints.model};return t}},methods:{openSettings(){this.$panel.drawer.open({component:"k-form-drawer",props:{icon:"settings",tabs:this.tabs,title:this.$t("settings"),value:this.attrs},on:{input:t=>this.$emit("updateAttrs",t)}})},remove(){this.$panel.dialog.open({component:"k-remove-dialog",props:{text:this.$t("field.layout.delete.confirm")},on:{submit:()=>{this.$emit("remove"),this.$panel.dialog.close()}}})}}},(function(){var t=this,e=t._self._c;return e("section",{staticClass:"k-layout",attrs:{"data-selected":t.isSelected,tabindex:"0"},on:{click:function(e){return t.$emit("select")}}},[e("k-grid",{staticClass:"k-layout-columns"},t._l(t.columns,(function(i,s){return e("k-layout-column",t._b({key:i.id,on:{input:function(e){return t.$emit("updateColumn",{column:i,columnIndex:s,blocks:e})}}},"k-layout-column",{...i,endpoints:t.endpoints,fieldsetGroups:t.fieldsetGroups,fieldsets:t.fieldsets},!1))})),1),t.disabled?t._e():e("nav",{staticClass:"k-layout-toolbar"},[t.settings?e("k-button",{staticClass:"k-layout-toolbar-button",attrs:{title:t.$t("settings"),icon:"settings"},on:{click:t.openSettings}}):t._e(),e("k-button",{staticClass:"k-layout-toolbar-button",attrs:{icon:"angle-down"},on:{click:function(e){return t.$refs.options.toggle()}}}),e("k-dropdown-content",{ref:"options",attrs:{options:t.options,"align-x":"end"}}),e("k-sort-handle")],1)],1)}),[]).exports,Wi={mixins:[Vi,X],props:{empty:String,max:Number,selector:Object,value:{type:Array,default:()=>[]}}};const Ji=ct({mixins:[Wi],emits:["input"],data(){return{current:null,nextIndex:null,rows:this.value,selected:null}},computed:{draggableOptions(){return{id:this.id,handle:!0,list:this.rows}},hasFieldsets(){return this.$helper.object.length(this.fieldsets)>0}},watch:{value(){this.rows=this.value}},methods:{copy(t,e){if(0===this.rows.length)return!1;const i=void 0!==e?this.rows[e]:this.rows;this.$helper.clipboard.write(JSON.stringify(i),t),this.$panel.notification.success({message:this.$t("copy.success",{count:i.length??1}),icon:"template"})},change(t,e){const i=e.columns.map((t=>t.width)),s=this.layouts.findIndex((t=>t.toString()===i.toString()));this.$panel.dialog.open({component:"k-layout-selector",props:{label:this.$t("field.layout.change"),layouts:this.layouts,selector:this.selector,value:this.layouts[s]},on:{submit:i=>{this.onChange(i,s,{rowIndex:t,layoutIndex:s,layout:e}),this.$panel.dialog.close()}}})},duplicate(t,e){const i=structuredClone(e),s=this.updateIds(i);this.rows.splice(t+1,0,...s),this.save()},async onAdd(t){let e=await this.$api.post(this.endpoints.field+"/layout",{columns:t});this.rows.splice(this.nextIndex,0,e),this.save()},async onChange(t,e,i){if(e===this.layouts[i.layoutIndex])return;const s=i.layout,n=await this.$api.post(this.endpoints.field+"/layout",{attrs:s.attrs,columns:t}),o=s.columns.filter((t=>{var e;return(null==(e=null==t?void 0:t.blocks)?void 0:e.length)>0})),a=[];if(0===o.length)a.push(n);else{const t=Math.ceil(o.length/n.columns.length)*n.columns.length;for(let e=0;e{var s;return t.blocks=(null==(s=o[i+e])?void 0:s.blocks)??[],t})),t.columns.filter((t=>{var e;return null==(e=null==t?void 0:t.blocks)?void 0:e.length})).length&&a.push(t)}}this.rows.splice(i.rowIndex,1,...a),this.save()},async paste(t,e=this.rows.length){let i=await this.$api.post(this.endpoints.field+"/layout/paste",{json:this.$helper.clipboard.read(t)});i.length&&(this.rows.splice(e,0,...i),this.save()),this.$panel.notification.success({message:this.$t("paste.success",{count:i.length}),icon:"download"})},pasteboard(t){this.$panel.dialog.open({component:"k-block-pasteboard",on:{paste:e=>this.paste(e,t)}})},remove(t){const e=this.rows.findIndex((e=>e.id===t.id));-1!==e&&this.$delete(this.rows,e),this.save()},removeAll(){this.$panel.dialog.open({component:"k-remove-dialog",props:{text:this.$t("field.layout.delete.confirm.all")},on:{submit:()=>{this.rows=[],this.save(),this.$panel.dialog.close()}}})},save(){this.$emit("input",this.rows)},select(t){if(this.nextIndex=t,1===this.layouts.length)return this.onAdd(this.layouts[0]);this.$panel.dialog.open({component:"k-layout-selector",props:{layouts:this.layouts,selector:this.selector,value:null},on:{submit:t=>{this.onAdd(t),this.$panel.dialog.close()}}})},updateAttrs(t,e){this.rows[t].attrs=e,this.save()},updateColumn(t){this.rows[t.index].columns[t.columnIndex].blocks=t.blocks,this.save()},updateIds(t){return!1===Array.isArray(t)&&(t=[t]),t.map((t=>(t.id=this.$helper.uuid(),t.columns=t.columns.map((t=>(t.id=this.$helper.uuid(),t.blocks=t.blocks.map((t=>(t.id=this.$helper.uuid(),t))),t))),t)))}}},(function(){var t=this,e=t._self._c;return e("div",[t.hasFieldsets&&t.rows.length?[e("k-draggable",t._b({staticClass:"k-layouts",on:{sort:t.save}},"k-draggable",t.draggableOptions,!1),t._l(t.rows,(function(i,s){return e("k-layout",t._b({key:i.id,on:{append:function(e){return t.select(s+1)},change:function(e){return t.change(s,i)},copy:function(e){return t.copy(e,s)},duplicate:function(e){return t.duplicate(s,i)},paste:function(e){return t.pasteboard(s+1)},prepend:function(e){return t.select(s)},remove:function(e){return t.remove(i)},select:function(e){t.selected=i.id},updateAttrs:function(e){return t.updateAttrs(s,e)},updateColumn:function(e){return t.updateColumn({layout:i,index:s,...e})}}},"k-layout",{...i,disabled:t.disabled,endpoints:t.endpoints,fieldsetGroups:t.fieldsetGroups,fieldsets:t.fieldsets,isSelected:t.selected===i.id,layouts:t.layouts,settings:t.settings},!1))})),1)]:!1===t.hasFieldsets?e("k-empty",{staticClass:"k-layout-empty",attrs:{icon:"dashboard"}},[t._v(" "+t._s(t.$t("field.blocks.fieldsets.empty"))+" ")]):e("k-empty",{staticClass:"k-layout-empty",attrs:{icon:"dashboard"},on:{click:function(e){return t.select(0)}}},[t._v(" "+t._s(t.empty??t.$t("field.layout.empty"))+" ")])],2)}),[]).exports;const Gi=ct({mixins:[Fe,Wi,V],inheritAttrs:!1,computed:{hasFieldsets(){return this.$helper.object.length(this.fieldsets)>0},isEmpty(){return 0===this.value.length},options(){return[{click:()=>this.$refs.layouts.copy(),disabled:this.isEmpty,icon:"template",text:this.$t("copy.all")},{click:()=>this.$refs.layouts.pasteboard(),icon:"download",text:this.$t("paste")},"-",{click:()=>this.$refs.layouts.removeAll(),disabled:this.isEmpty,icon:"trash",text:this.$t("delete.all")}]}}},(function(){var t=this,e=t._self._c;return e("k-field",t._b({staticClass:"k-layout-field",scopedSlots:t._u([!t.disabled&&t.hasFieldsets?{key:"options",fn:function(){return[e("k-button-group",{attrs:{layout:"collapsed"}},[e("k-button",{staticClass:"input-focus",attrs:{autofocus:t.autofocus,text:t.$t("add"),icon:"add",variant:"filled",size:"xs"},on:{click:function(e){return t.$refs.layouts.select(0)}}}),e("k-button",{attrs:{icon:"dots",variant:"filled",size:"xs"},on:{click:function(e){return t.$refs.options.toggle()}}}),e("k-dropdown-content",{ref:"options",attrs:{options:t.options,"align-x":"end"}})],1)]},proxy:!0}:null],null,!0)},"k-field",t.$props,!1),[e("k-layouts",t._b({ref:"layouts",on:{input:function(e){return t.$emit("input",e)}}},"k-layouts",t.$props,!1)),!t.disabled&&t.hasFieldsets?e("footer",[e("k-button",{attrs:{title:t.$t("add"),icon:"add",size:"xs",variant:"filled"},on:{click:function(e){return t.$refs.layouts.select(t.value.length)}}})],1):t._e()],1)}),[]).exports;const Xi=ct({},(function(){return(0,this._self._c)("hr",{staticClass:"k-line-field"})}),[]).exports;const Zi=ct({mixins:[{mixins:[Fe,Ue,Ie,nt],props:{value:{default:"",type:String}}}],inheritAttrs:!1,data:()=>({linkType:null,linkValue:null,expanded:!1,isInvalid:!1}),computed:{activeTypes(){return this.$helper.link.types(this.options)},activeTypesOptions(){const t=[];for(const e in this.activeTypes)t.push({click:()=>this.switchType(e),current:e===this.currentType.id,icon:this.activeTypes[e].icon,label:this.activeTypes[e].label});return t},currentType(){return this.activeTypes[this.linkType]??Object.values(this.activeTypes)[0]}},watch:{value:{async handler(t,e){if(t===e||t===this.linkValue)return;const i=this.$helper.link.detect(t,this.activeTypes);i&&(this.linkType=i.type,this.linkValue=i.link)},immediate:!0}},mounted(){this.$events.on("click",this.onOutsideClick)},destroyed(){this.$events.off("click",this.onOutsideClick)},methods:{clear(){this.linkValue="",this.$emit("input","")},focus(){var t;null==(t=this.$refs.input)||t.focus()},onInput(t){const e=(null==t?void 0:t.trim())??"";if(this.linkType??(this.linkType=this.currentType.id),this.linkValue=e,!e.length)return this.clear();this.$emit("input",this.currentType.value(e))},onInvalid(t){this.isInvalid=!!t},onOutsideClick(t){!1===this.$el.contains(t.target)&&(this.expanded=!1)},removeModel(){this.clear(),this.expanded=!1},selectModel(t){t.uuid?this.onInput(t.uuid):(this.switchType("url"),this.onInput(t.url))},async switchType(t){t!==this.currentType.id&&(this.isInvalid=!1,this.linkType=t,this.clear(),"page"===this.currentType.id||"file"===this.currentType.id?this.expanded=!0:this.expanded=!1,await this.$nextTick(),this.focus())},toggle(){this.expanded=!this.expanded}}},(function(){var t=this,e=t._self._c;return e("k-field",t._b({staticClass:"k-link-field",attrs:{input:t.id}},"k-field",t.$props,!1),[e("k-input",t._b({attrs:{invalid:t.isInvalid,icon:!1}},"k-input",t.$props,!1),[e("div",{staticClass:"k-link-input-header"},[e("k-button",{staticClass:"k-link-input-toggle",attrs:{disabled:t.disabled,dropdown:!t.disabled&&t.activeTypesOptions.length>1,icon:t.currentType.icon,variant:"filled"},on:{click:function(e){t.activeTypesOptions.length>1?t.$refs.types.toggle():t.toggle()}}},[t._v(" "+t._s(t.currentType.label)+" ")]),e("k-dropdown-content",{ref:"types",attrs:{options:t.activeTypesOptions}}),"page"===t.currentType.id||"file"===t.currentType.id?e("div",{staticClass:"k-link-input-model",on:{click:t.toggle}},[e("k-link-field-preview",{attrs:{removable:!0,type:t.currentType.id,value:t.value},on:{remove:t.removeModel},scopedSlots:t._u([{key:"placeholder",fn:function(){return[e("k-button",{staticClass:"k-link-input-model-placeholder"},[t._v(" "+t._s(t.currentType.placeholder)+" ")])]},proxy:!0}],null,!1,3171606015)}),e("k-button",{staticClass:"k-link-input-model-toggle",attrs:{icon:"bars"}})],1):e("k-"+t.currentType.input+"-input",{ref:"input",tag:"component",attrs:{id:t.id,disabled:t.disabled,pattern:t.currentType.pattern??null,placeholder:t.currentType.placeholder,value:t.linkValue},on:{invalid:t.onInvalid,input:t.onInput}})],1),"page"===t.currentType.id?e("div",{directives:[{name:"show",rawName:"v-show",value:t.expanded,expression:"expanded"}],staticClass:"k-link-input-body",attrs:{"data-type":"page"}},[e("div",{staticClass:"k-page-browser"},[e("k-page-tree",{attrs:{current:t.$helper.link.getPageUUID(t.value),root:!1},on:{select:function(e){return t.selectModel(e)}}})],1)]):"file"===t.currentType.id?e("div",{directives:[{name:"show",rawName:"v-show",value:t.expanded,expression:"expanded"}],staticClass:"k-link-input-body",attrs:{"data-type":"file"}},[e("k-file-browser",{attrs:{opened:t.$panel.view.props.model.uuid??t.$panel.view.props.model.id,selected:t.$helper.link.getFileUUID(t.value)},on:{select:function(e){return t.selectModel(e)}}})],1):t._e()])],1)}),[]).exports;const Qi=t=>({$from:e})=>((t,e)=>{for(let i=t.depth;i>0;i--){const s=t.node(i);if(e(s))return{pos:i>0?t.before(i):0,start:t.start(i),depth:i,node:s}}})(e,t),ts=t=>e=>{if((t=>t instanceof o)(e)){const{node:i,$from:s}=e;if(((t,e)=>Array.isArray(t)&&t.indexOf(e.type)>-1||e.type===t)(t,i))return{node:i,pos:s.pos,depth:s.depth}}},es=(t,e,i={})=>{const s=ts(e)(t.selection)||Qi((t=>t.type===e))(t.selection);return 0!==yt(i)&&s?s.node.hasMarkup(e,{...s.node.attrs,...i}):!!s};function is(t=null,e=null){if(!t||!e)return!1;const i=t.parent.childAfter(t.parentOffset);if(!i.node)return!1;const s=i.node.marks.find((t=>t.type===e));if(!s)return!1;let n=t.index(),o=t.start()+i.offset,a=n+1,r=o+i.node.nodeSize;for(;n>0&&s.isInSet(t.parent.child(n-1).marks);)n-=1,o-=t.parent.child(n).nodeSize;for(;a{n=[...n,...t.marks]}));const o=n.find((t=>t.type.name===e.name));return o?o.attrs:{}},getNodeAttrs:function(t,e){const{from:i,to:s}=t.selection;let n=[];t.doc.nodesBetween(i,s,(t=>{n=[...n,t]}));const o=n.reverse().find((t=>t.type.name===e.name));return o?o.attrs:{}},insertNode:function(t,e,i,s){return(n,o)=>{o(n.tr.replaceSelectionWith(t.create(e,i,s)).scrollIntoView())}},markInputRule:function(t,i,s){return new e(t,((t,e,n,o)=>{const a=s instanceof Function?s(e):s,{tr:r}=t,l=e.length-1;let c=o,u=n;if(e[l]){const s=n+e[0].indexOf(e[l-1]),a=s+e[l-1].length-1,d=s+e[l-1].lastIndexOf(e[l]),p=d+e[l].length,h=function(t,e,i){let s=[];return i.doc.nodesBetween(t,e,((t,e)=>{s=[...s,...t.marks.map((i=>({start:e,end:e+t.nodeSize,mark:i})))]})),s}(n,o,t).filter((t=>{const{excluded:e}=t.mark.type;return e.find((t=>t.name===i.name))})).filter((t=>t.end>s));if(h.length)return!1;ps&&r.delete(s,d),u=s,c=u+e[l].length}return r.addMark(u,c,i.create(a)),r.removeStoredMark(i),r}))},markIsActive:function(t,e){const{from:i,$from:s,to:n,empty:o}=t.selection;return o?!!e.isInSet(t.storedMarks||s.marks()):!!t.doc.rangeHasMark(i,n,e)},markPasteRule:function(t,e,o){const a=(i,s)=>{const r=[];return i.forEach((i=>{var n;if(i.isText){const{text:a,marks:l}=i;let c,u=0;const d=!!l.filter((t=>"link"===t.type.name))[0];for(;!d&&null!==(c=t.exec(a));)if((null==(n=null==s?void 0:s.type)?void 0:n.allowsMarkType(e))&&c[1]){const t=c.index,s=t+c[0].length,n=t+c[0].indexOf(c[1]),a=n+c[1].length,l=o instanceof Function?o(c):o;t>0&&r.push(i.cut(u,t)),r.push(i.cut(n,a).mark(e.create(l).addToSet(i.marks))),u=s}unew s(a(t.content),t.openStart,t.openEnd)}})},minMax:function(t=0,e=0,i=0){return Math.min(Math.max(parseInt(t,10),e),i)},nodeIsActive:es,nodeInputRule:function(t,i,s){return new e(t,((t,e,n,o)=>{const a=s instanceof Function?s(e):s,{tr:r}=t;return e[0]&&r.replaceWith(n,o,i.create(a)),r}))},pasteRule:function(t,e,o){const a=i=>{const s=[];return i.forEach((i=>{if(i.isText){const{text:n}=i;let a,r=0;do{if(a=t.exec(n),a){const t=a.index,n=t+a[0].length,l=o instanceof Function?o(a[0]):o;t>0&&s.push(i.cut(r,t)),s.push(i.cut(t,n).mark(e.create(l).addToSet(i.marks))),r=n}}while(a);rnew s(a(t.content),t.openStart,t.openEnd)}})},removeMark:function(t){return(e,i)=>{const{tr:s,selection:n}=e;let{from:o,to:a}=n;const{$from:r,empty:l}=n;if(l){const e=is(r,t);o=e.from,a=e.to}return s.removeMark(o,a,t),i(s)}},toggleBlockType:function(t,e,i={}){return(s,n,o)=>es(s,t,i)?a(e)(s,n,o):a(t,i)(s,n,o)},toggleList:function(t,e){return(i,s,n)=>{const{schema:o,selection:a}=i,{$from:c,$to:u}=a,d=c.blockRange(u);if(!d)return!1;const p=Qi((t=>ss(t,o)))(a);if(d.depth>=1&&p&&d.depth-p.depth<=1){if(p.node.type===t)return r(e)(i,s,n);if(ss(p.node,o)&&t.validContent(p.node.content)){const{tr:e}=i;return e.setNodeMarkup(p.pos,t),s&&s(e),!1}}return l(t)(i,s,n)}},toggleWrap:function(t,e={}){return(i,s,n)=>es(i,t,e)?c(i,s):u(t,e)(i,s,n)},updateMark:function(t,e){return(i,s)=>{const{tr:n,selection:o,doc:a}=i,{ranges:r,empty:l}=o;if(l){const{from:i,to:s}=is(o.$from,t);a.rangeHasMark(i,s,t)&&n.removeMark(i,s,t),n.addMark(i,s,t.create(e))}else r.forEach((i=>{const{$to:s,$from:o}=i;a.rangeHasMark(o.pos,s.pos,t)&&n.removeMark(o.pos,s.pos,t),n.addMark(o.pos,s.pos,t.create(e))}));return s(n)}}};class os{emit(t,...e){this._callbacks=this._callbacks??{};const i=this._callbacks[t]??[];for(const s of i)s.apply(this,e);return this}off(t,e){if(arguments.length){const i=this._callbacks?this._callbacks[t]:null;i&&(e?this._callbacks[t]=i.filter((t=>t!==e)):delete this._callbacks[t])}else this._callbacks={};return this}on(t,e){return this._callbacks=this._callbacks??{},this._callbacks[t]=this._callbacks[t]??[],this._callbacks[t].push(e),this}}class as{constructor(t=[],e){for(const i of t)i.bindEditor(e),i.init();this.extensions=t}commands({schema:t,view:e}){return this.extensions.filter((t=>t.commands)).reduce(((i,s)=>{const{name:n,type:o}=s,a={},r=s.commands({schema:t,utils:ns,...["node","mark"].includes(o)?{type:t[`${o}s`][n]}:{}}),l=(t,i)=>{a[t]=t=>{if("function"!=typeof i||!e.editable)return!1;e.focus();const s=i(t);return"function"==typeof s?s(e.state,e.dispatch,e):s}};if("object"==typeof r)for(const[t,e]of Object.entries(r))l(t,e);else l(n,r);return{...i,...a}}),{})}buttons(t="mark"){const e={};for(const i of this.extensions)if(i.type===t&&i.button)if(Array.isArray(i.button))for(const t of i.button)e[t.id??t.name]=t;else e[i.name]=i.button;return e}getAllowedExtensions(t){return t instanceof Array||!t?t instanceof Array?this.extensions.filter((e=>!t.includes(e.name))):this.extensions:[]}getFromExtensions(t,e,i=this.extensions){return i.filter((t=>["extension"].includes(t.type))).filter((e=>e[t])).map((i=>i[t]({...e,utils:ns})))}getFromNodesAndMarks(t,e,i=this.extensions){return i.filter((t=>["node","mark"].includes(t.type))).filter((e=>e[t])).map((i=>i[t]({...e,type:e.schema[`${i.type}s`][i.name],utils:ns})))}inputRules({schema:t,excludedExtensions:e}){const i=this.getAllowedExtensions(e);return[...this.getFromExtensions("inputRules",{schema:t},i),...this.getFromNodesAndMarks("inputRules",{schema:t},i)].reduce(((t,e)=>[...t,...e]),[])}keymaps({schema:t}){return[...this.getFromExtensions("keys",{schema:t}),...this.getFromNodesAndMarks("keys",{schema:t})].map((t=>v(t)))}get marks(){return this.extensions.filter((t=>"mark"===t.type)).reduce(((t,{name:e,schema:i})=>({...t,[e]:i})),{})}get markViews(){return this.extensions.filter((t=>["mark"].includes(t.type))).filter((t=>t.view)).reduce(((t,{name:e,view:i})=>({...t,[e]:i})),{})}get nodes(){return this.extensions.filter((t=>"node"===t.type)).reduce(((t,{name:e,schema:i})=>({...t,[e]:i})),{})}get nodeViews(){return this.extensions.filter((t=>["node"].includes(t.type))).filter((t=>t.view)).reduce(((t,{name:e,view:i})=>({...t,[e]:i})),{})}get options(){const{view:t}=this;return this.extensions.reduce(((e,i)=>({...e,[i.name]:new Proxy(i.options,{set(e,i,s){const n=e[i]!==s;return Object.assign(e,{[i]:s}),n&&t.updateState(t.state),!0}})})),{})}pasteRules({schema:t,excludedExtensions:e}){const i=this.getAllowedExtensions(e);return[...this.getFromExtensions("pasteRules",{schema:t},i),...this.getFromNodesAndMarks("pasteRules",{schema:t},i)].reduce(((t,e)=>[...t,...e]),[])}plugins({schema:t}){return[...this.getFromExtensions("plugins",{schema:t}),...this.getFromNodesAndMarks("plugins",{schema:t})].reduce(((t,e)=>[...t,...e]),[]).map((t=>t instanceof i?t:new i(t)))}}class rs{constructor(t={}){this.options={...this.defaults,...t}}init(){return null}bindEditor(t=null){this.editor=t}get name(){return null}get type(){return"extension"}get defaults(){return{}}plugins(){return[]}inputRules(){return[]}pasteRules(){return[]}keys(){return{}}}class ls extends rs{constructor(t={}){super(t)}get type(){return"node"}get schema(){return{}}commands(){return{}}}class cs extends ls{get defaults(){return{inline:!1}}get name(){return"doc"}get schema(){return{content:this.options.inline?"inline*":"block+"}}}class us extends ls{get button(){return{id:this.name,icon:"paragraph",label:window.panel.$t("toolbar.button.paragraph"),name:this.name,separator:!0}}commands({utils:t,schema:e,type:i}){return{paragraph:()=>this.editor.activeNodes.includes("bulletList")?t.toggleList(e.nodes.bulletList,e.nodes.listItem):this.editor.activeNodes.includes("orderedList")?t.toggleList(e.nodes.orderedList,e.nodes.listItem):this.editor.activeNodes.includes("quote")?t.toggleWrap(e.nodes.quote):t.setBlockType(i)}}get schema(){return{content:"inline*",group:"block",draggable:!1,parseDOM:[{tag:"p"}],toDOM:()=>["p",0]}}get name(){return"paragraph"}}let ds=class extends ls{get name(){return"text"}get schema(){return{group:"inline"}}};class ps extends os{constructor(t={}){super(),this.defaults={autofocus:!1,content:"",disableInputRules:!1,disablePasteRules:!1,editable:!0,element:null,extensions:[],emptyDocument:{type:"doc",content:[]},events:{},inline:!1,parseOptions:{},topNode:"doc",useBuiltInExtensions:!0},this.init(t)}blur(){this.view.dom.blur()}get builtInExtensions(){return!0!==this.options.useBuiltInExtensions?[]:[new cs({inline:this.options.inline}),new ds,new us]}buttons(t){return this.extensions.buttons(t)}clearContent(t=!1){this.setContent(this.options.emptyDocument,t)}command(t,...e){var i,s;null==(s=(i=this.commands)[t])||s.call(i,...e)}createCommands(){return this.extensions.commands({schema:this.schema,view:this.view})}createDocument(t,e=this.options.parseOptions){if(null===t)return this.schema.nodeFromJSON(this.options.emptyDocument);if("object"==typeof t)try{return this.schema.nodeFromJSON(t)}catch(i){return window.console.warn("Invalid content.","Passed value:",t,"Error:",i),this.schema.nodeFromJSON(this.options.emptyDocument)}if("string"==typeof t){const i=`

      ${t}
      `,s=(new window.DOMParser).parseFromString(i,"text/html").body.firstElementChild;return y.fromSchema(this.schema).parse(s,e)}return!1}createEvents(){const t=this.options.events??{};for(const[e,i]of Object.entries(t))this.on(e,i);return t}createExtensions(){return new as([...this.builtInExtensions,...this.options.extensions],this)}createFocusEvents(){const t=(t,e,i=!0)=>{this.focused=i,this.emit(i?"focus":"blur",{event:e,state:t.state,view:t});const s=this.state.tr.setMeta("focused",i);this.view.dispatch(s)};return new i({props:{attributes:{tabindex:0},handleDOMEvents:{focus:(e,i)=>t(e,i,!0),blur:(e,i)=>t(e,i,!1)}}})}createInputRules(){return this.extensions.inputRules({schema:this.schema,excludedExtensions:this.options.disableInputRules})}createKeymaps(){return this.extensions.keymaps({schema:this.schema})}createMarks(){return this.extensions.marks}createMarkViews(){return this.extensions.markViews}createNodes(){return this.extensions.nodes}createNodeViews(){return this.extensions.nodeViews}createPasteRules(){return this.extensions.pasteRules({schema:this.schema,excludedExtensions:this.options.disablePasteRules})}createPlugins(){return this.extensions.plugins({schema:this.schema})}createSchema(){return new $({topNode:this.options.topNode,nodes:this.nodes,marks:this.marks})}createState(){return w.create({schema:this.schema,doc:this.createDocument(this.options.content),plugins:[...this.plugins,x({rules:this.inputRules}),...this.pasteRules,...this.keymaps,v({Backspace:O}),v(M),this.createFocusEvents()]})}createView(){return new _(this.element,{dispatchTransaction:this.dispatchTransaction.bind(this),attributes:{class:"k-text"},editable:()=>this.options.editable,handlePaste:(t,e)=>{if("function"==typeof this.events.paste){const t=e.clipboardData.getData("text/html"),i=e.clipboardData.getData("text/plain");if(!0===this.events.paste(e,t,i))return!0}},handleDrop:(...t)=>{this.emit("drop",...t)},markViews:this.createMarkViews(),nodeViews:this.createNodeViews(),state:this.createState()})}destroy(){this.view&&this.view.destroy()}dispatchTransaction(t){const e=this.state,i=this.state.apply(t);this.view.updateState(i),this.setActiveNodesAndMarks();const s={editor:this,getHTML:this.getHTML.bind(this),getJSON:this.getJSON.bind(this),state:this.state,transaction:t};this.emit("transaction",s),!t.docChanged&&t.getMeta("preventUpdate")||this.emit("update",s);const{from:n,to:o}=this.state.selection,a=!e||!e.selection.eq(i.selection);this.emit(i.selection.empty?"deselect":"select",{...s,from:n,hasChanged:a,to:o})}focus(t=null){if(this.view.focused&&null===t||!1===t)return;const{from:e,to:i}=this.selectionAtPosition(t);this.setSelection(e,i),setTimeout((()=>this.view.focus()),10)}getHTML(t=this.state.doc.content){const e=document.createElement("div"),i=C.fromSchema(this.schema).serializeFragment(t);return e.appendChild(i),this.options.inline&&e.querySelector("p")?e.querySelector("p").innerHTML:e.innerHTML}getHTMLStartToSelection(){const t=this.state.doc.slice(0,this.selection.head).content;return this.getHTML(t)}getHTMLSelectionToEnd(){const t=this.state.doc.slice(this.selection.head).content;return this.getHTML(t)}getHTMLStartToSelectionToEnd(){return[this.getHTMLStartToSelection(),this.getHTMLSelectionToEnd()]}getJSON(){return this.state.doc.toJSON()}getMarkAttrs(t=null){return this.activeMarkAttrs[t]}getSchemaJSON(){return JSON.parse(JSON.stringify({nodes:this.nodes,marks:this.marks}))}init(t={}){this.options={...this.defaults,...t},this.element=this.options.element,this.focused=!1,this.events=this.createEvents(),this.extensions=this.createExtensions(),this.nodes=this.createNodes(),this.marks=this.createMarks(),this.schema=this.createSchema(),this.keymaps=this.createKeymaps(),this.inputRules=this.createInputRules(),this.pasteRules=this.createPasteRules(),this.plugins=this.createPlugins(),this.view=this.createView(),this.commands=this.createCommands(),this.setActiveNodesAndMarks(),!1!==this.options.autofocus&&this.focus(this.options.autofocus),this.emit("init",{view:this.view,state:this.state}),this.extensions.view=this.view,this.setContent(this.options.content,!0)}insertText(t,e=!1){const{tr:i}=this.state,s=i.insertText(t);if(this.view.dispatch(s),e){const e=i.selection.from,s=e-t.length;this.setSelection(s,e)}}isEditable(){return this.options.editable}isEmpty(){if(this.state)return 0===this.state.doc.textContent.length}get isActive(){return Object.entries({...this.activeMarks,...this.activeNodes}).reduce(((t,[e,i])=>({...t,[e]:(t={})=>i(t)})),{})}removeMark(t){if(this.schema.marks[t])return ns.removeMark(this.schema.marks[t])(this.state,this.view.dispatch)}get selection(){return this.state.selection}get selectionAtEnd(){return S.atEnd(this.state.doc)}get selectionIsAtEnd(){return this.selection.head===this.selectionAtEnd.head}get selectionAtStart(){return S.atStart(this.state.doc)}get selectionIsAtStart(){return this.selection.head===this.selectionAtStart.head}selectionAtPosition(t=null){return null===t?this.selection:"start"===t||!0===t?this.selectionAtStart:"end"===t?this.selectionAtEnd:{from:t,to:t}}setActiveNodesAndMarks(){this.activeMarks=Object.values(this.schema.marks).filter((t=>ns.markIsActive(this.state,t))).map((t=>t.name)),this.activeMarkAttrs=Object.entries(this.schema.marks).reduce(((t,[e,i])=>({...t,[e]:ns.getMarkAttrs(this.state,i)})),{}),this.activeNodes=Object.values(this.schema.nodes).filter((t=>ns.nodeIsActive(this.state,t))).map((t=>t.name)),this.activeNodeAttrs=Object.entries(this.schema.nodes).reduce(((t,[e,i])=>({...t,[e]:ns.getNodeAttrs(this.state,i)})),{})}setContent(t={},e=!1,i){const{doc:s,tr:n}=this.state,o=this.createDocument(t,i),a=n.replaceWith(0,s.content.size,o).setMeta("preventUpdate",!e);this.view.dispatch(a)}setSelection(t=0,e=0){const{doc:i,tr:s}=this.state,n=ns.minMax(t,0,i.content.size),o=ns.minMax(e,0,i.content.size),a=S.create(i,n,o),r=s.setSelection(a);this.view.dispatch(r)}get state(){var t;return null==(t=this.view)?void 0:t.state}toggleMark(t){if(this.schema.marks[t])return ns.toggleMark(this.schema.marks[t])(this.state,this.view.dispatch)}updateMark(t,e){if(this.schema.marks[t])return ns.updateMark(this.schema.marks[t],e)(this.state,this.view.dispatch)}}class hs extends rs{command(){return()=>{}}remove(){this.editor.removeMark(this.name)}get schema(){return{}}get type(){return"mark"}toggle(){return this.editor.toggleMark(this.name)}update(t){this.editor.updateMark(this.name,t)}}class ms extends hs{get button(){return{icon:"bold",label:window.panel.$t("toolbar.button.bold")}}commands(){return()=>this.toggle()}inputRules({type:t,utils:e}){return[e.markInputRule(/(?:\*\*|__)([^*_]+)(?:\*\*|__)$/,t)]}keys(){return{"Mod-b":()=>this.toggle()}}get name(){return"bold"}pasteRules({type:t,utils:e}){return[e.markPasteRule(/(?:\*\*|__)([^*_]+)(?:\*\*|__)/g,t)]}get schema(){return{parseDOM:[{tag:"strong"},{tag:"b",getAttrs:t=>"normal"!==t.style.fontWeight&&null},{style:"font-weight",getAttrs:t=>/^(bold(er)?|[5-9]\d{2,})$/.test(t)&&null}],toDOM:()=>["strong",0]}}}class fs extends hs{get button(){return{icon:"clear",label:window.panel.$t("toolbar.button.clear")}}commands(){return()=>this.clear()}clear(){const{state:t}=this.editor,{from:e,to:i}=t.tr.selection;for(const s of this.editor.activeMarks){const n=t.schema.marks[s],o=this.editor.state.tr.removeMark(e,i,n);this.editor.view.dispatch(o)}}get name(){return"clear"}}let gs=class extends hs{get button(){return{icon:"code",label:window.panel.$t("toolbar.button.code")}}commands(){return()=>this.toggle()}inputRules({type:t,utils:e}){return[e.markInputRule(/(?:`)([^`]+)(?:`)$/,t)]}keys(){return{"Mod-`":()=>this.toggle()}}get name(){return"code"}pasteRules({type:t,utils:e}){return[e.markPasteRule(/(?:`)([^`]+)(?:`)/g,t)]}get schema(){return{excludes:"_",parseDOM:[{tag:"code"}],toDOM:()=>["code",0]}}};class ks extends hs{get button(){return{icon:"email",label:window.panel.$t("toolbar.button.email")}}commands(){return{email:t=>{if(t.altKey||t.metaKey)return this.remove();this.editor.emit("email",this.editor)},insertEmail:(t={})=>{const{selection:e}=this.editor.state;if(e.empty&&this.editor.insertText(t.href,!0),t.href)return this.update(t)},removeEmail:()=>this.remove(),toggleEmail:(t={})=>{var e;(null==(e=t.href)?void 0:e.length)>0?this.editor.command("insertEmail",t):this.editor.command("removeEmail")}}}get defaults(){return{target:null}}get name(){return"email"}pasteRules({type:t,utils:e}){return[e.pasteRule(/^[\w-.]+@([\w-]+\.)+[\w-]{2,4}$/gi,t,(t=>({href:t})))]}plugins(){return[{props:{handleClick:(t,e,i)=>{const s=this.editor.getMarkAttrs("email");s.href&&!0===i.altKey&&i.target instanceof HTMLAnchorElement&&(i.stopPropagation(),window.open(s.href))}}}]}get schema(){return{attrs:{href:{default:null},title:{default:null}},inclusive:!1,parseDOM:[{tag:"a[href^='mailto:']",getAttrs:t=>({href:t.getAttribute("href").replace("mailto:",""),title:t.getAttribute("title")})}],toDOM:t=>["a",{...t.attrs,href:"mailto:"+t.attrs.href},0]}}}class bs extends hs{get button(){return{icon:"italic",label:window.panel.$t("toolbar.button.italic")}}commands(){return()=>this.toggle()}inputRules({type:t,utils:e}){return[e.markInputRule(/(?:^|\s)((?:\*)((?:[^*]+))(?:\*))$/,t),e.markInputRule(/(?:^|\s)((?:_)((?:[^_]+))(?:_))$/,t)]}keys(){return{"Mod-i":()=>this.toggle()}}get name(){return"italic"}pasteRules({type:t,utils:e}){return[e.markPasteRule(/_([^_]+)_/g,t),e.markPasteRule(/\*([^*]+)\*/g,t)]}get schema(){return{parseDOM:[{tag:"i"},{tag:"em"},{style:"font-style=italic"}],toDOM:()=>["em",0]}}}let vs=class extends hs{get button(){return{icon:"url",label:window.panel.$t("toolbar.button.link")}}commands(){return{link:t=>{if(t.altKey||t.metaKey)return this.remove();this.editor.emit("link",this.editor)},insertLink:(t={})=>{const{selection:e}=this.editor.state;if(e.empty&&!1===this.editor.activeMarks.includes("link")&&this.editor.insertText(t.href,!0),t.href)return this.update(t)},removeLink:()=>this.remove(),toggleLink:(t={})=>{var e;(null==(e=t.href)?void 0:e.length)>0?this.editor.command("insertLink",t):this.editor.command("removeLink")}}}get defaults(){return{target:null}}get name(){return"link"}pasteRules({type:t,utils:e}){return[e.pasteRule(/https?:\/\/(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z]{2,}\b([-a-zA-Z0-9@:%_+.~#?&//=,]*)/gi,t,(t=>({href:t})))]}plugins(){return[{props:{handleClick:(t,e,i)=>{const s=this.editor.getMarkAttrs("link");s.href&&!0===i.altKey&&i.target instanceof HTMLAnchorElement&&(i.stopPropagation(),window.open(s.href,s.target))}}}]}get schema(){return{attrs:{href:{default:null},target:{default:null},title:{default:null}},inclusive:!1,parseDOM:[{tag:"a[href]:not([href^='mailto:'])",getAttrs:t=>({href:t.getAttribute("href"),target:t.getAttribute("target"),title:t.getAttribute("title")})}],toDOM:t=>["a",{...t.attrs},0]}}};class ys extends hs{get button(){return{icon:"strikethrough",label:window.panel.$t("toolbar.button.strike")}}commands(){return()=>this.toggle()}inputRules({type:t,utils:e}){return[e.markInputRule(/~([^~]+)~$/,t)]}keys(){return{"Mod-d":()=>this.toggle()}}get name(){return"strike"}pasteRules({type:t,utils:e}){return[e.markPasteRule(/~([^~]+)~/g,t)]}get schema(){return{parseDOM:[{tag:"s"},{tag:"del"},{tag:"strike"},{style:"text-decoration",getAttrs:t=>"line-through"===t}],toDOM:()=>["s",0]}}}let $s=class extends hs{get button(){return{icon:"superscript",label:window.panel.$t("toolbar.button.sup")}}commands(){return()=>this.toggle()}get name(){return"sup"}get schema(){return{parseDOM:[{tag:"sup"}],toDOM:()=>["sup",0]}}};class ws extends hs{get button(){return{icon:"subscript",label:window.panel.$t("toolbar.button.sub")}}commands(){return()=>this.toggle()}get name(){return"sub"}get schema(){return{parseDOM:[{tag:"sub"}],toDOM:()=>["sub",0]}}}class xs extends hs{get button(){return{icon:"underline",label:window.panel.$t("toolbar.button.underline")}}commands(){return()=>this.toggle()}keys(){return{"Mod-u":()=>this.toggle()}}get name(){return"underline"}get schema(){return{parseDOM:[{tag:"u"},{style:"text-decoration",getAttrs:t=>"underline"===t}],toDOM:()=>["u",0]}}}class _s extends ls{get button(){return{id:this.name,icon:"list-bullet",label:window.panel.$t("toolbar.button.ul"),name:this.name,when:["listItem","bulletList","orderedList","paragraph"]}}commands({type:t,schema:e,utils:i}){return()=>i.toggleList(t,e.nodes.listItem)}inputRules({type:t,utils:e}){return[e.wrappingInputRule(/^\s*([-+*])\s$/,t)]}keys({type:t,schema:e,utils:i}){return{"Shift-Ctrl-8":i.toggleList(t,e.nodes.listItem)}}get name(){return"bulletList"}get schema(){return{content:"listItem+",group:"block",parseDOM:[{tag:"ul"}],toDOM:()=>["ul",0]}}}class Cs extends ls{commands({utils:t,type:e}){return()=>this.createHardBreak(t,e)}createHardBreak(t,e){return t.chainCommands(t.exitCode,t.insertNode(e))}get defaults(){return{enter:!1,text:!1}}keys({utils:t,type:e}){const i=this.createHardBreak(t,e);let s={"Mod-Enter":i,"Shift-Enter":i};return this.options.enter&&(s.Enter=i),s}get name(){return"hardBreak"}get schema(){return{inline:!0,group:"inline",selectable:!1,parseDOM:[{tag:"br"}],toDOM:()=>["br"]}}}class Ss extends ls{get button(){const t=this.options.levels.map((t=>({id:`h${t}`,command:`h${t}`,icon:`h${t}`,label:window.panel.$t("toolbar.button.heading."+t),attrs:{level:t},name:this.name,when:["heading","paragraph"]})));return t[t.length-1].separator=!0,t}commands({type:t,schema:e,utils:i}){let s={toggleHeading:s=>i.toggleBlockType(t,e.nodes.paragraph,s)};for(const n of this.options.levels)s[`h${n}`]=()=>i.toggleBlockType(t,e.nodes.paragraph,{level:n});return s}get defaults(){return{levels:[1,2,3,4,5,6]}}inputRules({type:t,utils:e}){return this.options.levels.map((i=>e.textblockTypeInputRule(new RegExp(`^(#{1,${i}})\\s$`),t,(()=>({level:i})))))}keys({type:t,utils:e}){return this.options.levels.reduce(((i,s)=>({...i,[`Shift-Ctrl-${s}`]:e.setBlockType(t,{level:s})})),{})}get name(){return"heading"}get schema(){return{attrs:{level:{default:1}},content:"inline*",group:"block",defining:!0,draggable:!1,parseDOM:this.options.levels.map((t=>({tag:`h${t}`,attrs:{level:t}}))),toDOM:t=>[`h${t.attrs.level}`,0]}}}class Os extends ls{commands({type:t,utils:e}){return()=>e.insertNode(t)}inputRules({type:t,utils:e}){const i=e.nodeInputRule(/^(?:---|___\s|\*\*\*\s)$/,t),s=i.handler;return i.handler=(t,e,i,n)=>s(t,e,i,n).replaceWith(i-1,i,""),[i]}get name(){return"horizontalRule"}get schema(){return{group:"block",parseDOM:[{tag:"hr"}],toDOM:()=>["hr"]}}}class Ms extends ls{keys({type:t,utils:e}){return{Enter:e.splitListItem(t),"Shift-Tab":e.liftListItem(t),Tab:e.sinkListItem(t)}}get name(){return"listItem"}get schema(){return{content:"paragraph block*",defining:!0,draggable:!1,parseDOM:[{tag:"li"}],toDOM:()=>["li",0]}}}class As extends ls{get button(){return{id:this.name,icon:"list-numbers",label:window.panel.$t("toolbar.button.ol"),name:this.name,when:["listItem","bulletList","orderedList","paragraph"],separator:!0}}commands({type:t,schema:e,utils:i}){return()=>i.toggleList(t,e.nodes.listItem)}inputRules({type:t,utils:e}){return[e.wrappingInputRule(/^(\d+)\.\s$/,t,(t=>({order:+t[1]})),((t,e)=>e.childCount+e.attrs.order===+t[1]))]}keys({type:t,schema:e,utils:i}){return{"Shift-Ctrl-9":i.toggleList(t,e.nodes.listItem)}}get name(){return"orderedList"}get schema(){return{attrs:{order:{default:1}},content:"listItem+",group:"block",parseDOM:[{tag:"ol",getAttrs:t=>({order:t.hasAttribute("start")?+t.getAttribute("start"):1})}],toDOM:t=>1===t.attrs.order?["ol",0]:["ol",{start:t.attrs.order},0]}}}class Is extends ls{get button(){return{id:this.name,icon:"quote",label:window.panel.$t("field.blocks.quote.name"),name:this.name}}commands({type:t,utils:e}){return()=>e.toggleWrap(t)}inputRules({type:t,utils:e}){return[e.wrappingInputRule(/^\s*>\s$/,t)]}keys({utils:t}){return{"Shift-Tab":(e,i)=>t.lift(e,i)}}get name(){return"quote"}get schema(){return{content:"block+",group:"block",defining:!0,draggable:!1,parseDOM:[{tag:"blockquote"}],toDOM:()=>["blockquote",0]}}}let Ds=class extends rs{commands(){return{undo:()=>j,redo:()=>D,undoDepth:()=>I,redoDepth:()=>A}}get defaults(){return{depth:"",newGroupDelay:""}}keys(){return{"Mod-z":j,"Mod-y":D,"Shift-Mod-z":D,"Mod-я":j,"Shift-Mod-я":D}}get name(){return"history"}plugins(){return[E({depth:this.options.depth,newGroupDelay:this.options.newGroupDelay})]}};class js extends rs{commands(){return{insertHtml:t=>(e,i)=>{let s=document.createElement("div");s.innerHTML=t.trim();const n=y.fromSchema(e.schema).parse(s);i(e.tr.replaceSelectionWith(n).scrollIntoView())}}}}class Es extends rs{keys(){const t={};for(const e in this.options)t[e]=()=>(this.options[e](),!0);return t}}let Ls=class extends rs{constructor(t){super(),this.writer=t}get component(){return this.writer.$refs.toolbar}init(){this.editor.on("deselect",(({event:t})=>{var e;return null==(e=this.component)?void 0:e.close(t)})),this.editor.on("select",(({hasChanged:t})=>{var e;!1!==t&&(null==(e=this.component)||e.open())}))}get type(){return"toolbar"}};const Ts={mixins:[V,W,at,lt],props:{breaks:Boolean,code:Boolean,emptyDocument:{type:Object,default:()=>({type:"doc",content:[]})},extensions:Array,headings:{default:()=>[1,2,3,4,5,6],type:[Array,Boolean]},inline:Boolean,keys:Object,marks:{type:[Array,Boolean],default:!0},nodes:{type:[Array,Boolean],default:()=>["heading","bulletList","orderedList"]},paste:{type:Function,default:()=>()=>!1},toolbar:{type:Object,default:()=>({inline:!0})},value:{type:String,default:""}}};const Bs=ct({mixins:[Ts],emits:["input"],data(){return{editor:null,json:{},html:this.value,isEmpty:!0}},computed:{isCursorAtEnd(){return this.editor.selectionIsAtEnd},isCursorAtStart(){return this.editor.selectionIsAtStart},toolbarOptions(){return{marks:Array.isArray(this.marks)?this.marks:void 0,...this.toolbar,editor:this.editor}}},watch:{value(t,e){t!==e&&t!==this.html&&(this.html=t,this.editor.setContent(this.html))}},mounted(){this.editor=new ps({autofocus:this.autofocus,content:this.value,editable:!this.disabled,element:this.$el,emptyDocument:this.emptyDocument,parseOptions:{preserveWhitespace:!0},events:{link:t=>{this.$panel.dialog.open({component:"k-link-dialog",props:{value:t.getMarkAttrs("link")},on:{cancel:()=>t.focus(),submit:e=>{this.$panel.dialog.close(),t.command("toggleLink",e)}}})},email:t=>{this.$panel.dialog.open({component:"k-email-dialog",props:{value:this.editor.getMarkAttrs("email")},on:{cancel:()=>t.focus(),submit:e=>{this.$panel.dialog.close(),t.command("toggleEmail",e)}}})},paste:this.paste,update:t=>{if(!this.editor)return;const e=JSON.stringify(this.editor.getJSON());e!==JSON.stringify(this.json)&&(this.json=e,this.isEmpty=t.editor.isEmpty(),this.html=t.editor.getHTML(),this.isEmpty&&(0===t.editor.activeNodes.length||t.editor.activeNodes.includes("paragraph"))&&(this.html=""),this.$emit("input",this.html))}},extensions:[...this.createMarks(),...this.createNodes(),new Es(this.keys),new Ds,new js,new Ls(this),...this.extensions||[]],inline:this.inline}),this.isEmpty=this.editor.isEmpty(),this.json=this.editor.getJSON(),this.$panel.events.on("click",this.onBlur),this.$panel.events.on("focus",this.onBlur)},beforeDestroy(){this.editor.destroy(),this.$panel.events.off("click",this.onBlur),this.$panel.events.off("focus",this.onBlur)},methods:{command(t,...e){this.editor.command(t,...e)},createMarks(){return this.filterExtensions({clear:new fs,code:new gs,underline:new xs,strike:new ys,link:new vs,email:new ks,bold:new ms,italic:new bs,sup:new $s,sub:new ws,...this.createMarksFromPanelPlugins()},this.marks)},createMarksFromPanelPlugins(){const t=window.panel.plugins.writerMarks??{},e={};for(const i in t)e[i]=Object.create(hs.prototype,Object.getOwnPropertyDescriptors({name:i,...t[i]}));return e},createNodes(){const t=new Cs({text:!0,enter:this.inline});return this.filterExtensions({bulletList:new _s,orderedList:new As,heading:new Ss({levels:this.headings}),horizontalRule:new Os,listItem:new Ms,quote:new Is,...this.createNodesFromPanelPlugins()},this.nodes,((e,i)=>((e.includes("bulletList")||e.includes("orderedList"))&&i.push(new Ms),!0===this.inline&&(i=i.filter((t=>!0===t.schema.inline))),i.push(t),i)))},createNodesFromPanelPlugins(){const t=window.panel.plugins.writerNodes??{},e={};for(const i in t)e[i]=Object.create(ls.prototype,Object.getOwnPropertyDescriptors({name:i,...t[i]}));return e},getHTML(){return this.editor.getHTML()},filterExtensions(t,e,i){!1===e?e=[]:!0!==e&&!1!==Array.isArray(e)||(e=Object.keys(t));let s=[];for(const n in t)e.includes(n)&&s.push(t[n]);return"function"==typeof i&&(s=i(e,s)),s},focus(){this.editor.focus()},getSplitContent(){return this.editor.getHTMLStartToSelectionToEnd()},onBlur(t){var e;!1===this.$el.contains(t.target)&&(null==(e=this.$refs.toolbar)||e.close())},onCommand(t,...e){this.editor.command(t,...e)}}},(function(){var t=this,e=t._self._c;return e("div",{directives:[{name:"direction",rawName:"v-direction"}],ref:"editor",staticClass:"k-writer",attrs:{"data-disabled":t.disabled,"data-empty":t.isEmpty,"data-placeholder":t.placeholder,"data-toolbar-inline":Boolean(t.toolbar.inline??!0),spellcheck:t.spellcheck}},[t.editor&&!t.disabled?e("k-writer-toolbar",t._b({ref:"toolbar",on:{command:t.onCommand}},"k-writer-toolbar",t.toolbarOptions,!1)):t._e()],1)}),[]).exports,qs={mixins:[Ie,Ts,et,it]};const Ps=ct({mixins:[De,qs],computed:{counterValue(){const t=this.$helper.string.stripHTML(this.value??"");return this.$helper.string.unescapeHTML(t)}},watch:{value(){this.onInvalid()}},mounted(){this.onInvalid(),this.$props.autofocus&&this.focus()},methods:{focus(){this.$refs.input.focus()},onInvalid(){this.$emit("invalid",this.$v.$invalid,this.$v)}},validations(){return{counterValue:{required:!this.required||t.required,minLength:!this.minlength||t.minLength(this.minlength),maxLength:!this.maxlength||t.maxLength(this.maxlength)}}}},(function(){var t=this;return(0,t._self._c)("k-writer",t._b({ref:"input",staticClass:"k-writer-input",on:{input:function(e){return t.$emit("input",e)}}},"k-writer",t.$props,!1))}),[]).exports;class Ns extends cs{get schema(){return{content:this.options.nodes.join("|")}}}const zs={mixins:[qs],inheritAttrs:!1,props:{nodes:{type:Array,default:()=>["bulletList","orderedList"]}}};const Fs=ct({mixins:[De,zs],data(){return{list:this.value,html:this.value}},computed:{listExtensions(){return[new Ns({inline:!0,nodes:this.nodes})]}},watch:{value(t){t!==this.html&&(this.list=t,this.html=t)}},methods:{focus(){this.$refs.input.focus()},onInput(t){let e=(new DOMParser).parseFromString(t,"text/html").querySelector("ul, ol");e&&0!==e.textContent.trim().length?(this.list=t,this.html=t.replace(/(

      |<\/p>)/gi,""),this.$emit("input",this.html)):this.$emit("input",this.list="")}}},(function(){var t=this;return(0,t._self._c)("k-writer",t._b({ref:"input",staticClass:"k-list-input",attrs:{extensions:t.listExtensions,value:t.list},on:{input:t.onInput}},"k-writer",t.$props,!1))}),[]).exports;const Ys=ct({mixins:[Fe,Ue,zs],inheritAttrs:!1,methods:{focus(){this.$refs.input.focus()}}},(function(){var t=this,e=t._self._c;return e("k-field",t._b({staticClass:"k-list-field",attrs:{input:t.id,counter:!1}},"k-field",t.$props,!1),[e("k-input",t._b({ref:"input",attrs:{type:"list"},on:{input:function(e){return t.$emit("input",e)}}},"k-input",t.$props,!1))],1)}),[]).exports,Rs={mixins:[W,X,nt],inheritAttrs:!1,props:{layout:String,sort:{default:!1,type:Boolean},value:{default:()=>[],type:Array}}};const Us=ct({mixins:[Rs],props:{draggable:{default:!0,type:Boolean}},emits:["edit","input"],data:()=>({tags:[]}),computed:{dragOptions(){return{delay:1,disabled:!this.isDraggable,draggable:".k-tag",handle:".k-tag-text"}},isDraggable(){return!0!==this.sort&&!1!==this.draggable&&0!==this.tags.length&&!0!==this.disabled}},watch:{value:{handler(){let t=structuredClone(this.value);if(!0===this.sort){const e=[];for(const i of this.options){const s=t.indexOf(i.value);-1!==s&&(e.push(i),t.splice(s,1))}e.push(...t),t=e}this.tags=t.map(this.tag).filter((t=>t))},immediate:!0}},methods:{edit(t,e,i){!1===this.disabled&&this.$emit("edit",t,e,i)},focus(t="last"){this.$refs.navigate.move(t)},index(t){return this.tags.findIndex((e=>e.value===t.value))},input(){this.$emit("input",this.tags.map((t=>t.value)))},navigate(t){this.focus(t)},remove(t){this.tags.length<=1?this.navigate("last"):this.navigate("prev"),this.tags.splice(t,1),this.input()},option(t){return this.options.find((e=>e.value===t.value))},select(){this.focus()},tag(t){"object"!=typeof t&&(t={value:t});const e=this.option(t);return e||{text:this.$helper.string.escapeHTML(t.text??t.value),value:t.value}}}},(function(){var t=this,e=t._self._c;return e("k-navigate",{ref:"navigate",attrs:{axis:"list"===t.layout?"y":"x",select:":where(.k-tag, .k-tags-navigatable):not(:disabled)"}},[e("k-draggable",{staticClass:"k-tags",attrs:{list:t.tags,options:t.dragOptions,"data-layout":t.layout},on:{end:t.input},scopedSlots:t._u([{key:"footer",fn:function(){return[t._t("default")]},proxy:!0}],null,!0)},t._l(t.tags,(function(i,s){return e("k-tag",{key:s,attrs:{disabled:t.disabled,image:i.image,removable:!t.disabled,name:"tag"},on:{remove:function(e){return t.remove(s,i)}},nativeOn:{click:function(t){t.stopPropagation()},keypress:function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"enter",13,e.key,"Enter")?null:t.edit(s,i,e)},dblclick:function(e){return t.edit(s,i,e)}}},[e("span",{domProps:{innerHTML:t._s(i.text)}})])})),1)],1)}),[]).exports,Hs={mixins:[st,rt,Rs,je],props:{value:{default:()=>[],type:Array}},watch:{value:{handler(){this.$emit("invalid",this.$v.$invalid,this.$v)},immediate:!0}},validations(){return{value:{required:!this.required||t.required,minLength:!this.min||t.minLength(this.min),maxLength:!this.max||t.maxLength(this.max)}}},methods:{open(){this.$refs.dropdown.open(this.$el)}}};const Vs=ct({mixins:[De,Hs]},(function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-multiselect-input"},[e("k-tags",t._b({ref:"tags",on:{input:function(e){return t.$emit("input",e)}},nativeOn:{click:function(e){return e.stopPropagation(),t.open.apply(null,arguments)}}},"k-tags",t.$props,!1),[!t.max||t.value.length({editing:null}),computed:{canAdd(){return!this.max||this.value.length!1===this.value.includes(t.value)))},picklist(){return{disabled:this.disabled,create:this.showCreate,ignore:this.ignore,min:this.min,max:this.max,search:this.showSearch}},replacableOptions(){return this.options.filter((t=>{var e;return!1===this.value.includes(t.value)||t.value===(null==(e=this.editing)?void 0:e.tag.value)}))},showCreate(){return"options"!==this.accept&&(!this.editing||{submit:this.$t("replace.with")})},showSearch(){return!1!==this.search&&(this.editing?{placeholder:this.$t("replace.with"),...this.search}:"options"===this.accept?{placeholder:this.$t("filter"),...this.search}:this.search)}},methods:{create(t){const e=t.split(this.separator).map((t=>t.trim())),i=structuredClone(this.value);for(let s of e)s=this.$refs.tags.tag(s,this.separator),!0===this.isAllowed(s)&&i.push(s.value);this.$emit("input",i),this.$refs.create.close()},async edit(t,e){this.editing={index:t,tag:e},this.$refs.replace.open()},focus(){this.canAdd&&this.$refs.create.open()},isAllowed(t){return"object"==typeof t&&0!==t.value.trim().length&&(!("options"===this.accept&&!this.$refs.tags.option(t))&&!0!==this.value.includes(t.value))},pick(t){this.$emit("input",t),this.$refs.create.close()},replace(t){const{index:e}=this.editing,i=this.$refs.tags.tag(t);if(this.$refs.replace.close(),this.editing=null,!1===this.isAllowed(i))return!1;const s=structuredClone(this.value);s.splice(e,1,i.value),this.$emit("input",s),this.$refs.tags.navigate(e)},toggle(t){return!(t.metaKey||t.altKey||t.ctrlKey)&&("ArrowDown"===t.key?(this.$refs.create.open(),void t.preventDefault()):void(String.fromCharCode(t.keyCode).match(/(\w)/g)&&this.$refs.create.open()))}}},(function(){var t,e=this,i=e._self._c;return i("div",{staticClass:"k-tags-input",attrs:{"data-can-add":e.canAdd}},[i("k-tags",e._b({ref:"tags",on:{edit:e.edit,input:function(t){return e.$emit("input",t)}},nativeOn:{click:function(t){var i,s;t.stopPropagation(),null==(s=null==(i=e.$refs.toggle)?void 0:i.$el)||s.click()}}},"k-tags",e.$props,!1),[e.canAdd?i("k-button",{ref:"toggle",staticClass:"k-tags-input-toggle k-tags-navigatable input-focus",attrs:{id:e.id,autofocus:e.autofocus,disabled:e.disabled,size:"xs",icon:"add"},on:{click:function(t){return e.$refs.create.open()}},nativeOn:{keydown:[function(t){return!t.type.indexOf("key")&&e._k(t.keyCode,"delete",[8,46],t.key,["Backspace","Delete","Del"])?null:e.$refs.tags.focus("prev")},function(t){return e.toggle.apply(null,arguments)}]}}):e._e()],1),i("k-picklist-dropdown",e._b({ref:"replace",attrs:{multiple:!1,options:e.replacableOptions,value:(null==(t=e.editing)?void 0:t.tag.value)??""},on:{create:e.replace,input:e.replace}},"k-picklist-dropdown",e.picklist,!1)),i("k-picklist-dropdown",e._b({ref:"create",attrs:{options:e.creatableOptions,value:e.value},on:{create:e.create,input:e.pick}},"k-picklist-dropdown",e.picklist,!1))],1)}),[]).exports;const Js=ct({mixins:[Fe,Ue,Ks,xi],inheritAttrs:!1,computed:{hasNoOptions(){return 0===this.options.length&&"options"===this.accept}},methods:{focus(){this.$refs.input.focus()}}},(function(){var t=this,e=t._self._c;return e("k-field",t._b({staticClass:"k-tags-field",attrs:{input:t.id,counter:t.counterOptions}},"k-field",t.$props,!1),[t.hasNoOptions?e("k-empty",{attrs:{icon:t.icon,text:t.$t("options.none")}}):e("k-input",t._g(t._b({ref:"input",attrs:{type:"tags"}},"k-input",t.$props,!1),t.$listeners))],1)}),[]).exports;const Gs=ct({extends:Js,inheritAttrs:!1},(function(){var t=this,e=t._self._c;return e("k-field",t._b({staticClass:"k-multiselect-field",attrs:{input:t.id,counter:t.counterOptions}},"k-field",t.$props,!1),[t.hasNoOptions?e("k-empty",{attrs:{icon:t.icon,text:t.$t("options.none")}}):e("k-input",t._g(t._b({ref:"input",attrs:{type:"multiselect"}},"k-input",t.$props,!1),t.$listeners))],1)}),[]).exports,Xs={mixins:[Ie,at],props:{max:Number,min:Number,name:[Number,String],preselect:Boolean,step:[Number,String],value:{type:[Number,String],default:""}}};const Zs=ct({mixins:[De,Xs],data(){return{number:this.format(this.value),stepNumber:this.format(this.step),timeout:null,listeners:{...this.$listeners,input:t=>this.onInput(t.target.value),blur:this.onBlur}}},watch:{value(t){this.number=t},number:{immediate:!0,handler(){this.onInvalid()}}},mounted(){this.$props.autofocus&&this.focus(),this.$props.preselect&&this.select()},methods:{decimals(){const t=Number(this.step??0);return Math.floor(t)===t?0:-1!==t.toString().indexOf("e")?parseInt(t.toFixed(16).split(".")[1].split("").reverse().join("")).toString().length:t.toString().split(".")[1].length??0},format(t){if(isNaN(t)||""===t)return"";const e=this.decimals();return t=e?parseFloat(t).toFixed(e):Number.isInteger(this.step)?parseInt(t):parseFloat(t)},clean(){this.number=this.format(this.number)},emit(t){t=parseFloat(t),isNaN(t)&&(t=""),t!==this.value&&this.$emit("input",t)},onInvalid(){this.$emit("invalid",this.$v.$invalid,this.$v)},onInput(t){this.number=t,this.emit(t)},onBlur(){this.clean(),this.emit(this.number)},select(){this.$refs.input.select()}},validations(){return{value:{required:!this.required||t.required,min:!this.min||t.minValue(this.min),max:!this.max||t.maxValue(this.max)}}}},(function(){var t=this;return(0,t._self._c)("input",t._g(t._b({ref:"input",staticClass:"k-number-input",attrs:{step:t.stepNumber,type:"number"},domProps:{value:t.number},on:{keydown:[function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"s",void 0,e.key,void 0)?null:e.ctrlKey?t.clean.apply(null,arguments):null},function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"s",void 0,e.key,void 0)?null:e.metaKey?t.clean.apply(null,arguments):null}]}},"input",{autofocus:t.autofocus,disabled:t.disabled,id:t.id,max:t.max,min:t.min,name:t.name,placeholder:t.placeholder,required:t.required},!1),t.listeners))}),[]).exports;const Qs=ct({mixins:[Fe,Ue,Xs],inheritAttrs:!1,methods:{focus(){this.$refs.input.focus()}}},(function(){var t=this,e=t._self._c;return e("k-field",t._b({staticClass:"k-number-field",attrs:{input:t.id}},"k-field",t.$props,!1),[e("k-input",t._g(t._b({ref:"input",attrs:{type:"number"}},"k-input",t.$props,!1),t.$listeners))],1)}),[]).exports;const tn=ct({mixins:[Fe,Ue],props:{empty:String,fields:[Object,Array],value:[String,Object]},data:()=>({object:{}}),computed:{hasFields(){return this.$helper.object.length(this.fields)>0},isEmpty(){return null===this.object||0===this.$helper.object.length(this.object)},isInvalid(){return!0===this.required&&this.isEmpty}},watch:{value:{handler(t){this.object=this.valueToObject(t)},immediate:!0}},methods:{add(){this.object=this.$helper.field.form(this.fields),this.save(),this.open()},cell(t,e){this.$set(this.object,t,e),this.save()},form(t){const e=this.$helper.field.subfields(this,this.fields);if(t)for(const i in e)e[i].autofocus=i===t;return e},remove(){this.object={},this.save()},open(t){if(this.disabled)return!1;this.$panel.drawer.open({component:"k-form-drawer",props:{breadcrumb:[],icon:"box",tab:"object",tabs:{object:{fields:this.form(t)}},title:this.label,value:this.object},on:{input:t=>{for(const e in t)this.$set(this.object,e,t[e]);this.save()}}})},save(){this.$emit("input",this.object)},valueToObject:t=>"object"!=typeof t?{}:t}},(function(){var t=this,e=t._self._c;return e("k-field",t._b({staticClass:"k-object-field",scopedSlots:t._u([!t.disabled&&t.hasFields?{key:"options",fn:function(){return[t.isEmpty?e("k-button",{attrs:{icon:"add",size:"xs",variant:"filled"},on:{click:t.add}}):e("k-button",{attrs:{icon:"remove",size:"xs",variant:"filled"},on:{click:t.remove}})]},proxy:!0}:null],null,!0)},"k-field",t.$props,!1),[t.hasFields?[t.isEmpty?e("k-empty",{attrs:{"data-invalid":t.isInvalid,icon:"box"},on:{click:t.add}},[t._v(" "+t._s(t.empty??t.$t("field.object.empty"))+" ")]):e("table",{staticClass:"k-table k-object-field-table",attrs:{"aria-disabled":t.disabled,"data-invalid":t.isInvalid}},[e("tbody",[t._l(t.fields,(function(i){return[i.saveable&&t.$helper.field.isVisible(i,t.value)?e("tr",{key:i.name,on:{click:function(e){return t.open(i.name)}}},[e("th",{attrs:{"data-has-button":"","data-mobile":"true"}},[e("button",{attrs:{type:"button"}},[t._v(t._s(i.label))])]),e("k-table-cell",{attrs:{column:i,field:i,mobile:!0,value:t.object[i.name]},on:{input:function(e){return t.cell(i.name,e)}}})],1):t._e()]}))],2)])]:[e("k-empty",{attrs:{icon:"box"}},[t._v(t._s(t.$t("fields.empty")))])]],2)}),[]).exports;const en=ct({extends:Ni,type:"pages",computed:{emptyProps(){return{icon:"page",text:this.empty??(this.multiple&&1!==this.max?this.$t("field.pages.empty"):this.$t("field.pages.empty.single"))}}}},null,null).exports,sn={mixins:[Li],props:{autocomplete:{type:String,default:"new-password"},type:{type:String,default:"password"}}};const nn=ct({extends:Ti,mixins:[sn]},null,null).exports;const on=ct({mixins:[Fe,Ue,sn,xi],inheritAttrs:!1,props:{minlength:{type:Number,default:8},icon:{type:String,default:"key"}},methods:{focus(){this.$refs.input.focus()}}},(function(){var t=this,e=t._self._c;return e("k-field",t._b({staticClass:"k-password-field",attrs:{input:t.id,counter:t.counterOptions},scopedSlots:t._u([{key:"options",fn:function(){return[t._t("options")]},proxy:!0}],null,!0)},"k-field",t.$props,!1),[e("k-input",t._g(t._b({ref:"input",attrs:{type:"password"}},"k-input",t.$props,!1),t.$listeners))],1)}),[]).exports,an={mixins:[Ie,nt],props:{columns:{default:1,type:Number},reset:{default:!0,type:Boolean},theme:String,value:[String,Number,Boolean]}};const rn=ct({mixins:[De,an],computed:{choices(){return this.options.map(((t,e)=>({autofocus:this.autofocus&&0===e,checked:this.value===t.value,disabled:this.disabled||t.disabled,id:`${this.id}-${e}`,info:t.info,label:t.text,name:this.name??this.id,type:"radio",value:t.value})))}},watch:{value:{handler(){this.validate()},immediate:!0}},methods:{focus(){var t;null==(t=this.$el.querySelector("input"))||t.focus()},select(){this.focus()},toggle(t){t===this.value&&this.reset&&!this.required&&this.$emit("input","")},validate(){this.$emit("invalid",this.$v.$invalid,this.$v)}},validations(){return{value:{required:!this.required||t.required}}}},(function(){var t=this,e=t._self._c;return e("ul",{staticClass:"k-radio-input k-grid",style:{"--columns":t.columns},attrs:{"data-variant":"choices"}},t._l(t.choices,(function(i,s){return e("li",{key:s},[e("k-choice-input",t._b({on:{input:function(e){return t.$emit("input",i.value)}},nativeOn:{click:function(e){return e.stopPropagation(),t.toggle(i.value)}}},"k-choice-input",i,!1))],1)})),0)}),[]).exports;const ln=ct({mixins:[Fe,Ue,an],inheritAttrs:!1,methods:{focus(){this.$refs.input.focus()}}},(function(){var t,e=this,i=e._self._c;return i("k-field",e._b({staticClass:"k-radio-field",attrs:{input:e.id+"-0"}},"k-field",e.$props,!1),[(null==(t=e.options)?void 0:t.length)?i("k-radio-input",e._g(e._b({ref:"input"},"k-radio-input",e.$props,!1),e.$listeners)):i("k-empty",{attrs:{text:e.$t("options.none"),icon:"checklist"}})],1)}),[]).exports,cn={mixins:[Ie],props:{default:[Number,String],max:{type:Number,default:100},min:{type:Number,default:0},step:{type:[Number,String],default:1},tooltip:{type:[Boolean,Object],default:()=>({before:null,after:null})},value:[Number,String]}};const un=ct({mixins:[De,cn],computed:{baseline(){return this.min<0?0:this.min},isEmpty(){return""===this.value||void 0===this.value||null===this.value},label(){return this.required||this.value||0===this.value?this.format(this.position):"–"},position(){return this.value||0===this.value?this.value:this.default??this.baseline}},watch:{position(){this.onInvalid()},value(){this.validate()}},mounted(){this.onInvalid(),this.validate(),this.$props.autofocus&&this.focus()},methods:{focus(){var t;null==(t=this.$el.querySelector("input"))||t.focus()},format(t){const e=document.lang?document.lang.replace("_","-"):"en",i=this.step.toString().split("."),s=i.length>1?i[1].length:0;return new Intl.NumberFormat(e,{minimumFractionDigits:s}).format(t)},onInvalid(){this.$emit("invalid",this.$v.$invalid,this.$v)},onInput(t){this.$emit("input",t)},validate(){var t;const e=[];this.required&&!0===this.isEmpty&&e.push(this.$t("error.validation.required")),!1===this.isEmpty&&this.min&&this.valuethis.max&&e.push(this.$t("error.validation.max",{max:this.max})),null==(t=this.$refs.range)||t.setCustomValidity(e.join(", "))}},validations(){return{position:{required:!this.required||t.required,min:!this.min||t.minValue(this.min),max:!this.max||t.maxValue(this.max)}}}},(function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-range-input",attrs:{"data-disabled":t.disabled}},[e("input",t._b({ref:"range",attrs:{type:"range"},domProps:{value:t.position},on:{input:function(e){return t.$emit("input",e.target.valueAsNumber)}}},"input",{autofocus:t.autofocus,disabled:t.disabled,id:t.id,max:t.max,min:t.min,name:t.name,required:t.required,step:t.step},!1)),t.tooltip?e("output",{staticClass:"k-range-input-tooltip",attrs:{for:t.id}},[t.tooltip.before?e("span",{staticClass:"k-range-input-tooltip-before"},[t._v(t._s(t.tooltip.before))]):t._e(),e("span",{staticClass:"k-range-input-tooltip-text"},[t._v(t._s(t.label))]),t.tooltip.after?e("span",{staticClass:"k-range-input-tooltip-after"},[t._v(t._s(t.tooltip.after))]):t._e()]):t._e()])}),[]).exports;const dn=ct({mixins:[Ue,Fe,cn],inheritAttrs:!1,methods:{focus(){this.$refs.input.focus()}}},(function(){var t=this,e=t._self._c;return e("k-field",t._b({staticClass:"k-range-field",attrs:{input:t.id}},"k-field",t.$props,!1),[e("k-input",t._g(t._b({ref:"input",attrs:{type:"range"}},"k-input",t.$props,!1),t.$listeners))],1)}),[]).exports,pn={mixins:[Ie,nt,at],props:{ariaLabel:String,default:String,empty:{type:[Boolean,String],default:!0},value:{type:[String,Number,Boolean],default:""}}};const hn=ct({mixins:[De,pn],emits:["click","input"],data(){return{selected:this.value,listeners:{...this.$listeners,click:t=>this.onClick(t),change:t=>this.onInput(t.target.value),input:()=>{}}}},computed:{emptyOption(){return this.placeholder??"—"},hasEmptyOption(){return!1!==this.empty&&!(this.required&&this.default)},isEmpty(){return null===this.selected||void 0===this.selected||""===this.selected},label(){const t=this.text(this.selected);return""===this.selected||null===this.selected||null===t?this.emptyOption:t}},watch:{value(t){this.selected=t,this.onInvalid()}},mounted(){this.onInvalid(),this.$props.autofocus&&this.focus()},methods:{focus(){this.$refs.input.focus()},onClick(t){t.stopPropagation(),this.$emit("click",t)},onInvalid(){this.$emit("invalid",this.$v.$invalid,this.$v)},onInput(t){this.selected=t,this.$emit("input",this.selected)},select(){this.focus()},text(t){let e=null;for(const i of this.options)i.value==t&&(e=i.text);return e}},validations(){return{selected:{required:!this.required||t.required}}}},(function(){var t=this,e=t._self._c;return e("span",{staticClass:"k-select-input",attrs:{"data-disabled":t.disabled,"data-empty":t.isEmpty}},[e("select",t._g({ref:"input",staticClass:"k-select-input-native",attrs:{id:t.id,autofocus:t.autofocus,"aria-label":t.ariaLabel,disabled:t.disabled,name:t.name,required:t.required},domProps:{value:t.selected}},t.listeners),[t.hasEmptyOption?e("option",{attrs:{disabled:t.required,value:""}},[t._v(" "+t._s(t.emptyOption)+" ")]):t._e(),t._l(t.options,(function(i){return e("option",{key:i.value,attrs:{disabled:i.disabled},domProps:{value:i.value}},[t._v(" "+t._s(i.text)+" ")])}))],2),t._v(" "+t._s(t.label)+" ")])}),[]).exports;const mn=ct({mixins:[Fe,Ue,pn],inheritAttrs:!1,props:{icon:{type:String,default:"angle-down"}},methods:{focus(){this.$refs.input.focus()}}},(function(){var t=this,e=t._self._c;return e("k-field",t._b({staticClass:"k-select-field",attrs:{input:t.id}},"k-field",t.$props,!1),[e("k-input",t._g(t._b({ref:"input",attrs:{type:"select"}},"k-input",t.$props,!1),t.$listeners))],1)}),[]).exports,fn={mixins:[Li],props:{allow:{type:String,default:""},formData:{type:Object,default:()=>({})},sync:{type:String}}};const gn=ct({extends:Ti,mixins:[fn],data(){return{slug:this.sluggify(this.value),slugs:this.$panel.language.rules??this.$panel.system.slugs,syncValue:null}},watch:{formData:{handler(t){return!this.disabled&&(!(!this.sync||void 0===t[this.sync])&&(t[this.sync]!=this.syncValue&&(this.syncValue=t[this.sync],void this.onInput(this.sluggify(this.syncValue)))))},deep:!0,immediate:!0},value(t){(t=this.sluggify(t))!==this.slug&&(this.slug=t,this.$emit("input",this.slug))}},methods:{sluggify(t){return this.$helper.slug(t,[this.slugs,this.$panel.system.ascii],this.allow)},onInput(t){this.slug=this.sluggify(t),this.$emit("input",this.slug)}}},(function(){var t=this;return(0,t._self._c)("input",t._g(t._b({directives:[{name:"direction",rawName:"v-direction"}],ref:"input",staticClass:"k-text-input",attrs:{autocomplete:"off",spellcheck:"false",type:"text"},domProps:{value:t.slug}},"input",{autofocus:t.autofocus,disabled:t.disabled,id:t.id,minlength:t.minlength,name:t.name,pattern:t.pattern,placeholder:t.placeholder,required:t.required},!1),t.listeners))}),[]).exports;const kn=ct({mixins:[Fe,Ue,fn],inheritAttrs:!1,props:{icon:{type:String,default:"url"},path:{type:String},wizard:{type:[Boolean,Object],default:!1}},data(){return{slug:this.value}},computed:{preview(){return void 0!==this.help?this.help:void 0!==this.path?this.path+this.value:null}},watch:{value(){this.slug=this.value}},methods:{focus(){this.$refs.input.focus()},onWizard(){var t;let e=null==(t=this.wizard)?void 0:t.field;if(e){const t=this.formData[e.toLowerCase()];t&&(this.slug=t)}}}},(function(){var t=this,e=t._self._c;return e("k-field",t._b({staticClass:"k-slug-field",attrs:{input:t.id,help:t.preview},scopedSlots:t._u([t.wizard&&t.wizard.text?{key:"options",fn:function(){return[e("k-button",{attrs:{text:t.wizard.text,icon:"sparkling",size:"xs",variant:"filled"},on:{click:t.onWizard}})]},proxy:!0}:null],null,!0)},"k-field",t.$props,!1),[e("k-input",t._g(t._b({ref:"input",attrs:{value:t.slug,type:"slug"}},"k-input",t.$props,!1),t.$listeners))],1)}),[]).exports;const bn=ct({mixins:[Fe],inheritAttrs:!1,props:{autofocus:Boolean,columns:Object,duplicate:{type:Boolean,default:!0},empty:String,fields:[Array,Object],limit:Number,max:Number,min:Number,prepend:{type:Boolean,default:!1},sortable:{type:Boolean,default:!0},sortBy:String,value:{type:Array,default:()=>[]}},data:()=>({items:[],page:1}),computed:{dragOptions(){return{disabled:!this.isSortable,fallbackClass:"k-sortable-row-fallback"}},index(){return this.limit?(this.page-1)*this.limit+1:1},hasFields(){return this.$helper.object.length(this.fields)>0},isInvalid(){return!0!==this.disabled&&(!!(this.min&&this.items.lengththis.max))},isSortable(){return!this.sortBy&&(!this.limit&&(!0!==this.disabled&&(!(this.items.length<=1)&&!1!==this.sortable)))},more(){return!0!==this.disabled&&!(this.max&&this.items.length>=this.max)},pagination(){let t=0;return this.limit&&(t=(this.page-1)*this.limit),{page:this.page,offset:t,limit:this.limit,total:this.items.length,align:"center",details:!0}},options(){return this.disabled?[]:[{icon:"edit",text:this.$t("edit"),click:"edit"},{disabled:!this.duplicate||!this.more,icon:"copy",text:this.$t("duplicate"),click:"duplicate"},"-",{icon:"trash",text:this.$t("delete"),click:"remove"}]},paginatedItems(){return this.limit?this.items.slice(this.pagination.offset,this.pagination.offset+this.limit):this.items}},watch:{value:{handler(t){t!==this.items&&(this.items=this.toItems(t))},immediate:!0}},methods:{add(t=null){if(!1===this.more)return!1;(t=t??this.$helper.field.form(this.fields))._id=t._id??this.$helper.uuid(),!0===this.prepend?this.items.unshift(t):this.items.push(t),this.save(),this.open(t)},close(){this.$panel.drawer.close(this.id)},focus(){var t,e;null==(e=null==(t=this.$refs.add)?void 0:t.focus)||e.call(t)},form(t){const e=this.$helper.field.subfields(this,this.fields);if(t)for(const i in e)e[i].autofocus=i===t;return e},findIndex(t){return this.items.findIndex((e=>e._id===t._id))},navigate(t,e){const i=this.findIndex(t);!0!==this.disabled&&-1!==i&&this.open(this.items[i+e],null,!0)},open(t,e,i=!1){const s=this.findIndex(t);if(!0===this.disabled||-1===s)return!1;this.$panel.drawer.open({component:"k-structure-drawer",id:this.id,props:{icon:this.icon??"list-bullet",next:this.items[s+1],prev:this.items[s-1],tabs:{content:{fields:this.form(e)}},title:this.label,value:t},replace:i,on:{input:e=>{const i=this.findIndex(t);this.$panel.drawer.props.next=this.items[i+1],this.$panel.drawer.props.prev=this.items[i-1],this.$set(this.items,i,e),this.save()},next:()=>{this.navigate(t,1)},prev:()=>{this.navigate(t,-1)},remove:()=>{this.remove(t)}}})},option(t,e){switch(t){case"remove":this.remove(e);break;case"duplicate":this.add({...structuredClone(e),_id:this.$helper.uuid()});break;case"edit":this.open(e)}},onTableInput(t){this.limit&&(t=this.items.toSpliced(this.pagination.offset,this.limit,...t)),this.save(t)},paginate({page:t}){this.page=t},remove(t){const e=this.findIndex(t);this.disabled||-1===e||this.$panel.dialog.open({component:"k-remove-dialog",props:{text:this.$t("field.structure.delete.confirm")},on:{submit:()=>{this.items.splice(e,1),this.save(),this.$panel.dialog.close(),this.close(),0===this.paginatedItems.length&&this.page>1&&this.page--}}})},removeAll(){this.$panel.dialog.open({component:"k-remove-dialog",props:{text:this.$t("field.structure.delete.confirm.all")},on:{submit:()=>{this.page=1,this.items=[],this.save(),this.$panel.dialog.close()}}})},save(t=this.items){this.$emit("input",t)},sort(t){return this.sortBy?this.$helper.array.sortBy(t,this.sortBy):t},toItems(t){return!1===Array.isArray(t)?[]:(t=t.map((t=>({_id:t._id??this.$helper.uuid(),...t}))),this.sort(t))}}},(function(){var t=this,e=t._self._c;return e("k-field",t._b({staticClass:"k-structure-field",nativeOn:{click:function(t){t.stopPropagation()}},scopedSlots:t._u([t.hasFields&&!t.disabled?{key:"options",fn:function(){return[e("k-button-group",{attrs:{layout:"collapsed"}},[e("k-button",{attrs:{autofocus:t.autofocus,disabled:!t.more,responsive:!0,text:t.$t("add"),icon:"add",variant:"filled",size:"xs"},on:{click:function(e){return t.add()}}}),e("k-button",{attrs:{icon:"dots",size:"xs",variant:"filled"},on:{click:function(e){return t.$refs.options.toggle()}}}),e("k-dropdown-content",{ref:"options",attrs:{options:[{click:()=>t.add(),disabled:!t.more,icon:"add",text:t.$t("add")},{click:()=>t.removeAll(),disabled:0===t.items.length||t.disabled,icon:"trash",text:t.$t("delete.all")}],"align-x":"end"}})],1)]},proxy:!0}:null],null,!0)},"k-field",t.$props,!1),[t.hasFields?[0===t.items.length?e("k-empty",{attrs:{"data-invalid":t.isInvalid,icon:"list-bullet"},on:{click:function(e){return t.add()}}},[t._v(" "+t._s(t.empty??t.$t("field.structure.empty"))+" ")]):[e("k-table",{attrs:{columns:t.columns,disabled:t.disabled,fields:t.fields,empty:t.$t("field.structure.empty"),index:t.index,options:t.options,pagination:!!t.limit&&t.pagination,rows:t.paginatedItems,sortable:t.isSortable,"data-invalid":t.isInvalid},on:{cell:function(e){return t.open(e.row,e.columnIndex)},input:t.onTableInput,option:t.option,paginate:t.paginate}}),t.more?e("footer",[e("k-button",{attrs:{title:t.$t("add"),icon:"add",size:"xs",variant:"filled"},on:{click:function(e){return t.add()}}})],1):t._e()]]:[e("k-empty",{attrs:{icon:"list-bullet"}},[t._v(t._s(t.$t("fields.empty")))])]],2)}),[]).exports,vn={mixins:[Li],props:{autocomplete:{default:"tel"},placeholder:{default:()=>window.panel.$t("tel.placeholder")},type:{default:"tel"}}};const yn=ct({extends:Ti,mixins:[vn]},null,null).exports;const $n=ct({mixins:[Fe,Ue,vn],inheritAttrs:!1,props:{icon:{type:String,default:"phone"}},methods:{focus(){this.$refs.input.focus()}}},(function(){var t=this,e=t._self._c;return e("k-field",t._b({staticClass:"k-tel-field",attrs:{input:t.id}},"k-field",t.$props,!1),[e("k-input",t._g(t._b({ref:"input",attrs:{type:"tel"}},"k-input",t.$props,!1),t.$listeners))],1)}),[]).exports;const wn=ct({mixins:[Fe,Ue,Li,xi],inheritAttrs:!1,computed:{inputType(){return this.$helper.isComponent(`k-${this.type}-input`)?this.type:"text"}},methods:{focus(){this.$refs.input.focus()},select(){this.$refs.input.select()}}},(function(){var t=this,e=t._self._c;return e("k-field",t._b({staticClass:"k-text-field",attrs:{input:t.id,counter:t.counterOptions},scopedSlots:t._u([{key:"options",fn:function(){return[t._t("options")]},proxy:!0}],null,!0)},"k-field",t.$props,!1),[e("k-input",t._g(t._b({ref:"input",attrs:{type:t.inputType}},"k-input",t.$props,!1),t.$listeners))],1)}),[]).exports,xn={props:{buttons:{type:[Array,Boolean],default:!0},uploads:[Boolean,Object,Array]}};const _n=ct({mixins:[xn],emits:["command"],computed:{commands(){return{headlines:{label:this.$t("toolbar.button.headings"),icon:"title",dropdown:[{label:this.$t("toolbar.button.heading.1"),icon:"h1",click:()=>this.command("prepend","#")},{label:this.$t("toolbar.button.heading.2"),icon:"h2",click:()=>this.command("prepend","##")},{label:this.$t("toolbar.button.heading.3"),icon:"h3",click:()=>this.command("prepend","###")}]},bold:{label:this.$t("toolbar.button.bold"),icon:"bold",click:()=>this.command("toggle","**"),shortcut:"b"},italic:{label:this.$t("toolbar.button.italic"),icon:"italic",click:()=>this.command("toggle","*"),shortcut:"i"},link:{label:this.$t("toolbar.button.link"),icon:"url",click:()=>this.command("dialog","link"),shortcut:"k"},email:{label:this.$t("toolbar.button.email"),icon:"email",click:()=>this.command("dialog","email"),shortcut:"e"},file:{label:this.$t("toolbar.button.file"),icon:"attachment",click:()=>this.command("file"),dropdown:this.uploads?[{label:this.$t("toolbar.button.file.select"),icon:"check",click:()=>this.command("file")},{label:this.$t("toolbar.button.file.upload"),icon:"upload",click:()=>this.command("upload")}]:void 0},code:{label:this.$t("toolbar.button.code"),icon:"code",click:()=>this.command("toggle","`")},ul:{label:this.$t("toolbar.button.ul"),icon:"list-bullet",click:()=>this.command("insert",((t,e)=>e.split("\n").map((t=>"- "+t)).join("\n")))},ol:{label:this.$t("toolbar.button.ol"),icon:"list-numbers",click:()=>this.command("insert",((t,e)=>e.split("\n").map(((t,e)=>e+1+". "+t)).join("\n")))}}},default:()=>["headlines","|","bold","italic","code","|","link","email","file","|","ul","ol"],layout(){if(!1===this.buttons)return[];const t=[],e=Array.isArray(this.buttons)?this.buttons:this.default,i={...this.commands,...window.panel.plugins.textareaButtons??{}};for(const s of e)if("|"===s)t.push("|");else if(i[s]){const e={...i[s],click:()=>{var t;null==(t=i[s].click)||t.call(this)}};t.push(e)}return t}},methods:{close(){this.$refs.toolbar.close()},command(t,...e){this.$emit("command",t,...e)},shortcut(t,e){var i;const s=this.layout.find((e=>e.shortcut===t));s&&(e.preventDefault(),null==(i=s.click)||i.call(s))}}},(function(){return(0,this._self._c)("k-toolbar",{ref:"toolbar",staticClass:"k-textarea-toolbar",attrs:{buttons:this.layout}})}),[]).exports,Cn={mixins:[xn,Ie,J,et,it,at,lt],props:{endpoints:Object,preselect:Boolean,size:String,theme:String,value:String}};const Sn=ct({mixins:[De,Cn],emits:["focus","input","submit"],data:()=>({over:!1}),computed:{uploadOptions(){const t=this.restoreSelectionCallback();return{url:this.$panel.urls.api+"/"+this.endpoints.field+"/upload",multiple:!1,on:{cancel:t,done:e=>{t((()=>this.insertUpload(e)))}}}}},watch:{async value(){this.onInvalid(),await this.$nextTick(),this.$library.autosize.update(this.$refs.input)}},async mounted(){await this.$nextTick(),this.$library.autosize(this.$refs.input),this.onInvalid(),this.$props.autofocus&&this.focus(),this.$props.preselect&&this.select()},methods:{dialog(t){const e=this.restoreSelectionCallback();this.$panel.dialog.open({component:"k-toolbar-"+t+"-dialog",props:{value:this.parseSelection()},on:{cancel:e,submit:t=>{this.$panel.dialog.close(),e((()=>this.insert(t)))}}})},file(){const t=this.restoreSelectionCallback();this.$panel.dialog.open({component:"k-files-dialog",props:{endpoint:this.endpoints.field+"/files",multiple:!1},on:{cancel:t,submit:e=>{t((()=>this.insertFile(e))),this.$panel.dialog.close()}}})},focus(){this.$refs.input.focus()},insert(t){const e=this.$refs.input,i=e.value;"function"==typeof t&&(t=t(this.$refs.input,this.selection())),setTimeout((()=>{if(e.focus(),document.execCommand("insertText",!1,t),e.value===i){const i=e.selectionStart,s=e.selectionEnd,n=i===s?"end":"select";e.setRangeText(t,i,s,n)}this.$emit("input",e.value)}))},insertFile(t){(null==t?void 0:t.length)>0&&this.insert(t.map((t=>t.dragText)).join("\n\n"))},insertUpload(t){this.insertFile(t),this.$events.emit("model.update")},onCommand(t,...e){if("function"!=typeof this[t])return console.warn(t+" is not a valid command");this[t](...e)},onDrop(t){if(this.uploads&&this.$helper.isUploadEvent(t))return this.$panel.upload.open(t.dataTransfer.files,this.uploadOptions);"text"===this.$panel.drag.type&&(this.focus(),this.insert(this.$panel.drag.data))},onFocus(t){this.$emit("focus",t)},onInput(t){this.$emit("input",t.target.value)},onInvalid(){this.$emit("invalid",this.$v.$invalid,this.$v)},onOut(){this.$refs.input.blur(),this.over=!1},onOver(t){if(this.uploads&&this.$helper.isUploadEvent(t))return t.dataTransfer.dropEffect="copy",this.focus(),void(this.over=!0);"text"===this.$panel.drag.type&&(t.dataTransfer.dropEffect="copy",this.focus(),this.over=!0)},onShortcut(t){var e;!1!==this.buttons&&"Meta"!==t.key&&"Control"!==t.key&&(null==(e=this.$refs.toolbar)||e.shortcut(t.key,t))},onSubmit(t){return this.$emit("submit",t)},parseSelection(){const t=this.selection();if(0===(null==t?void 0:t.length))return{href:null,title:null};let e;e=this.$panel.config.kirbytext?/^\(link:\s*(?.*?)(?:\s*text:\s*(?.*?))?\)$/is:/^(\[(?.*?)\]\((?.*?)\))|(<(?.*?)>)$/is;const i=e.exec(t);return null!==i?{href:i.groups.url??i.groups.link,title:i.groups.text??null}:{href:null,title:t}},prepend(t){this.insert(t+" "+this.selection())},restoreSelectionCallback(){const t=this.$refs.input.selectionStart,e=this.$refs.input.selectionEnd;return i=>{setTimeout((()=>{this.$refs.input.setSelectionRange(t,e),i&&i()}))}},select(){this.$refs.select()},selection(){return this.$refs.input.value.substring(this.$refs.input.selectionStart,this.$refs.input.selectionEnd)},toggle(t,e){e=e??t;const i=this.selection();return i.startsWith(t)&&i.endsWith(e)?this.insert(i.slice(t.length).slice(0,i.length-t.length-e.length)):this.wrap(t,e)},upload(){this.$panel.upload.pick(this.uploadOptions)},wrap(t,e){this.insert(t+this.selection()+(e??t))}},validations(){return{value:{required:!this.required||t.required,minLength:!this.minlength||t.minLength(this.minlength),maxLength:!this.maxlength||t.maxLength(this.maxlength)}}}},(function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-textarea-input",attrs:{"data-over":t.over,"data-size":t.size}},[e("div",{staticClass:"k-textarea-input-wrapper"},[t.buttons&&!t.disabled?e("k-textarea-toolbar",{ref:"toolbar",attrs:{buttons:t.buttons,disabled:t.disabled,uploads:t.uploads},on:{command:t.onCommand},nativeOn:{mousedown:function(t){t.preventDefault()}}}):t._e(),e("textarea",t._b({directives:[{name:"direction",rawName:"v-direction"}],ref:"input",staticClass:"k-textarea-input-native",attrs:{"data-font":t.font},on:{click:function(e){var i;null==(i=t.$refs.toolbar)||i.close()},focus:t.onFocus,input:t.onInput,keydown:[function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"enter",13,e.key,"Enter")?null:e.metaKey?t.onSubmit.apply(null,arguments):null},function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"enter",13,e.key,"Enter")?null:e.ctrlKey?t.onSubmit.apply(null,arguments):null},function(e){return e.metaKey?e.ctrlKey||e.shiftKey||e.altKey?null:t.onShortcut.apply(null,arguments):null},function(e){return e.ctrlKey?e.shiftKey||e.altKey||e.metaKey?null:t.onShortcut.apply(null,arguments):null}],dragover:t.onOver,dragleave:t.onOut,drop:t.onDrop}},"textarea",{autofocus:t.autofocus,disabled:t.disabled,id:t.id,minlength:t.minlength,name:t.name,placeholder:t.placeholder,required:t.required,spellcheck:t.spellcheck,value:t.value},!1))],1)])}),[]).exports;const On=ct({mixins:[Fe,Ue,Cn,xi],inheritAttrs:!1,methods:{focus(){this.$refs.input.focus()}}},(function(){var t=this,e=t._self._c;return e("k-field",t._b({staticClass:"k-textarea-field",attrs:{input:t.id,counter:t.counterOptions}},"k-field",t.$props,!1),[e("k-input",t._g(t._b({ref:"input",attrs:{type:"textarea"}},"k-input",t.$props,!1),t.$listeners))],1)}),[]).exports,Mn={props:{max:String,min:String,value:String}},An={mixins:[Mn],props:{display:{type:String,default:"HH:mm"},step:{type:Object,default:()=>({size:5,unit:"minute"})},type:{type:String,default:"time"}}};const In=ct({mixins:[ji,An],computed:{inputType:()=>"time"}},null,null).exports;const Dn=ct({mixins:[Fe,Ue,An],inheritAttrs:!1,props:{icon:{type:String,default:"clock"},times:{type:Boolean,default:!0}},methods:{focus(){this.$refs.input.focus()},select(t){var e;this.$emit("input",t),null==(e=this.$refs.times)||e.close()}}},(function(){var t=this,e=t._self._c;return e("k-field",t._b({staticClass:"k-time-field",attrs:{input:t.id}},"k-field",t.$props,!1),[e("k-input",t._b({ref:"input",attrs:{type:"time"},on:{input:function(e){return t.$emit("input",e??"")}},scopedSlots:t._u([t.times?{key:"icon",fn:function(){return[e("k-button",{staticClass:"k-input-icon-button",attrs:{disabled:t.disabled,icon:t.icon??"clock",title:t.$t("time.select")},on:{click:function(e){return t.$refs.times.toggle()}}}),e("k-dropdown-content",{ref:"times",attrs:{"align-x":"end"}},[e("k-timeoptions-input",{attrs:{display:t.display,value:t.value},on:{input:t.select}})],1)]},proxy:!0}:null],null,!0)},"k-input",t.$props,!1))],1)}),[]).exports,jn={props:{autofocus:Boolean,disabled:Boolean,id:[Number,String],text:{type:[Array,String]},required:Boolean,value:Boolean}};const En=ct({mixins:[De,jn],computed:{label(){const t=this.text??[this.$t("off"),this.$t("on")];return Array.isArray(t)?this.value?t[1]:t[0]:t}},watch:{value(){this.onInvalid()}},mounted(){this.onInvalid(),this.$props.autofocus&&this.focus()},methods:{onEnter(t){"Enter"===t.key&&this.$refs.input.click()},onInput(t){this.$emit("input",t)},onInvalid(){this.$emit("invalid",this.$v.$invalid,this.$v)},select(){this.$refs.input.focus()}},validations(){return{value:{required:!this.required||t.required}}}},(function(){var t=this;return(0,t._self._c)("k-choice-input",{ref:"input",staticClass:"k-toggle-input",attrs:{id:t.id,checked:t.value,disabled:t.disabled,label:t.label,type:"checkbox",variant:"toggle"},on:{input:function(e){return t.$emit("input",e)}}})}),[]).exports;const Ln=ct({mixins:[Fe,Ue,jn],inheritAttrs:!1,methods:{focus(){this.$refs.input.focus()}}},(function(){var t=this,e=t._self._c;return e("k-field",t._b({staticClass:"k-toggle-field",attrs:{input:t.id}},"k-field",t.$props,!1),[e("k-input",t._g(t._b({ref:"input",attrs:{type:"toggle"}},"k-input",t.$props,!1),t.$listeners))],1)}),[]).exports,Tn={mixins:[Ie],props:{columns:Number,grow:Boolean,labels:Boolean,options:Array,reset:Boolean,value:[String,Number,Boolean]}};const Bn=ct({mixins:[De,Tn],watch:{value(){this.onInvalid()}},mounted(){this.onInvalid(),this.$props.autofocus&&this.focus()},methods:{focus(){var t;null==(t=this.$el.querySelector("input[checked]")||this.$el.querySelector("input"))||t.focus()},onClick(t){t===this.value&&this.reset&&!this.required&&this.$emit("input","")},onInput(t){this.$emit("input",t)},onInvalid(){this.$emit("invalid",this.$v.$invalid,this.$v)},select(){this.focus()}},validations(){return{value:{required:!this.required||t.required}}}},(function(){var t=this,e=t._self._c;return e("ul",{staticClass:"k-toggles-input",style:{"--options":t.columns??t.options.length},attrs:{"data-invalid":t.$v.$invalid,"data-labels":t.labels}},t._l(t.options,(function(i,s){return e("li",{key:s,attrs:{"data-disabled":t.disabled}},[e("input",{staticClass:"input-hidden",attrs:{id:t.id+"-"+s,"aria-label":i.text,disabled:t.disabled,name:t.id,type:"radio"},domProps:{value:i.value,checked:t.value===i.value},on:{click:function(e){return t.onClick(i.value)},change:function(e){return t.onInput(i.value)}}}),e("label",{attrs:{for:t.id+"-"+s,title:i.text}},[i.icon?e("k-icon",{attrs:{type:i.icon}}):t._e(),t.labels||!i.icon?e("span",{staticClass:"k-toggles-text",domProps:{innerHTML:t._s(i.text)}}):t._e()],1)])})),0)}),[]).exports;const qn=ct({mixins:[Fe,Ue,Tn],inheritAttrs:!1,methods:{focus(){this.$refs.input.focus()},onInput(t){this.$emit("input",t)}}},(function(){var t,e=this,i=e._self._c;return i("k-field",e._b({staticClass:"k-toggles-field",attrs:{input:e.id}},"k-field",e.$props,!1),[(null==(t=e.options)?void 0:t.length)?i("k-input",e._g(e._b({ref:"input",class:{grow:e.grow},attrs:{type:"toggles"}},"k-input",e.$props,!1),e.$listeners)):i("k-empty",{attrs:{text:e.$t("options.none"),icon:"checklist"}})],1)}),[]).exports,Pn={mixins:[Li],props:{autocomplete:{type:String,default:"url"},placeholder:{type:String,default:()=>window.panel.$t("url.placeholder")},type:{type:String,default:"url"}}};const Nn=ct({extends:Ti,mixins:[Pn]},null,null).exports;const zn=ct({mixins:[Fe,Ue,Pn],inheritAttrs:!1,props:{link:{type:Boolean,default:!0},icon:{type:String,default:"url"}},computed:{isValidUrl(){return""!==this.value&&!0===this.$helper.url.isUrl(this.value,!0)}},methods:{focus(){this.$refs.input.focus()}}},(function(){var t=this,e=t._self._c;return e("k-field",t._b({staticClass:"k-url-field",attrs:{input:t.id}},"k-field",t.$props,!1),[e("k-input",t._g(t._b({ref:"input",attrs:{type:"url"},scopedSlots:t._u([{key:"icon",fn:function(){return[t.link&&t.isValidUrl?e("k-button",{staticClass:"k-input-icon-button",attrs:{icon:t.icon,link:t.value,title:t.$t("open"),tabindex:"-1",target:"_blank"}}):t._e()]},proxy:!0}])},"k-input",t.$props,!1),t.$listeners))],1)}),[]).exports;const Fn=ct({extends:Ni,type:"users",computed:{emptyProps(){return{icon:"users",text:this.empty??(this.multiple&&1!==this.max?this.$t("field.users.empty"):this.$t("field.users.empty.single"))}}}},null,null).exports;const Yn=ct({mixins:[Fe,Ue,qs,xi],inheritAttrs:!1,computed:{counterValue(){const t=this.$helper.string.stripHTML(this.value??"");return this.$helper.string.unescapeHTML(t)}},methods:{focus(){this.$refs.input.focus()}}},(function(){var t=this,e=t._self._c;return e("k-field",t._b({staticClass:"k-writer-field",attrs:{input:t.id,counter:t.counterOptions}},"k-field",t.$props,!1),[e("k-input",t._b({ref:"input",attrs:{after:t.after,before:t.before,icon:t.icon,type:"writer"},on:{input:function(e){return t.$emit("input",e)}}},"k-input",t.$props,!1))],1)}),[]).exports,Rn={install(t){t.component("k-blocks-field",yi),t.component("k-checkboxes-field",_i),t.component("k-color-field",Ai),t.component("k-date-field",Ei),t.component("k-email-field",Pi),t.component("k-files-field",zi),t.component("k-gap-field",Fi),t.component("k-headline-field",Yi),t.component("k-info-field",Ri),t.component("k-layout-field",Gi),t.component("k-line-field",Xi),t.component("k-link-field",Zi),t.component("k-list-field",Ys),t.component("k-multiselect-field",Gs),t.component("k-number-field",Qs),t.component("k-object-field",tn),t.component("k-pages-field",en),t.component("k-password-field",on),t.component("k-radio-field",ln),t.component("k-range-field",dn),t.component("k-select-field",mn),t.component("k-slug-field",kn),t.component("k-structure-field",bn),t.component("k-tags-field",Js),t.component("k-text-field",wn),t.component("k-textarea-field",On),t.component("k-tel-field",$n),t.component("k-time-field",Dn),t.component("k-toggle-field",Ln),t.component("k-toggles-field",qn),t.component("k-url-field",zn),t.component("k-users-field",Fn),t.component("k-writer-field",Yn)}},Un={mixins:[cn],props:{max:null,min:null,step:{default:.01,type:Number},tooltip:{default:!1,type:[Boolean,Object]}}};const Hn=ct({mixins:[un,Un]},(function(){var t=this;return(0,t._self._c)("k-range-input",t._b({staticClass:"k-alpha-input",attrs:{min:0,max:1},on:{input:function(e){return t.$emit("input",e)}}},"k-range-input",t.$props,!1))}),[]).exports;const Vn=ct({mixins:[Ie,Ii],data(){const t=this.$library.dayjs();return{maxdate:null,mindate:null,month:t.month(),selected:null,today:t,year:t.year()}},computed:{numberOfDays(){return this.toDate().daysInMonth()},firstWeekday(){const t=this.toDate().day();return t>0?t:7},weekdays(){return["mon","tue","wed","thu","fri","sat","sun"].map((t=>this.$t("days."+t)))},weeks(){const t=this.firstWeekday-1;return Math.ceil((this.numberOfDays+t)/7)},monthnames(){return["january","february","march","april","may","june","july","august","september","october","november","december"].map((t=>this.$t("months."+t)))},months(){var t=[];return this.monthnames.forEach(((e,i)=>{t.push({value:i,text:e})})),t},years(){const t=this.year-20,e=this.year+20;return this.toOptions(t,e)}},watch:{max:{handler(t,e){t!==e&&(this.maxdate=this.$library.dayjs.interpret(t,"date"))},immediate:!0},min:{handler(t,e){t!==e&&(this.mindate=this.$library.dayjs.interpret(t,"date"))},immediate:!0},value:{handler(t,e){t!==e&&(this.selected=this.$library.dayjs.interpret(t,"date"),this.show(this.selected))},immediate:!0}},methods:{days(t){let e=[];const i=7*(t-1)+1,s=i+7;for(let n=i;nthis.numberOfDays;e.push(i?"":t)}return e},isDisabled(t){const e=this.toDate(t);return this.disabled||e.isBefore(this.mindate,"day")||e.isAfter(this.maxdate,"day")},isSelected(t){return this.toDate(t).isSame(this.selected,"day")},isToday(t){return this.toDate(t).isSame(this.today,"day")},onNext(){const t=this.toDate().add(1,"month");this.show(t)},onPrev(){const t=this.toDate().subtract(1,"month");this.show(t)},select(t){this.$emit("input",(null==t?void 0:t.toISO("date"))??null)},show(t){this.month=(t??this.today).month(),this.year=(t??this.today).year()},toDate(t=1,e){return this.$library.dayjs(`${this.year}-${(e??this.month)+1}-${t}`)},toOptions(t,e){for(var i=[],s=t;s<=e;s++)i.push({value:s,text:this.$helper.pad(s)});return i}}},(function(){var t=this,e=t._self._c;return e("fieldset",{staticClass:"k-calendar-input",on:{click:function(t){t.stopPropagation()}}},[e("legend",{staticClass:"sr-only"},[t._v(t._s(t.$t("date.select")))]),e("nav",[e("k-button",{attrs:{title:t.$t("prev"),icon:"angle-left"},on:{click:t.onPrev}}),e("span",{staticClass:"k-calendar-selects"},[e("k-select-input",{attrs:{"aria-label":t.$t("month"),autofocus:t.autofocus,options:t.months,empty:!1,required:!0,value:t.month},on:{input:function(e){t.month=Number(e)}}}),e("k-select-input",{attrs:{"aria-label":t.$t("year"),options:t.years,empty:!1,required:!0,value:t.year},on:{input:function(e){t.year=Number(e)}}})],1),e("k-button",{attrs:{title:t.$t("next"),icon:"angle-right"},on:{click:t.onNext}})],1),e("table",{key:t.year+"-"+t.month,staticClass:"k-calendar-table"},[e("thead",[e("tr",t._l(t.weekdays,(function(i){return e("th",{key:"weekday_"+i},[t._v(" "+t._s(i)+" ")])})),0)]),e("tbody",t._l(t.weeks,(function(i){return e("tr",{key:"week_"+i},t._l(t.days(i),(function(i,s){return e("td",{key:"day_"+s,staticClass:"k-calendar-day",attrs:{"aria-current":!!t.isToday(i)&&"date","aria-selected":!!t.isSelected(i)&&"date"}},[i?e("k-button",{attrs:{disabled:t.isDisabled(i),text:i},on:{click:function(e){t.select(t.toDate(i))}}}):t._e()],1)})),0)})),0),e("tfoot",[e("tr",[e("td",{staticClass:"k-calendar-today",attrs:{colspan:"7"}},[e("k-button",{attrs:{disabled:t.disabled,text:t.$t("today")},on:{click:function(e){t.show(t.today),t.select(t.today)}}})],1)])])]),e("input",{staticClass:"input-hidden",attrs:{id:t.id,disabled:t.disabled,min:t.min,max:t.max,name:t.name,required:t.required,tabindex:"-1",type:"date"},domProps:{value:t.value}})])}),[]).exports;const Kn=ct({mixins:[De,{mixins:[Ie],props:{checked:{type:Boolean},info:{type:String},label:{type:String},type:{default:"checkbox",type:String},value:{type:[Boolean,Number,String]},variant:{type:String}}}]},(function(){var t=this,e=t._self._c;return e("label",{staticClass:"k-choice-input",attrs:{"aria-disabled":t.disabled}},[e("input",t._b({class:{"sr-only":"invisible"===t.variant},attrs:{"data-variant":t.variant},on:{input:function(e){return t.$emit("input",e.target.checked)}}},"input",{autofocus:t.autofocus,id:t.id,checked:t.checked,disabled:t.disabled,name:t.name,required:t.required,type:t.type,value:t.value},!1)),t.label||t.info?e("span",{staticClass:"k-choice-input-label"},[e("span",{staticClass:"k-choice-input-label-text",domProps:{innerHTML:t._s(t.label)}}),t.info?e("span",{staticClass:"k-choice-input-label-info",domProps:{innerHTML:t._s(t.info)}}):t._e()]):t._e()])}),[]).exports;const Wn=ct({extends:Kn},null,null).exports;const Jn=ct({mixins:[rn,{mixins:[an],props:{format:{type:String,default:"hex",validator:t=>["hex","rgb","hsl"].includes(t)},value:{type:String}}}],computed:{choices(){return this.options.map((t=>({...t,title:t.text??t.value,value:this.colorToString(t.value)})))}},methods:{colorToString(t){try{return this.$library.colors.toString(t,this.format)}catch{return t}}}},(function(){var t=this,e=t._self._c;return t.choices.length?e("fieldset",{staticClass:"k-coloroptions-input",attrs:{disabled:t.disabled}},[e("legend",{staticClass:"sr-only"},[t._v(t._s(t.$t("options")))]),e("ul",t._l(t.choices,(function(i,s){return e("li",{key:s},[e("label",{attrs:{title:i.title}},[e("input",{staticClass:"input-hidden",attrs:{autofocus:t.autofocus&&0===s,disabled:t.disabled,name:t.name??t.id,required:t.required,type:"radio"},domProps:{checked:i.value===t.value,value:i.value},on:{click:function(e){return t.toggle(i.value)},input:function(e){return t.$emit("input",i.value)}}}),e("k-color-frame",{attrs:{color:i.value}})],1)])})),0)]):t._e()}),[]).exports;const Gn=ct({mixins:[De,{mixins:[Ie,nt],props:{alpha:{default:!0,type:Boolean},format:{default:"hex",type:String,validator:t=>["hex","rgb","hsl"].includes(t)},value:{type:[Object,String]}}}],data:()=>({color:{h:0,s:0,v:1,a:1},formatted:null}),computed:{coords(){return this.value?{x:100*this.color.s,y:100*(1-this.color.v)}:null},hsl(){try{const t=this.$library.colors.convert(this.color,"hsl");return{h:t.h,s:(100*t.s).toFixed()+"%",l:(100*t.l).toFixed()+"%",a:t.a}}catch{return{h:0,s:"0%",l:"0%",a:1}}}},watch:{value:{handler(t,e){if(t===e||t===this.formatted)return;const i=this.$library.colors.parseAs(t??"","hsv");i?(this.formatted=this.$library.colors.toString(i,this.format),this.color=i):(this.formatted=null,this.color={h:0,s:0,v:1,a:1})},immediate:!0}},methods:{between:(t,e,i)=>Math.min(Math.max(t,e),i),emit(){return this.formatted=this.$library.colors.toString(this.color,this.format),this.$emit("input",this.formatted)},focus(){this.$refs.coords.focus()},setAlpha(t){this.color.a=this.alpha?this.between(Number(t),0,1):1,this.emit()},setCoords(t){if(!t)return this.$emit("input","");const e=Math.round(t.x),i=Math.round(t.y);this.color.s=this.between(e/100,0,1),this.color.v=this.between(1-i/100,0,1),this.emit()},setHue(t){this.color.h=this.between(Number(t),0,360),this.emit()}}},(function(){var t=this,e=t._self._c;return e("fieldset",{staticClass:"k-colorpicker-input",style:{"--h":t.hsl.h,"--s":t.hsl.s,"--l":t.hsl.l,"--a":t.hsl.a}},[e("legend",{staticClass:"sr-only"},[t._v(t._s(t.$t("color")))]),e("k-coords-input",{ref:"coords",attrs:{autofocus:t.autofocus,disabled:t.disabled,required:t.required,value:t.coords},on:{input:function(e){return t.setCoords(e)}}}),e("label",{attrs:{"aria-label":t.$t("hue")}},[e("k-hue-input",{attrs:{disabled:t.disabled,required:t.required,value:t.color.h},on:{input:function(e){return t.setHue(e)}}})],1),t.alpha?e("label",{attrs:{"aria-label":t.$t("alpha")}},[e("k-alpha-input",{attrs:{disabled:t.disabled,required:t.required,value:t.color.a},on:{input:function(e){return t.setAlpha(e)}}})],1):t._e(),e("k-coloroptions-input",{attrs:{disabled:t.disabled,format:t.format,options:t.options,required:t.required,value:t.value},on:{input:function(e){return t.$emit("input",e)}}}),e("input",{staticClass:"input-hidden",attrs:{name:t.name,required:t.required,tabindex:"-1",type:"text"},domProps:{value:t.formatted}})],1)}),[]).exports;const Xn=ct({mixins:[De,{mixins:[Ie],props:{reset:{default:!0,type:Boolean},value:{default:()=>({x:0,y:0}),type:Object}}}],data:()=>({x:0,y:0}),watch:{value:{handler(t){const e=this.parse(t);this.x=(null==e?void 0:e.x)??0,this.y=(null==e?void 0:e.y)??0},immediate:!0}},methods:{focus(){var t;null==(t=this.$el.querySelector("button"))||t.focus()},getCoords:(t,e)=>({x:Math.min(Math.max(t.clientX-e.left,0),e.width),y:Math.min(Math.max(t.clientY-e.top,0),e.height)}),onDelete(){this.reset&&!this.required&&this.$emit("input",null)},onDrag(t){if(0!==t.button)return;const e=t=>this.onMove(t),i=()=>{window.removeEventListener("mousemove",e),window.removeEventListener("mouseup",i)};window.addEventListener("mousemove",e),window.addEventListener("mouseup",i)},onEnter(){var t;null==(t=this.$el.form)||t.requestSubmit()},onInput(t,e){if(t.preventDefault(),t.stopPropagation(),this.disabled)return!1;this.x=Math.min(Math.max(parseFloat(e.x??this.x??0),0),100),this.y=Math.min(Math.max(parseFloat(e.y??this.y??0),0),100),this.$emit("input",{x:this.x,y:this.y})},onKeys(t){const e=t.shiftKey?10:1,i={ArrowUp:{y:this.y-e},ArrowDown:{y:this.y+e},ArrowLeft:{x:this.x-e},ArrowRight:{x:this.x+e}};i[t.key]&&this.onInput(t,i[t.key])},async onMove(t){const e=this.$el.getBoundingClientRect(),i=this.getCoords(t,e),s=i.x/e.width*100,n=i.y/e.height*100;this.onInput(t,{x:s,y:n}),await this.$nextTick(),this.focus()},parse(t){if("object"==typeof t)return t;const e={"top left":{x:0,y:0},"top center":{x:50,y:0},"top right":{x:100,y:0},"center left":{x:0,y:50},center:{x:50,y:50},"center center":{x:50,y:50},"center right":{x:100,y:50},"bottom left":{x:0,y:100},"bottom center":{x:50,y:100},"bottom right":{x:100,y:100}};if(e[t])return e[t];const i=t.split(",").map((t=>t.trim()));return{x:i[0],y:i[1]??0}}}},(function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-coords-input",attrs:{"aria-disabled":t.disabled,"data-empty":!t.value},on:{mousedown:t.onDrag,click:t.onMove,keydown:t.onKeys}},[t._t("default"),e("button",{staticClass:"k-coords-input-thumb",style:{left:`${t.x}%`,top:`${t.y}%`},attrs:{id:t.id,autofocus:t.autofocus,disabled:t.disabled},on:{keydown:[function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"enter",13,e.key,"Enter")?null:(e.preventDefault(),t.onEnter.apply(null,arguments))},function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"delete",[8,46],e.key,["Backspace","Delete","Del"])?null:t.onDelete.apply(null,arguments)}]}}),e("input",{staticClass:"input-hidden",attrs:{name:t.name,required:t.required,tabindex:"-1",type:"text"},domProps:{value:t.value?[t.value.x,t.value.y]:null}})],2)}),[]).exports,Zn={mixins:[cn],props:{max:null,min:null,step:{default:1,type:Number},tooltip:{default:!1,type:[Boolean,Object]}}};const Qn=ct({mixins:[un,Zn]},(function(){var t=this;return(0,t._self._c)("k-range-input",t._b({staticClass:"k-hue-input",attrs:{min:0,max:360},on:{input:function(e){return t.$emit("input",e)}}},"k-range-input",t.$props,!1))}),[]).exports;const to=ct({mixins:[Si,{mixins:[Ci],props:{autocomplete:null,pattern:null,spellcheck:null,placeholder:{default:()=>window.panel.$t("search")+" …",type:String}}}]},(function(){var t=this;return(0,t._self._c)("k-string-input",t._b({staticClass:"k-search-input",attrs:{spellcheck:!1,autocomplete:"off",type:"search"},on:{input:function(e){return t.$emit("input",e)}}},"k-string-input",t.$props,!1))}),[]).exports;const eo=ct({mixins:[De,{mixins:[Ie,Mn]}],props:{display:{type:String,default:"HH:mm"},value:String},computed:{day(){return this.formatTimes([6,7,8,9,10,11,"-",12,13,14,15,16,17])},night(){return this.formatTimes([18,19,20,21,22,23,"-",0,1,2,3,4,5])}},methods:{focus(){this.$el.querySelector("button").focus()},formatTimes(t){return t.map((t=>{if("-"===t)return t;const e=this.$library.dayjs(t+":00","H:mm");return{display:e.format(this.display),select:e.toISO("time")}}))},select(t){this.$emit("input",t)}}},(function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-timeoptions-input"},[e("div",[e("h3",[e("k-icon",{attrs:{type:"sun"}}),t._v(" "),e("span",{staticClass:"sr-only"},[t._v(t._s(t.$t("day")))])],1),e("ul",t._l(t.day,(function(i,s){return e("li",{key:i.select},["-"===i?e("hr"):e("k-button",{attrs:{autofocus:t.autofocus&&0===s,disabled:t.disabled,selected:i.select===t.value&&"time"},on:{click:function(e){return t.select(i.select)}}},[t._v(" "+t._s(i.display)+" ")])],1)})),0)]),e("div",[e("h3",[e("k-icon",{attrs:{type:"moon"}}),t._v(" "),e("span",{staticClass:"sr-only"},[t._v(t._s(t.$t("night")))])],1),e("ul",t._l(t.night,(function(i){return e("li",{key:i.select},["-"===i?e("hr"):e("k-button",{attrs:{disabled:t.disabled,selected:i.select===t.value&&"time"},on:{click:function(e){return t.select(i.select)}}},[t._v(" "+t._s(i.display)+" ")])],1)})),0)]),e("input",{staticClass:"input-hidden",attrs:{id:t.id,disabled:t.disabled,min:t.min,max:t.max,name:t.name,required:t.required,tabindex:"-1",type:"time"},domProps:{value:t.value}})])}),[]).exports,io={install(t){t.component("k-alpha-input",Hn),t.component("k-calendar-input",Vn),t.component("k-checkbox-input",Wn),t.component("k-checkboxes-input",wi),t.component("k-choice-input",Kn),t.component("k-colorname-input",Mi),t.component("k-coloroptions-input",Jn),t.component("k-colorpicker-input",Gn),t.component("k-coords-input",Xn),t.component("k-date-input",ji),t.component("k-email-input",qi),t.component("k-hue-input",Qn),t.component("k-list-input",Fs),t.component("k-multiselect-input",Vs),t.component("k-number-input",Zs),t.component("k-password-input",nn),t.component("k-picklist-input",Le),t.component("k-radio-input",rn),t.component("k-range-input",un),t.component("k-search-input",to),t.component("k-select-input",hn),t.component("k-slug-input",gn),t.component("k-string-input",Si),t.component("k-tags-input",Ws),t.component("k-tel-input",yn),t.component("k-text-input",Ti),t.component("k-textarea-input",Sn),t.component("k-time-input",In),t.component("k-timeoptions-input",eo),t.component("k-toggle-input",En),t.component("k-toggles-input",Bn),t.component("k-url-input",Nn),t.component("k-writer-input",Ps),t.component("k-calendar",Vn),t.component("k-times",eo)}};const so=ct({mixins:[Et],inheritAttrs:!1,props:{cancelButton:{default:!1},label:{default(){return this.$t("field.layout.select")},type:String},layouts:{type:Array},selector:Object,submitButton:{default:!1},value:{type:Array}},emits:["cancel","input","submit"]},(function(){var t,e,i=this,s=i._self._c;return s("k-dialog",i._b({staticClass:"k-layout-selector",attrs:{size:(null==(t=i.selector)?void 0:t.size)??"medium"},on:{cancel:function(t){return i.$emit("cancel")},submit:function(t){return i.$emit("submit",i.value)}}},"k-dialog",i.$props,!1),[s("h3",{staticClass:"k-label"},[i._v(i._s(i.label))]),s("k-navigate",{staticClass:"k-layout-selector-options",style:{"--columns":Number((null==(e=i.selector)?void 0:e.columns)??3)},attrs:{axis:"x"}},i._l(i.layouts,(function(t,e){return s("button",{key:e,staticClass:"k-layout-selector-option",attrs:{"aria-current":i.value===t,"aria-label":t.join(","),value:t},on:{click:function(e){return i.$emit("input",t)}}},[s("k-grid",{attrs:{"aria-hidden":""}},i._l(t,(function(t,e){return s("k-column",{key:e,attrs:{width:t}})})),1)],1)})),0)],1)}),[]).exports,no={install(t){t.component("k-layout",Ki),t.component("k-layout-column",Hi),t.component("k-layouts",Ji),t.component("k-layout-selector",so)}},oo={inheritAttrs:!1,props:{column:{default:()=>({}),type:Object},field:{default:()=>({}),type:Object},value:{}}},ao={props:{html:{type:Boolean}}};const ro=ct({mixins:[ao],inheritAttrs:!1,props:{bubbles:[Array,String]},computed:{items(){let t=this.bubbles;return"string"==typeof t&&(t=t.split(",")),t.map((t=>"string"===t?{text:t}:t))}}},(function(){var t=this,e=t._self._c;return e("ul",{staticClass:"k-bubbles"},t._l(t.items,(function(i,s){return e("li",{key:s},[e("k-bubble",t._b({attrs:{html:t.html}},"k-bubble",i,!1))],1)})),0)}),[]).exports;const lo=ct({mixins:[oo,ao],props:{value:{default:()=>[],type:[Array,String]}},computed:{bubbles(){let t=this.value;const e=this.column.options??this.field.options??[];return"string"==typeof t&&(t=t.split(",")),(t??[]).map((t=>{"string"==typeof t&&(t={value:t,text:t});for(const i of e)i.value===t.value&&(t.text=i.text);return t}))}}},(function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-bubbles-field-preview",class:t.$options.class},[e("k-bubbles",{attrs:{bubbles:t.bubbles,html:t.html}})],1)}),[]).exports;const co=ct({extends:lo,inheritAttrs:!1,class:"k-array-field-preview",computed:{bubbles(){return[{text:1===this.value.length?`1 ${this.$t("entry")}`:`${this.value.length} ${this.$t("entries")}`}]}}},null,null).exports;const uo=ct({mixins:[oo],props:{value:String},computed:{text(){var t;if(!this.value)return;const e=this.$library.colors.toString(this.value,this.field.format,this.field.alpha),i=null==(t=this.field.options)?void 0:t.find((t=>this.$library.colors.toString(t.value,this.field.format,this.field.alpha)===e));return i?i.text:null}}},(function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-color-field-preview"},[e("k-color-frame",{attrs:{color:t.value}}),t.text?[t._v(" "+t._s(t.text)+" ")]:t._e()],2)}),[]).exports;const po=ct({mixins:[oo],inheritAttrs:!1,computed:{text(){return this.value}}},(function(){var t=this;return(0,t._self._c)("p",{staticClass:"k-text-field-preview",class:t.$options.class},[t._v(" "+t._s(t.column.before)+" "),t._t("default",(function(){return[t._v(t._s(t.text))]})),t._v(" "+t._s(t.column.after)+" ")],2)}),[]).exports;const ho=ct({extends:po,props:{value:String},class:"k-date-field-preview",computed:{display(){return this.column.display??this.field.display},format(){var t;let e=this.display??"YYYY-MM-DD";return(null==(t=this.time)?void 0:t.display)&&(e+=" "+this.time.display),e},parsed(){return this.$library.dayjs(this.value)},text(){var t;return!1===this.parsed.isValid()?this.value:null==(t=this.parsed)?void 0:t.format(this.format)},time(){return this.column.time??this.field.time}}},null,null).exports;const mo=ct({mixins:[oo],props:{value:[String,Object]},computed:{link(){return"object"==typeof this.value?this.value.href:this.value},text(){return"object"==typeof this.value?this.value.text:this.link}}},(function(){var t=this,e=t._self._c;return e("p",{staticClass:"k-url-field-preview",class:t.$options.class,attrs:{"data-link":t.link}},[t._v(" "+t._s(t.column.before)+" "),e("k-link",{attrs:{to:t.link},nativeOn:{click:function(t){t.stopPropagation()}}},[e("span",[t._v(t._s(t.text))])]),t._v(" "+t._s(t.column.after)+" ")],1)}),[]).exports;const fo=ct({extends:mo,class:"k-email-field-preview"},null,null).exports;const go=ct({extends:lo,class:"k-files-field-preview",props:{html:{type:Boolean,default:!0}},computed:{bubbles(){return this.value.map((t=>({text:t.filename,link:t.link,image:t.image})))}}},null,null).exports;const ko=ct({mixins:[oo],props:{value:Object},computed:{status(){var t;return{...this.$helper.page.status(null==(t=this.value)?void 0:t.status),...this.value}}}},(function(){var t=this,e=t._self._c;return t.value?e("k-button",t._b({staticClass:"k-flag-field-preview",attrs:{size:"md"}},"k-button",t.status,!1)):t._e()}),[]).exports;const bo=ct({mixins:[oo],props:{value:String},computed:{html(){return this.value}}},(function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-html-field-preview",class:t.$options.class},[t._v(" "+t._s(t.column.before)+" "),e("k-text",{attrs:{html:t.html}}),t._v(" "+t._s(t.column.after)+" ")],1)}),[]).exports;const vo=ct({mixins:[oo],props:{value:[Object]}},(function(){var t=this,e=t._self._c;return t.value?e("k-item-image",{staticClass:"k-image-field-preview",attrs:{image:t.value}}):t._e()}),[]).exports;const yo=ct({mixins:[oo],inheritAttrs:!1,props:{removable:Boolean,type:String},emits:["remove"],data:()=>({model:null}),computed:{currentType(){return this.type??this.detected.type},detected(){return this.$helper.link.detect(this.value)},isLink(){return["url","email","tel"].includes(this.currentType)}},watch:{detected:{async handler(t,e){t!==e&&(this.model=await this.$helper.link.preview(this.detected))},immediate:!0},type(){this.model=null}}},(function(){var t=this,e=t._self._c;return e("div",{class:{"k-link-field-preview":!0,"k-url-field-preview":t.isLink}},["page"===t.currentType||"file"===t.currentType?[t.model?[e("k-tag",{attrs:{image:{...t.model.image,cover:!0},removable:t.removable,text:t.model.label},on:{remove:function(e){return t.$emit("remove",e)}}})]:t._t("placeholder")]:t.isLink?[e("p",{staticClass:"k-text"},[e("a",{attrs:{href:t.value,target:"_blank"}},[e("span",[t._v(t._s(t.detected.link))])])])]:[t._v(" "+t._s(t.detected.link)+" ")]],2)}),[]).exports;const $o=ct({extends:lo,class:"k-object-field-preview",props:{value:[Array,Object]},computed:{bubbles(){return this.value?[{text:"{ ... }"}]:[]}}},null,null).exports;const wo=ct({extends:lo,inheritAttrs:!1,class:"k-pages-field-preview",props:{html:{type:Boolean,default:!0}}},null,null).exports;const xo=ct({extends:ho,class:"k-time-field-preview",computed:{format(){return this.display??"HH:mm"},parsed(){return this.$library.dayjs.iso(this.value,"time")},text(){var t;return null==(t=this.parsed)?void 0:t.format(this.format)}}},null,null).exports;const _o=ct({mixins:[oo],props:{value:Boolean},emits:["input"],computed:{isEditable(){return!0!==this.field.disabled},text(){return!1!==this.column.text?this.field.text:null}}},(function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-toggle-field-preview"},[e("k-toggle-input",{attrs:{disabled:!t.isEditable,text:t.text,value:t.value},on:{input:function(e){return t.$emit("input",e)}},nativeOn:{click:function(e){t.isEditable&&e.stopPropagation()}}})],1)}),[]).exports;const Co=ct({extends:lo,class:"k-users-field-preview",computed:{bubble(){return this.value.map((t=>({text:t.username,link:t.link,image:t.image})))}}},null,null).exports,So={install(t){t.component("k-array-field-preview",co),t.component("k-bubbles-field-preview",lo),t.component("k-color-field-preview",uo),t.component("k-date-field-preview",ho),t.component("k-email-field-preview",fo),t.component("k-files-field-preview",go),t.component("k-flag-field-preview",ko),t.component("k-html-field-preview",bo),t.component("k-image-field-preview",vo),t.component("k-link-field-preview",yo),t.component("k-object-field-preview",$o),t.component("k-pages-field-preview",wo),t.component("k-text-field-preview",po),t.component("k-toggle-field-preview",_o),t.component("k-time-field-preview",xo),t.component("k-url-field-preview",mo),t.component("k-users-field-preview",Co),t.component("k-list-field-preview",bo),t.component("k-writer-field-preview",bo),t.component("k-checkboxes-field-preview",lo),t.component("k-multiselect-field-preview",lo),t.component("k-radio-field-preview",lo),t.component("k-select-field-preview",lo),t.component("k-tags-field-preview",lo),t.component("k-toggles-field-preview",lo)}};const Oo=ct({mixins:[{props:{buttons:{type:Array,default:()=>[]},theme:{type:String,default:"light"}}}],methods:{close(){for(const t in this.$refs){const e=this.$refs[t][0];"function"==typeof(null==e?void 0:e.close)&&e.close()}}}},(function(){var t=this,e=t._self._c;return e("nav",{staticClass:"k-toolbar",attrs:{"data-theme":t.theme}},[t._l(t.buttons,(function(i,s){var n;return["|"===i?e("hr",{key:s}):i.when??1?e("k-button",{key:s,class:["k-toolbar-button",i.class],attrs:{current:i.current,disabled:i.disabled,icon:i.icon,title:i.label??i.title,tabindex:"0"},on:{click:function(e){var n,o;(null==(n=i.dropdown)?void 0:n.length)?t.$refs[s+"-dropdown"][0].toggle():null==(o=i.click)||o.call(i,e)}},nativeOn:{keydown:function(t){var e;null==(e=i.key)||e.call(i,t)}}}):t._e(),(i.when??1)&&(null==(n=i.dropdown)?void 0:n.length)?e("k-dropdown-content",{key:s+"-dropdown",ref:s+"-dropdown",refInFor:!0,attrs:{options:i.dropdown,theme:"dark"===t.theme?"light":"dark"}}):t._e()]}))],2)}),[]).exports;const Mo=ct({extends:Bt,props:{fields:{default:()=>{const t=Bt.props.fields.default();return t.title.label=window.panel.$t("link.text"),t}}},methods:{submit(){const t=this.values.href??"",e=this.values.title??"";return this.$panel.config.kirbytext?(null==e?void 0:e.length)>0?this.$emit("submit",`(email: ${t} text: ${e})`):this.$emit("submit",`(email: ${t})`):(null==e?void 0:e.length)>0?this.$emit("submit",`[${e}](mailto:${t})`):this.$emit("submit",`<${t}>`)}}},null,null).exports;const Ao=ct({extends:Kt,props:{fields:{default:()=>({href:{label:window.panel.$t("link"),type:"link",placeholder:window.panel.$t("url.placeholder"),icon:"url"},title:{label:window.panel.$t("link.text"),type:"text",icon:"title"}})}},methods:{submit(){const t=this.values.href??"",e=this.values.title??"";return this.$panel.config.kirbytext?(null==e?void 0:e.length)>0?this.$emit("submit",`(link: ${t} text: ${e})`):this.$emit("submit",`(link: ${t})`):(null==e?void 0:e.length)>0?this.$emit("submit",`[${e}](${t})`):this.$emit("submit",`<${t}>`)}}},null,null).exports,Io={install(t){t.component("k-toolbar",Oo),t.component("k-textarea-toolbar",_n),t.component("k-toolbar-email-dialog",Mo),t.component("k-toolbar-link-dialog",Ao)}};const Do=ct({props:{editor:{required:!0,type:Object},inline:{default:!0,type:Boolean},marks:{default:()=>["bold","italic","underline","strike","code","|","link","email","|","clear"],type:[Array,Boolean]},nodes:{default:!0,type:[Array,Boolean]}},emits:["command"],data:()=>({isOpen:!1,position:{x:0,y:0}}),computed:{activeNode(){return Object.values(this.nodeButtons).find((t=>this.isNodeActive(t)))??!1},buttons(){var t,e,i;const s=[];if(this.hasNodes){const n=[];let o=0;for(const s in this.nodeButtons){const a=this.nodeButtons[s];n.push({current:(null==(t=this.activeNode)?void 0:t.id)===a.id,disabled:!1===(null==(i=null==(e=this.activeNode)?void 0:e.when)?void 0:i.includes(a.name)),icon:a.icon,label:a.label,click:()=>this.command(a.command??s)}),!0===a.separator&&o!==Object.keys(this.nodeButtons).length-1&&n.push("-"),o++}s.push({current:Boolean(this.activeNode),icon:this.activeNode.icon??"title",dropdown:n})}if(this.hasNodes&&this.hasMarks&&s.push("|"),this.hasMarks)for(const n in this.markButtons){const t=this.markButtons[n];"|"!==t?s.push({current:this.editor.activeMarks.includes(n),icon:t.icon,label:t.label,click:e=>this.command(t.command??n,e)}):s.push("|")}return s},hasMarks(){return this.$helper.object.length(this.markButtons)>0},hasNodes(){return this.$helper.object.length(this.nodeButtons)>1},markButtons(){const t=this.editor.buttons("mark");if(!1===this.marks||0===this.$helper.object.length(t))return{};if(!0===this.marks)return t;const e={};for(const[i,s]of this.marks.entries())"|"===s?e["divider"+i]="|":t[s]&&(e[s]=t[s]);return e},nodeButtons(){const t=this.editor.buttons("node");if(!1===this.nodes||0===this.$helper.object.length(t))return{};if("block+"!==this.editor.nodes.doc.content&&t.paragraph&&delete t.paragraph,!0===this.nodes)return t;const e={};for(const i of this.nodes)t[i]&&(e[i]=t[i]);return e},positions(){return!1===this.inline?null:{top:this.position.y+"px",left:this.position.x+"px"}}},methods:{close(t){t&&!1!==this.$el.contains(t.relatedTarget)||(this.isOpen=!1)},command(t,...e){this.$emit("command",t,...e)},isNodeActive(t){if(!1===this.editor.activeNodes.includes(t.name))return!1;if("paragraph"===t.name)return!1===this.editor.activeNodes.includes("listItem")&&!1===this.editor.activeNodes.includes("quote");if(t.attrs){if(void 0===Object.values(this.editor.activeNodeAttrs).find((e=>JSON.stringify(e)===JSON.stringify(t.attrs))))return!1}return!0},open(){0!==this.buttons.length&&(this.isOpen=!0,this.inline&&this.$nextTick(this.setPosition))},setPosition(){const t=this.$el.getBoundingClientRect(),e=this.editor.element.getBoundingClientRect(),i=document.querySelector(".k-panel-menu").getBoundingClientRect(),{from:s,to:n}=this.editor.selection,o=this.editor.view.coordsAtPos(s),a=this.editor.view.coordsAtPos(n,!0),r=new DOMRect(o.left,o.top,a.right-o.left,a.bottom-o.top);let l=r.x-e.x+r.width/2-t.width/2,c=r.y-e.y-t.height-5;if(t.widthe.width&&(l=e.width-t.width);else{const s=e.x+l,n=s+t.width,o=i.width+20,a=20;swindow.innerWidth-a&&(l-=n-(window.innerWidth-a))}this.position={x:l,y:c}}}},(function(){var t=this,e=t._self._c;return t.isOpen||!t.inline?e("k-toolbar",{ref:"toolbar",staticClass:"k-writer-toolbar",style:t.positions,attrs:{buttons:t.buttons,"data-inline":t.inline,theme:t.inline?"dark":"light"}}):t._e()}),[]).exports,jo={install(t){t.component("k-writer-toolbar",Do),t.component("k-writer",Bs)}},Eo={install(t){t.component("k-counter",Pe),t.component("k-autocomplete",qe),t.component("k-form",Ne),t.component("k-form-buttons",ze),t.component("k-field",Ye),t.component("k-fieldset",Re),t.component("k-input",He),t.component("k-upload",Ve),t.use(vi),t.use(io),t.use(Rn),t.use(no),t.use(So),t.use(Io),t.use(jo)}},Lo=()=>Y((()=>import("./IndexView.min.js")),__vite__mapDeps([0,1]),import.meta.url),To=()=>Y((()=>import("./DocsView.min.js")),__vite__mapDeps([2,3,1]),import.meta.url),Bo=()=>Y((()=>import("./PlaygroundView.min.js")),__vite__mapDeps([4,3,1]),import.meta.url),qo={install(t){t.component("k-lab-index-view",Lo),t.component("k-lab-docs-view",To),t.component("k-lab-playground-view",Bo)}};const Po=ct({props:{cover:Boolean,ratio:String},computed:{ratioPadding(){return this.$helper.ratio(this.ratio)}},mounted(){window.panel.deprecated(" will be removed in a future version. Use the instead.")}},(function(){var t=this;return(0,t._self._c)("span",{staticClass:"k-aspect-ratio",style:{"padding-bottom":t.ratioPadding},attrs:{"data-cover":t.cover}},[t._t("default")],2)}),[]).exports;const No=ct({props:{align:{type:String,default:"start"}},mounted(){(this.$slots.left||this.$slots.center||this.$slots.right)&&window.panel.deprecated(": left/centre/right slots will be removed in a future version. Use with default slot only instead.")}},(function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-bar",attrs:{"data-align":t.align}},[t.$slots.left||t.$slots.center||t.$slots.right?[t.$slots.left?e("div",{staticClass:"k-bar-slot",attrs:{"data-position":"left"}},[t._t("left")],2):t._e(),t.$slots.center?e("div",{staticClass:"k-bar-slot",attrs:{"data-position":"center"}},[t._t("center")],2):t._e(),t.$slots.right?e("div",{staticClass:"k-bar-slot",attrs:{"data-position":"right"}},[t._t("right")],2):t._e()]:t._t("default")],2)}),[]).exports;const zo=ct({props:{align:{type:String,default:"start"},button:Boolean,height:String,icon:String,theme:{type:String},text:String,html:{type:Boolean}},computed:{element(){return this.button?"button":"div"},type(){return this.button?"button":null}}},(function(){var t=this,e=t._self._c;return e(t.element,{tag:"component",staticClass:"k-box",style:t.height?{"--box-height":t.height}:null,attrs:{"data-align":t.align,"data-theme":t.theme,type:t.type}},[t.icon?e("k-icon",{attrs:{type:t.icon}}):t._e(),t._t("default",(function(){return[t.html?e("k-text",{attrs:{html:t.text}}):e("k-text",[t._v(" "+t._s(t.text)+" ")])]}),null,{html:t.html,text:t.text})],2)}),[]).exports;const Fo=ct({inheritAttrs:!1,props:{back:String,color:String,element:{type:String,default:"li"},html:{type:Boolean},image:Object,link:String,text:String},mounted(){this.back&&window.panel.deprecated(": `back` prop will be removed in a future version. Use the `--bubble-back` CSS property instead."),this.color&&window.panel.deprecated(": `color` prop will be removed in a future version. Use the `--bubble-text` CSS property instead.")}},(function(){var t=this,e=t._self._c;return e(t.link?"k-link":"p",{tag:"component",staticClass:"k-bubble",style:{color:t.$helper.color(t.color),background:t.$helper.color(t.back)},attrs:{to:t.link,"data-has-text":Boolean(t.text)},nativeOn:{click:function(t){t.stopPropagation()}}},[t._t("image",(function(){var i;return[(null==(i=t.image)?void 0:i.src)?e("k-image-frame",t._b({},"k-image-frame",t.image,!1)):t.image?e("k-icon-frame",t._b({},"k-icon-frame",t.image,!1)):e("span")]})),t.text?[t.html?e("span",{staticClass:"k-bubble-text",domProps:{innerHTML:t._s(t.text)}}):e("span",{staticClass:"k-bubble-text"},[t._v(t._s(t.text))])]:t._e()],2)}),[]).exports;const Yo=ct({props:{width:{type:String,default:"1/1"},sticky:Boolean}},(function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-column",style:{"--width":t.width},attrs:{"data-sticky":t.sticky}},[t.sticky?e("div",[t._t("default")],2):t._t("default")],2)}),[]).exports,Ro={props:{element:{type:String,default:"div"},fit:String,ratio:String,cover:Boolean,back:String,theme:String}};const Uo=ct({mixins:[Ro],inheritAttrs:!1,computed:{background(){return this.$helper.color(this.back)}}},(function(){var t=this;return(0,t._self._c)(t.element,{tag:"component",staticClass:"k-frame",style:{"--fit":t.fit??(t.cover?"cover":"contain"),"--ratio":t.ratio,"--back":t.background},attrs:{"data-theme":t.theme}},[t._t("default")],2)}),[]).exports;const Ho=ct({mixins:[{mixins:[Ro],props:{color:String}}],inheritAttrs:!1},(function(){var t=this;return(0,t._self._c)("k-frame",t._b({staticClass:"k-color-frame",style:{"--color-frame-back":t.color}},"k-frame",t.$props,!1),[t._t("default")],2)}),[]).exports;const Vo=ct({props:{disabled:{type:Boolean}},emits:["drop"],data:()=>({files:[],dragging:!1,over:!1}),methods:{cancel(){this.reset()},reset(){this.dragging=!1,this.over=!1},onDrop(t){return!0===this.disabled||!1===this.$helper.isUploadEvent(t)?this.reset():(this.$events.emit("dropzone.drop"),this.files=t.dataTransfer.files,this.$emit("drop",this.files),void this.reset())},onEnter(t){!1===this.disabled&&this.$helper.isUploadEvent(t)&&(this.dragging=!0)},onLeave(){this.reset()},onOver(t){!1===this.disabled&&this.$helper.isUploadEvent(t)&&(t.dataTransfer.dropEffect="copy",this.over=!0)}}},(function(){var t=this;return(0,t._self._c)("div",{staticClass:"k-dropzone",attrs:{"data-dragging":t.dragging,"data-over":t.over},on:{dragenter:t.onEnter,dragleave:t.onLeave,dragover:t.onOver,drop:t.onDrop}},[t._t("default")],2)}),[]).exports;const Ko=ct({props:{gutter:String,variant:String},mounted(){this.gutter&&window.panel.deprecated(': the `gutter` prop will be removed in a future version. Use `style="gap: "` or `variant` prop instead.')}},(function(){var t=this;return(0,t._self._c)("div",{staticClass:"k-grid",attrs:{"data-gutter":t.gutter,"data-variant":t.variant}},[t._t("default")],2)}),[]).exports;const Wo=ct({props:{editable:{type:Boolean},tabs:Array},emits:["edit"],mounted(){this.tabs&&window.panel.deprecated(": `tabs` prop isn't supported anymore and has no effect. Use `` as standalone component instead."),(this.$slots.left||this.$slots.right)&&window.panel.deprecated(": left/right slots will be removed in a future version. Use `buttons` slot instead.")}},(function(){var t=this,e=t._self._c;return e("header",{staticClass:"k-header",attrs:{"data-has-buttons":Boolean(t.$slots.buttons||t.$slots.left||t.$slots.right)}},[e("h1",{staticClass:"k-header-title"},[t.editable?e("button",{staticClass:"k-header-title-button",attrs:{type:"button"},on:{click:function(e){return t.$emit("edit")}}},[e("span",{staticClass:"k-header-title-text"},[t._t("default")],2),e("span",{staticClass:"k-header-title-icon"},[e("k-icon",{attrs:{type:"edit"}})],1)]):e("span",{staticClass:"k-header-title-text"},[t._t("default")],2)]),t.$slots.buttons||t.$slots.left||t.$slots.right?e("div",{staticClass:"k-header-buttons"},[t._t("buttons"),t._t("left"),t._t("right")],2):t._e()])}),[]).exports,Jo={props:{alt:String,color:String,type:String}};const Go=ct({mixins:[Jo],computed:{isEmoji(){return this.$helper.string.hasEmoji(this.type)}}},(function(){var t=this,e=t._self._c;return t.isEmoji?e("span",{attrs:{"data-type":"emoji"}},[t._v(t._s(t.type))]):e("svg",{staticClass:"k-icon",style:{color:t.$helper.color(t.color)},attrs:{"aria-label":t.alt,role:t.alt?"img":null,"aria-hidden":!t.alt,"data-type":t.type}},[e("use",{attrs:{"xlink:href":"#icon-"+t.type}})])}),[]).exports;const Xo=ct({mixins:[{mixins:[Ro,Jo],props:{type:null,icon:String}}],inheritAttrs:!1},(function(){var t=this,e=t._self._c;return e("k-frame",t._b({staticClass:"k-icon-frame",attrs:{element:"figure"}},"k-frame",t.$props,!1),[e("k-icon",t._b({},"k-icon",{color:t.color,type:t.icon,alt:t.alt},!1))],1)}),[]).exports;const Zo=ct({mixins:[{mixins:[Ro],props:{alt:String,sizes:String,src:String,srcset:String}}],inheritAttrs:!1},(function(){var t=this,e=t._self._c;return e("k-frame",t._g(t._b({staticClass:"k-image-frame k-image",attrs:{element:"figure"}},"k-frame",t.$props,!1),t.$listeners),[t.src?e("img",{key:t.src,attrs:{alt:t.alt??"",src:t.src,srcset:t.srcset,sizes:t.sizes},on:{dragstart:function(t){t.preventDefault()}}}):t._e()])}),[]).exports;const Qo=ct({mixins:[{props:{autofocus:{default:!0,type:Boolean},nested:{default:!1,type:Boolean},type:{default:"overlay",type:String},visible:{default:!1,type:Boolean}}}],inheritAttrs:!0,emits:["cancel","close","open"],watch:{visible(t,e){t!==e&&this.toggle()}},mounted(){this.toggle()},methods:{cancel(){this.$emit("cancel"),this.close()},close(){if(!1!==this.$refs.overlay.open)return this.nested?this.onClose():void this.$refs.overlay.close()},focus(){this.$helper.focus(this.$refs.overlay)},onCancel(t){this.nested&&(t.preventDefault(),this.cancel())},onClick(t){t.target.matches(".k-portal")&&this.cancel()},onClose(){this.$emit("close")},open(){!0!==this.$refs.overlay.open&&this.$refs.overlay.showModal(),setTimeout((()=>{!0===this.autofocus&&this.focus(),this.$emit("open")}))},toggle(){!0===this.visible?this.open():this.close()}}},(function(){var t=this;return(0,t._self._c)("dialog",{ref:"overlay",staticClass:"k-overlay",attrs:{"data-type":t.type},on:{cancel:t.onCancel,mousedown:t.onClick,touchdown:t.onClick,close:t.onClose}},[t._t("default")],2)}),[]).exports;const ta=ct({props:{label:String,value:String,icon:String,info:String,theme:String,link:String,click:Function,dialog:{type:[String,Object]}},computed:{component(){return null!==this.target?"k-link":"div"},target(){return this.link?this.link:this.click?this.click:this.dialog?()=>this.$dialog(this.dialog):null}}},(function(){var t=this,e=t._self._c;return e(t.component,{tag:"component",staticClass:"k-stat",attrs:{"data-theme":t.theme,to:t.target}},[t.label?e("dt",{staticClass:"k-stat-label"},[t.icon?e("k-icon",{attrs:{type:t.icon}}):t._e(),t._v(" "+t._s(t.label)+" ")],1):t._e(),t.value?e("dd",{staticClass:"k-stat-value"},[t._v(t._s(t.value))]):t._e(),t.info?e("dd",{staticClass:"k-stat-info"},[t._v(t._s(t.info))]):t._e()])}),[]).exports;const ea=ct({props:{reports:{type:Array,default:()=>[]},size:{type:String,default:"large"}},methods:{component(t){return null!==this.target(t)?"k-link":"div"},target(t){return t.link?t.link:t.click?t.click:t.dialog?()=>this.$dialog(t.dialog):null}}},(function(){var t=this,e=t._self._c;return e("dl",{staticClass:"k-stats",attrs:{"data-size":t.size}},t._l(t.reports,(function(i,s){return e("k-stat",t._b({key:s},"k-stat",i,!1))})),1)}),[]).exports;const ia=ct({inheritAttrs:!1,props:{columns:{type:Object,default:()=>({})},disabled:Boolean,fields:{type:Object,default:()=>({})},empty:String,index:{type:[Number,Boolean],default:1},rows:Array,options:{default:()=>[],type:[Array,Function]},pagination:[Object,Boolean],sortable:Boolean},emits:["cell","change","header","input","option","paginate","sort"],data(){return{values:this.rows}},computed:{colspan(){let t=this.columnsCount;return this.hasIndexColumn&&t++,this.hasOptions&&t++,t},columnsCount(){return this.$helper.object.length(this.columns)},dragOptions(){return{disabled:!this.sortable,draggable:".k-table-sortable-row",fallbackClass:"k-table-row-fallback",ghostClass:"k-table-row-ghost"}},hasIndexColumn(){return this.sortable||!1!==this.index},hasOptions(){var t;return this.$scopedSlots.options||(null==(t=this.options)?void 0:t.length)>0||Object.values(this.values).filter((t=>null==t?void 0:t.options)).length>0}},watch:{rows(){this.values=this.rows}},methods:{isColumnEmpty(t){return 0===this.rows.filter((e=>!1===this.$helper.object.isEmpty(e[t]))).length},label(t,e){return t.label??this.$helper.string.ucfirst(e)},onChange(t){this.$emit("change",t)},onCell(t){this.$emit("cell",t)},onCellUpdate({columnIndex:t,rowIndex:e,value:i}){this.values[e][t]=i,this.$emit("input",this.values)},onHeader(t){this.$emit("header",t)},onOption(t,e,i){this.$emit("option",t,e,i)},onSort(){this.$emit("input",this.values),this.$emit("sort",this.values)},width(t){return"string"!=typeof t?"auto":!1===t.includes("/")?t:this.$helper.ratio(t,"auto",!1)}}},(function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-table",attrs:{"aria-disabled":t.disabled}},[e("table",{attrs:{"data-disabled":t.disabled,"data-indexed":t.hasIndexColumn}},[e("thead",[e("tr",[t.hasIndexColumn?e("th",{staticClass:"k-table-index-column",attrs:{"data-mobile":"true"}},[t._v(" # ")]):t._e(),t._l(t.columns,(function(i,s){return e("th",{key:s+"-header",staticClass:"k-table-column",style:{width:t.width(i.width)},attrs:{"data-align":i.align,"data-column-id":s,"data-mobile":i.mobile},on:{click:function(e){return t.onHeader({column:i,columnIndex:s})}}},[t._t("header",(function(){return[t._v(" "+t._s(t.label(i,s))+" ")]}),null,{column:i,columnIndex:s,label:t.label(i,s)})],2)})),t.hasOptions?e("th",{staticClass:"k-table-options-column",attrs:{"data-mobile":"true"}}):t._e()],2)]),e("k-draggable",{attrs:{list:t.values,options:t.dragOptions,handle:!0,element:"tbody"},on:{change:t.onChange,end:t.onSort}},[0===t.rows.length?e("tr",[e("td",{staticClass:"k-table-empty",attrs:{colspan:t.colspan}},[t._v(" "+t._s(t.empty)+" ")])]):t._l(t.values,(function(i,s){return e("tr",{key:s,class:{"k-table-sortable-row":t.sortable&&!1!==i.sortable}},[t.hasIndexColumn?e("td",{staticClass:"k-table-index-column",attrs:{"data-mobile":"true"}},[t._t("index",(function(){return[e("div",{staticClass:"k-table-index",domProps:{textContent:t._s(t.index+s)}})]}),null,{row:i,rowIndex:s}),t.sortable&&!1!==i.sortable?e("k-sort-handle",{staticClass:"k-table-sort-handle"}):t._e()],2):t._e(),t._l(t.columns,(function(n,o){return e("k-table-cell",{key:s+"-"+o,staticClass:"k-table-column",style:{width:t.width(n.width)},attrs:{id:o,column:n,field:t.fields[o],row:i,mobile:n.mobile,value:i[o]},on:{input:function(e){return t.onCellUpdate({columnIndex:o,rowIndex:s,value:e})}},nativeOn:{click:function(e){return t.onCell({row:i,rowIndex:s,column:n,columnIndex:o})}}})})),t.hasOptions?e("td",{staticClass:"k-table-options-column",attrs:{"data-mobile":"true"}},[t._t("options",(function(){return[e("k-options-dropdown",{attrs:{options:i.options??t.options,text:(i.options??t.options).length>1},on:{option:function(e){return t.onOption(e,i,s)}}})]}),null,{row:i,rowIndex:s})],2):t._e()],2)}))],2)],1),t.pagination?e("k-pagination",t._b({staticClass:"k-table-pagination",on:{paginate:function(e){return t.$emit("paginate",e)}}},"k-pagination",t.pagination,!1)):t._e()],1)}),[]).exports;const sa=ct({inheritAttrs:!1,props:{column:Object,field:Object,id:String,mobile:{type:Boolean,default:!1},row:Object,value:{default:""}},emits:["input"],computed:{component(){return this.$helper.isComponent(`k-${this.type}-field-preview`)?`k-${this.type}-field-preview`:this.$helper.isComponent(`k-table-${this.type}-cell`)?`k-table-${this.type}-cell`:Array.isArray(this.value)?"k-array-field-preview":"object"==typeof this.value?"k-object-field-preview":"k-text-field-preview"},type(){var t;return this.column.type??(null==(t=this.field)?void 0:t.type)}}},(function(){var t=this,e=t._self._c;return e("td",{staticClass:"k-table-cell",attrs:{"data-align":t.column.align,"data-column-id":t.id,"data-mobile":t.mobile}},[!1===t.$helper.object.isEmpty(t.value)?e(t.component,{tag:"component",attrs:{column:t.column,field:t.field,row:t.row,value:t.value},on:{input:function(e){return t.$emit("input",e)}}}):t._e()],1)}),[]).exports;const na=ct({props:{tab:String,tabs:{type:Array,default:()=>[]},theme:{type:String,default:"passive"}},data(){return{observer:null,visible:this.tabs,invisible:[]}},computed:{buttons(){return this.visible.map(this.button)},current(){const t=this.tabs.find((t=>t.name===this.tab))??this.tabs[0];return null==t?void 0:t.name},dropdown(){return this.invisible.map(this.button)}},watch:{tabs:{async handler(){var t;null==(t=this.observer)||t.disconnect(),await this.$nextTick(),this.$el instanceof Element&&(this.observer=new ResizeObserver(this.resize),this.observer.observe(this.$el))},immediate:!0}},destroyed(){var t;null==(t=this.observer)||t.disconnect()},methods:{button(t){return{...t,current:t.name===this.current,title:t.label,text:t.label??t.text??t.name}},async resize(){const t=this.$el.offsetWidth;this.visible=this.tabs,this.invisible=[],await this.$nextTick();const e=[...this.$refs.visible].map((t=>t.$el.offsetWidth));let i=32;for(let s=0;st)return this.visible=this.tabs.slice(0,s),void(this.invisible=this.tabs.slice(s))}}},(function(){var t=this,e=t._self._c;return t.tabs.length>1?e("nav",{staticClass:"k-tabs"},[t._l(t.buttons,(function(i){return e("div",{key:i.name,staticClass:"k-tabs-tab"},[e("k-button",t._b({ref:"visible",refInFor:!0,staticClass:"k-tab-button",attrs:{variant:"dimmed"}},"k-button",i,!1),[t._v(" "+t._s(i.text)+" ")]),i.badge?e("span",{staticClass:"k-tabs-badge",attrs:{"data-theme":t.theme}},[t._v(" "+t._s(i.badge)+" ")]):t._e()],1)})),t.invisible.length?[e("k-button",{staticClass:"k-tab-button k-tabs-dropdown-button",attrs:{current:!!t.invisible.find((e=>t.tab===e.name)),title:t.$t("more"),icon:"dots",variant:"dimmed"},on:{click:function(e){return e.stopPropagation(),t.$refs.more.toggle()}}}),e("k-dropdown-content",{ref:"more",staticClass:"k-tabs-dropdown",attrs:{options:t.dropdown,"align-x":"end"}})]:t._e()],2):t._e()}),[]).exports;const oa=ct({props:{align:String},mounted(){window.panel.deprecated(" will be removed in a future version.")}},(function(){var t=this;return(0,t._self._c)("div",{staticClass:"k-view",attrs:{"data-align":t.align}},[t._t("default")],2)}),[]).exports,aa={install(t){t.component("k-aspect-ratio",Po),t.component("k-bar",No),t.component("k-box",zo),t.component("k-bubble",Fo),t.component("k-bubbles",ro),t.component("k-color-frame",Ho),t.component("k-column",Yo),t.component("k-dropzone",Vo),t.component("k-frame",Uo),t.component("k-grid",Ko),t.component("k-header",Wo),t.component("k-icon-frame",Xo),t.component("k-image-frame",Zo),t.component("k-image",Zo),t.component("k-overlay",Qo),t.component("k-stat",ta),t.component("k-stats",ea),t.component("k-table",ia),t.component("k-table-cell",sa),t.component("k-tabs",na),t.component("k-view",oa)}};const ra=ct({components:{draggable:()=>Y((()=>import("./vuedraggable.min.js")),[],import.meta.url)},props:{data:Object,element:{type:String,default:"div"},handle:[String,Boolean],list:[Array,Object],move:Function,options:Object},emits:["change","end","sort","start"],computed:{dragOptions(){let t=this.handle;return!0===t&&(t=".k-sort-handle"),{fallbackClass:"k-sortable-fallback",fallbackOnBody:!0,forceFallback:!0,ghostClass:"k-sortable-ghost",handle:t,scroll:document.querySelector(".k-panel-main"),...this.options}}},methods:{onStart(t){this.$panel.drag.start("data",{}),this.$emit("start",t)},onEnd(t){this.$panel.drag.stop(),this.$emit("end",t)}}},(function(){var t=this;return(0,t._self._c)("draggable",t._b({staticClass:"k-draggable",attrs:{"component-data":t.data,tag:t.element,list:t.list,move:t.move},on:{change:function(e){return t.$emit("change",e)},end:t.onEnd,sort:function(e){return t.$emit("sort",e)},start:t.onStart},scopedSlots:t._u([{key:"footer",fn:function(){return[t._t("footer")]},proxy:!0}],null,!0)},"draggable",t.dragOptions,!1),[t._t("default")],2)}),[]).exports;const la=ct({data:()=>({error:null}),errorCaptured(t){return this.$panel.debug&&window.console.warn(t),this.error=t,!1},render(){return this.error?this.$slots.error?this.$slots.error[0]:this.$scopedSlots.error?this.$scopedSlots.error({error:this.error}):Vue.h("k-box",{attrs:{theme:"negative"}},this.error.message??this.error):this.$slots.default[0]}},null,null).exports;const ca=ct({props:{html:String},mounted(){try{let t=this.$refs.iframe.contentWindow.document;t.open(),t.write(this.html),t.close()}catch(t){console.error(t)}}},(function(){var t=this,e=t._self._c;return e("k-overlay",{staticClass:"k-fatal",attrs:{visible:!0}},[e("div",{staticClass:"k-fatal-box"},[e("div",{staticClass:"k-notification",attrs:{"data-theme":"negative"}},[e("p",[t._v("The JSON response could not be parsed")]),e("k-button",{attrs:{icon:"cancel"},on:{click:function(e){return e.stopPropagation(),t.$panel.notification.close()}}})],1),e("iframe",{ref:"iframe",staticClass:"k-fatal-iframe"})])])}),[]).exports;const ua=ct({icons:window.panel.plugins.icons,methods:{viewbox(t,e){const i=document.createElementNS("http://www.w3.org/2000/svg","svg");i.innerHTML=e,document.body.appendChild(i);const s=i.getBBox(),n=(s.width+2*s.x+(s.height+2*s.y))/2,o=Math.abs(n-16),a=Math.abs(n-24);return document.body.removeChild(i),o element with the corresponding viewBox attribute.`),"0 0 16 16"):"0 0 24 24"}}},(function(){var t=this,e=t._self._c;return e("svg",{staticClass:"k-icons",attrs:{"aria-hidden":"true",xmlns:"http://www.w3.org/2000/svg",overflow:"hidden"}},[e("defs",t._l(t.$options.icons,(function(i,s){return e("symbol",{key:s,attrs:{id:"icon-"+s,viewBox:t.viewbox(s,i)},domProps:{innerHTML:t._s(i)}})})),0)])}),[]).exports;const da=ct({mounted(){window.panel.deprecated(' will be removed in a future version. Use instead.')}},(function(){var t=this._self._c;return t("span",{staticClass:"k-loader"},[t("k-icon",{staticClass:"k-loader-icon",attrs:{type:"loader"}})],1)}),[]).exports;const pa=ct({},(function(){var t=this,e=t._self._c;return t.$panel.notification.isOpen?e("div",{staticClass:"k-notification",attrs:{"data-theme":t.$panel.notification.theme}},[e("p",[t._v(t._s(t.$panel.notification.message))]),e("k-button",{attrs:{icon:"cancel"},on:{click:function(e){return t.$panel.notification.close()}}})],1):t._e()}),[]).exports;const ha=ct({},(function(){var t=this,e=t._self._c;return!t.$panel.system.isLocal&&t.$panel.isOffline?e("div",{staticClass:"k-offline-warning"},[e("p",[e("k-icon",{attrs:{type:"bolt"}}),t._v(" "+t._s(t.$t("error.offline")))],1)]):t._e()}),[]).exports,ma=(t,e=!1)=>{if(t>=0&&t<=100)return!0;if(e)throw new Error("value has to be between 0 and 100");return!1};const fa=ct({props:{value:{type:Number,default:0,validator:ma}},data(){return{state:this.value}},watch:{value(t){this.state=t}},methods:{set(t){window.panel.deprecated(": `set` method will be removed in a future version. Use the `value` prop instead."),ma(t,!0),this.state=t}}},(function(){var t=this;return(0,t._self._c)("progress",{staticClass:"k-progress",attrs:{max:"100"},domProps:{value:t.state}},[t._v(t._s(t.state)+"%")])}),[]).exports;const ga=ct({},(function(){return(0,this._self._c)("k-button",{staticClass:"k-sort-handle k-sort-button",attrs:{title:this.$t("sort.drag"),icon:"sort","aria-hidden":"true"}})}),[]).exports,ka={install(t){t.component("k-draggable",ra),t.component("k-error-boundary",la),t.component("k-fatal",ca),t.component("k-icon",Go),t.component("k-icons",ua),t.component("k-loader",da),t.component("k-notification",pa),t.component("k-offline-warning",ha),t.component("k-progress",fa),t.component("k-sort-handle",ga)}};const ba=ct({props:{crumbs:{type:Array,default:()=>[]},label:{type:String,default:"Breadcrumb"},view:Object},computed:{dropdown(){return this.segments.map((t=>({...t,text:t.label,icon:"angle-right"})))},segments(){const t=[];return this.view&&t.push({link:this.view.link,label:this.view.label??this.view.breadcrumbLabel,icon:this.view.icon,loading:this.$panel.isLoading}),[...t,...this.crumbs]}},mounted(){this.view&&window.panel.deprecated(": `view` prop will be removed in a future version. Use `crumbs` instead.")}},(function(){var t=this,e=t._self._c;return e("nav",{staticClass:"k-breadcrumb",attrs:{"aria-label":t.label}},[t.segments.length>1?e("div",{staticClass:"k-breadcrumb-dropdown"},[e("k-button",{attrs:{icon:"home"},on:{click:function(e){return t.$refs.dropdown.toggle()}}}),e("k-dropdown-content",{ref:"dropdown",attrs:{options:t.dropdown}})],1):t._e(),e("ol",t._l(t.segments,(function(i,s){return e("li",{key:s},[e("k-button",{staticClass:"k-breadcrumb-link",attrs:{icon:i.loading?"loader":i.icon,link:i.link,disabled:!i.link,text:i.text??i.label,title:i.text??i.label,current:s===t.segments.length-1&&"page",variant:"dimmed",size:"sm"}})],1)})),0)])}),[]).exports;const va=ct({props:{items:{type:Array},name:{default:"items",type:String},selected:{type:String},type:{default:"radio",type:String}},emits:["select"]},(function(){var t=this,e=t._self._c;return e("nav",{staticClass:"k-browser"},[e("div",{staticClass:"k-browser-items"},t._l(t.items,(function(i){return e("label",{key:i.value,staticClass:"k-browser-item",attrs:{"aria-selected":t.selected===i.value}},[e("input",{attrs:{name:t.name,type:t.type},domProps:{checked:t.selected===i.value},on:{change:function(e){return t.$emit("select",i)}}}),i.image?e("k-item-image",{staticClass:"k-browser-item-image",attrs:{image:{...i.image,cover:!0,back:"black"}}}):t._e(),e("span",{staticClass:"k-browser-item-info"},[t._v(" "+t._s(i.label)+" ")])],1)})),0)])}),[]).exports,ya={props:{disabled:Boolean,download:Boolean,rel:String,tabindex:[String,Number],target:String,title:String}};const $a=ct({mixins:[ya],props:{to:[String,Function]},emits:["click"],computed:{href(){return"function"==typeof this.to?"":"/"!==this.to[0]||this.target?!0===this.to.includes("@")&&!1===this.to.includes("/")&&!1===this.to.startsWith("mailto:")?"mailto:"+this.to:this.to:this.$url(this.to)},relAttr(){return"_blank"===this.target?"noreferrer noopener":this.rel}},methods:{isRoutable(t){if(t.metaKey||t.altKey||t.ctrlKey||t.shiftKey)return!1;if(t.defaultPrevented)return!1;if(void 0!==t.button&&0!==t.button)return!1;if(this.target)return!1;if("string"==typeof this.href){if(this.href.includes("://")||this.href.startsWith("//"))return!1;if(this.href.includes("mailto:"))return!1}return!0},onClick(t){if(!0===this.disabled)return t.preventDefault(),!1;"function"==typeof this.to&&(t.preventDefault(),this.to()),this.isRoutable(t)&&(t.preventDefault(),this.$go(this.to)),this.$emit("click",t)}}},(function(){var t=this,e=t._self._c;return t.to&&!t.disabled?e("a",{ref:"link",staticClass:"k-link",attrs:{download:t.download,href:t.href,rel:t.relAttr,tabindex:t.tabindex,target:t.target,title:t.title},on:{click:t.onClick}},[t._t("default")],2):e("span",{staticClass:"k-link",attrs:{title:t.title,"aria-disabled":""}},[t._t("default")],2)}),[]).exports;const wa=ct({mixins:[{mixins:[ya],props:{autofocus:Boolean,click:{type:Function,default:()=>{}},current:[String,Boolean],dialog:String,drawer:String,dropdown:Boolean,element:String,icon:String,id:[String,Number],link:String,responsive:[Boolean,String],role:String,selected:[String,Boolean],size:String,text:[String,Number],theme:String,tooltip:String,type:{type:String,default:"button"},variant:String}}],inheritAttrs:!1,emits:["click"],computed:{attrs(){const t={"aria-current":this.current,"aria-disabled":this.disabled,"aria-label":this.text??this.title,"aria-selected":this.selected,"data-responsive":this.responsive,"data-size":this.size,"data-theme":this.theme,"data-variant":this.variant,id:this.id,tabindex:this.tabindex,title:this.title??this.tooltip};return"k-link"===this.component?(t.disabled=this.disabled,t.download=this.download,t.to=this.link,t.rel=this.rel,t.target=this.target):"button"===this.component&&(t.autofocus=this.autofocus,t.role=this.role,t.type=this.type),this.dropdown&&(t["aria-haspopup"]="menu",t["data-dropdown"]=this.dropdown),t},component(){return this.element?this.element:this.link?"k-link":"button"}},mounted(){this.tooltip&&window.panel.deprecated(": the `tooltip` prop will be removed in a future version. Use the `title` prop instead.")},methods:{focus(){var t,e;null==(e=(t=this.$el).focus)||e.call(t)},onClick(t){var e;return this.disabled?(t.preventDefault(),!1):this.dialog?this.$dialog(this.dialog):this.drawer?this.$drawer(this.drawer):(null==(e=this.click)||e.call(this,t),void this.$emit("click",t))}}},(function(){var t=this,e=t._self._c;return e(t.component,t._b({tag:"component",staticClass:"k-button",attrs:{"data-has-icon":Boolean(t.icon),"data-has-text":Boolean(t.text||t.$slots.default)},on:{click:t.onClick}},"component",t.attrs,!1),[t.icon?e("span",{staticClass:"k-button-icon"},[e("k-icon",{attrs:{type:t.icon}})],1):t._e(),t.text||t.$slots.default?e("span",{staticClass:"k-button-text"},[t._t("default",(function(){return[t._v(" "+t._s(t.text)+" ")]}))],2):t._e(),t.dropdown&&(t.text||t.$slots.default)?e("span",{staticClass:"k-button-arrow"},[e("k-icon",{attrs:{type:"angle-dropdown"}})],1):t._e()])}),[]).exports;const xa=ct({props:{buttons:Array,layout:String,variant:String,theme:String,size:String,responsive:Boolean}},(function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-button-group",attrs:{"data-layout":t.layout}},[t.$slots.default?t._t("default"):t._l(t.buttons,(function(i,s){return e("k-button",t._b({key:s},"k-button",{variant:t.variant,theme:t.theme,size:t.size,responsive:t.responsive,...i},!1))}))],2)}),[]).exports;const _a=ct({props:{limit:{default:50,type:Number},opened:{type:String},selected:{type:String}},emits:["select"],data(){return{files:[],page:null,pagination:null,view:this.opened?"files":"tree"}},methods:{paginate(t){this.selectPage(this.page,t.page)},selectFile(t){this.$emit("select",t)},async selectPage(t,e=1){this.page=t;const i="/"===t.id?"/site/files":"/pages/"+this.$api.pages.id(t.id)+"/files",{data:s,pagination:n}=await this.$api.get(i,{select:"filename,id,panelImage,url,uuid",limit:this.limit,page:e});this.pagination=n,this.files=s.map((t=>({label:t.filename,image:t.panelImage,id:t.id,url:t.url,uuid:t.uuid,value:t.uuid??t.url}))),this.view="files"},async togglePage(){await this.$nextTick(),this.$refs.tree.scrollIntoView({behaviour:"smooth",block:"nearest",inline:"nearest"})}}},(function(){var t,e,i=this,s=i._self._c;return s("div",{staticClass:"k-file-browser",attrs:{"data-view":i.view}},[s("div",{staticClass:"k-file-browser-layout"},[s("aside",{ref:"tree",staticClass:"k-file-browser-tree"},[s("k-page-tree",{attrs:{current:(null==(t=i.page)?void 0:t.value)??i.opened},on:{select:i.selectPage,toggleBranch:i.togglePage}})],1),s("div",{ref:"items",staticClass:"k-file-browser-items"},[s("k-button",{staticClass:"k-file-browser-back-button",attrs:{icon:"angle-left",text:null==(e=i.page)?void 0:e.label},on:{click:function(t){i.view="tree"}}}),i.files.length?s("k-browser",{attrs:{items:i.files,selected:i.selected},on:{select:i.selectFile}}):i._e()],1),s("div",{staticClass:"k-file-browser-pagination",on:{click:function(t){t.stopPropagation()}}},[i.pagination?s("k-pagination",i._b({attrs:{details:!0},on:{paginate:i.paginate}},"k-pagination",i.pagination,!1)):i._e()],1)])])}),[]).exports;const Ca=ct({props:{tab:String,tabs:{type:Array,default:()=>[]}},computed:{withBadges(){const t=Object.keys(this.$store.getters["content/changes"]());return this.tabs.map((e=>{const i=[];for(const t in e.columns)for(const s in e.columns[t].sections)if("fields"===e.columns[t].sections[s].type)for(const n in e.columns[t].sections[s].fields)i.push(n);return e.badge=i.filter((e=>t.includes(e.toLowerCase()))).length,e}))}}},(function(){var t=this;return(0,t._self._c)("k-tabs",{staticClass:"k-model-tabs",attrs:{tab:t.tab,tabs:t.withBadges,theme:"notice"}})}),[]).exports;const Sa=ct({props:{axis:String,disabled:Boolean,element:{type:String,default:"div"},select:{type:String,default:":where(button, a):not(:disabled)"}},emits:["next","prev"],computed:{keys(){switch(this.axis){case"x":return{ArrowLeft:this.prev,ArrowRight:this.next};case"y":return{ArrowUp:this.prev,ArrowDown:this.next};default:return{ArrowLeft:this.prev,ArrowRight:this.next,ArrowUp:this.prev,ArrowDown:this.next}}}},mounted(){this.$el.addEventListener("keydown",this.keydown)},destroyed(){this.$el.removeEventListener("keydown",this.keydown)},methods:{focus(t=0,e){this.move(t,e)},keydown(t){var e;if(this.disabled)return!1;null==(e=this.keys[t.key])||e.apply(this,[t])},move(t=0,e){var i;const s=[...this.$el.querySelectorAll(this.select)];let n=s.findIndex((t=>t===document.activeElement||t.contains(document.activeElement)));switch(-1===n&&(n=0),t){case"first":t=0;break;case"next":t=n+1;break;case"last":t=s.length-1;break;case"prev":t=n-1}t<0?this.$emit("prev"):t>=s.length?this.$emit("next"):null==(i=s[t])||i.focus(),null==e||e.preventDefault()},next(t){this.move("next",t)},prev(t){this.move("prev",t)}}},(function(){var t=this;return(0,t._self._c)(t.element,{tag:"component",staticClass:"k-navigate"},[t._t("default")],2)}),[]).exports;const Oa=ct({name:"k-tree",inheritAttrs:!1,props:{element:{type:String,default:"k-tree"},current:{type:String},items:{type:[Array,Object]},level:{default:0,type:Number}},emits:["close","open","select","toggle"],data(){return{state:this.items}},methods:{arrow:t=>!0===t.loading?"loader":t.open?"angle-down":"angle-right",close(t){this.$set(t,"open",!1),this.$emit("close",t)},isItem:(t,e)=>t.value===e,open(t){this.$set(t,"open",!0),this.$emit("open",t)},select(t){this.$emit("select",t)},toggle(t){this.$emit("toggle",t),!0===t.open?this.close(t):this.open(t)}}},(function(){var t=this,e=t._self._c;return e("ul",{staticClass:"k-tree",class:t.$options.name,style:{"--tree-level":t.level}},t._l(t.state,(function(i){return e("li",{key:i.value,attrs:{"aria-expanded":i.open,"aria-current":t.isItem(i,t.current)}},[e("p",{staticClass:"k-tree-branch",attrs:{"data-has-subtree":i.hasChildren&&i.open}},[e("button",{staticClass:"k-tree-toggle",attrs:{disabled:!i.hasChildren,type:"button"},on:{click:function(e){return t.toggle(i)}}},[e("k-icon",{attrs:{type:t.arrow(i)}})],1),e("button",{staticClass:"k-tree-folder",attrs:{disabled:i.disabled,type:"button"},on:{click:function(e){return t.select(i)},dblclick:function(e){return t.toggle(i)}}},[e("k-icon-frame",{attrs:{icon:i.icon??"folder"}}),e("span",{staticClass:"k-tree-folder-label"},[t._v(t._s(i.label))])],1)]),i.hasChildren&&i.open?[e(t.$options.name,t._b({ref:i.value,refInFor:!0,tag:"component",attrs:{items:i.children,level:t.level+1},on:{close:function(e){return t.$emit("close",e)},open:function(e){return t.$emit("open",e)},select:function(e){return t.$emit("select",e)},toggle:function(e){return t.$emit("toggle",e)}}},"component",t.$props,!1))]:t._e()],2)})),0)}),[]).exports;const Ma=ct({name:"k-page-tree",extends:Oa,inheritAttrs:!1,props:{current:{type:String},move:{type:String},root:{default:!0,type:Boolean}},data:()=>({state:[]}),async mounted(){if(this.items)this.state=this.items;else{const t=await this.load(null);await this.open(t[0]),this.state=this.root?t:t[0].children,this.current&&this.preselect(this.current)}},methods:{findItem(t){return this.state.find((e=>this.isItem(e,t)))},isItem:(t,e)=>t.value===e||t.uuid===e||t.id===e,async load(t){return await this.$panel.get("site/tree",{query:{move:this.move??null,parent:t}})},async open(t){if(t){if(!1===t.hasChildren)return!1;this.$set(t,"loading",!0),"string"==typeof t.children&&(t.children=await this.load(t.children)),this.$set(t,"open",!0),this.$set(t,"loading",!1)}},async preselect(t){const e=(await this.$panel.get("site/tree/parents",{query:{page:t,root:this.root}})).data;let i=this;for(let n=0;nPromise.resolve()}},emits:["paginate"],computed:{detailsText(){return 1===this.limit?this.start:this.start+"-"+this.end},end(){return Math.min(this.start-1+this.limit,this.total)},offset(){return this.start-1},pages(){return Math.ceil(this.total/this.limit)},start(){return(this.page-1)*this.limit+1}},methods:{async goTo(t){var e;try{await this.validate(t),null==(e=this.$refs.dropdown)||e.close();const i=((t=Math.max(1,Math.min(t,this.pages)))-1)*this.limit+1;this.$emit("paginate",{page:t,start:i,end:Math.min(i-1+this.limit,this.total),limit:this.limit,offset:i-1,total:this.total})}catch{}},prev(){this.goTo(this.page-1)},next(){this.goTo(this.page+1)}}},(function(){var t=this,e=t._self._c;return t.pages>1?e("k-button-group",{staticClass:"k-pagination",attrs:{layout:"collapsed"},nativeOn:{keydown:[function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"left",37,e.key,["Left","ArrowLeft"])||"button"in e&&0!==e.button?null:t.prev.apply(null,arguments)},function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"right",39,e.key,["Right","ArrowRight"])||"button"in e&&2!==e.button?null:t.next.apply(null,arguments)}]}},[e("k-button",{staticClass:"k-pagination-button",attrs:{disabled:t.start<=1,title:t.$t("prev"),icon:"angle-left",size:"xs",variant:"filled"},on:{click:t.prev}}),t.details?[e("k-button",{staticClass:"k-pagination-details",attrs:{disabled:t.total<=t.limit,text:t.total>1?`${t.detailsText} / ${t.total}`:t.total,size:"xs",variant:"filled"},on:{click:function(e){return t.$refs.dropdown.toggle()}}}),e("k-dropdown-content",{ref:"dropdown",staticClass:"k-pagination-selector",attrs:{"align-x":"end"},nativeOn:{keydown:[function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"left",37,e.key,["Left","ArrowLeft"])||"button"in e&&0!==e.button?null:void e.stopPropagation()},function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"right",39,e.key,["Right","ArrowRight"])||"button"in e&&2!==e.button?null:void e.stopPropagation()}]}},[e("form",{attrs:{method:"dialog"},on:{click:function(t){t.stopPropagation()},submit:function(e){return t.goTo(t.$refs.page.value)}}},[e("label",[t._v(" "+t._s(t.$t("pagination.page"))+": "),e("select",{ref:"page",attrs:{autofocus:!0}},t._l(t.pages,(function(i){return e("option",{key:i,domProps:{selected:t.page===i,value:i}},[t._v(" "+t._s(i)+" ")])})),0)]),e("k-button",{attrs:{type:"submit",icon:"check"}})],1)])]:t._e(),e("k-button",{staticClass:"k-pagination-button",attrs:{disabled:t.end>=t.total,title:t.$t("next"),icon:"angle-right",size:"xs",variant:"filled"},on:{click:t.next}})],2):t._e()}),[]).exports;const Ia=ct({props:{prev:{type:[Boolean,Object],default:!1},next:{type:[Boolean,Object],default:!1}},computed:{buttons(){return[{...this.button(this.prev),icon:"angle-left"},{...this.button(this.next),icon:"angle-right"}]},isFullyDisabled(){return 0===this.buttons.filter((t=>!t.disabled)).length}},methods:{button:t=>t||{disabled:!0,link:"#"}}},(function(){var t=this,e=t._self._c;return t.isFullyDisabled?t._e():e("k-button-group",{staticClass:"k-prev-next",attrs:{buttons:t.buttons,layout:"collapsed",size:"xs"}})}),[]).exports;const Da=ct({mixins:[zt],props:{defaultType:String,isLoading:Boolean,pagination:{type:Object,default:()=>({})},results:Array,types:{type:Object,default:()=>({})}},emits:["close","more","navigate","search"],data(){return{selected:-1,type:this.types[this.defaultType]?this.defaultType:Object.keys(this.types)[0]}},computed:{typesDropdown(){return Object.values(this.types).map((t=>({...t,current:this.type===t.id,click:()=>{this.type=t.id,this.focus()}})))}},watch:{type(){this.search()}},methods:{focus(){var t;null==(t=this.$refs.input)||t.focus()},onDown(){this.select(Math.min(this.selected+1,this.results.length-1))},onEnter(){this.$emit("navigate",this.results[this.selected]??this.results[0])},onUp(){this.select(Math.max(this.selected-1,-1))},async search(){var t,e;null==(t=this.$refs.types)||t.close(),null==(e=this.select)||e.call(this,-1),this.$emit("search",{type:this.type,query:this.query})},select(t){var e;this.selected=t;const i=(null==(e=this.$refs.results)?void 0:e.$el.querySelectorAll(".k-item"))??[];for(const s of i)delete s.dataset.selected;t>=0&&(i[t].dataset.selected=!0)}}},(function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-search-bar"},[e("div",{staticClass:"k-search-bar-input"},[t.typesDropdown.length>1?[e("k-button",{staticClass:"k-search-bar-types",attrs:{dropdown:!0,icon:t.types[t.type].icon,text:t.types[t.type].label,variant:"dimmed"},on:{click:function(e){return t.$refs.types.toggle()}}}),e("k-dropdown-content",{ref:"types",attrs:{options:t.typesDropdown}})]:t._e(),e("k-search-input",{ref:"input",attrs:{"aria-label":t.$t("search"),autofocus:!0,value:t.query},on:{input:function(e){t.query=e}},nativeOn:{keydown:[function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"down",40,e.key,["Down","ArrowDown"])?null:(e.preventDefault(),t.onDown.apply(null,arguments))},function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"up",38,e.key,["Up","ArrowUp"])?null:(e.preventDefault(),t.onUp.apply(null,arguments))},function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"enter",13,e.key,"Enter")?null:t.onEnter.apply(null,arguments)}]}}),e("k-button",{staticClass:"k-search-bar-close",attrs:{icon:t.isLoading?"loader":"cancel",title:t.$t("close")},on:{click:function(e){return t.$emit("close")}}})],2),t.results?e("div",{staticClass:"k-search-bar-results"},[t.results.length?e("k-collection",{ref:"results",attrs:{items:t.results},nativeOn:{mouseout:function(e){return t.select(-1)}}}):t._e(),e("footer",{staticClass:"k-search-bar-footer"},[0===t.results.length?e("p",[t._v(" "+t._s(t.$t("search.results.none"))+" ")]):t._e(),t.results.length will be removed in a future version. Use instead.')}},(function(){var t=this,e=t._self._c;return e("span",{staticClass:"k-button",attrs:{id:t.id,"data-disabled":!0,"data-responsive":t.responsive,"data-theme":t.theme,title:t.tooltip}},[t.icon?e("k-icon",{staticClass:"k-button-icon",attrs:{type:t.icon,alt:t.tooltip}}):t._e(),t.$slots.default?e("span",{staticClass:"k-button-text"},[t._t("default")],2):t._e()],1)}),[]).exports;const La=ct({inheritAttrs:!1,props:{autofocus:Boolean,current:[String,Boolean],icon:String,id:[String,Number],link:String,rel:String,responsive:Boolean,role:String,target:String,tabindex:String,theme:String,tooltip:String},mounted(){window.panel.deprecated(' will be removed in a future version. Use instead.')},methods:{focus(){this.$el.focus()}}},(function(){var t=this,e=t._self._c;return e("k-link",{staticClass:"k-button",attrs:{id:t.id,"aria-current":t.current,autofocus:t.autofocus,"data-theme":t.theme,"data-responsive":t.responsive,rel:t.rel,role:t.role,tabindex:t.tabindex,target:t.target,title:t.tooltip,to:t.link}},[t.icon?e("k-icon",{staticClass:"k-button-icon",attrs:{type:t.icon,alt:t.tooltip}}):t._e(),t.$slots.default?e("span",{staticClass:"k-button-text"},[t._t("default")],2):t._e()],1)}),[]).exports;const Ta=ct({inheritAttrs:!1,props:{autofocus:Boolean,click:{type:Function,default:()=>{}},current:[String,Boolean],icon:String,id:[String,Number],responsive:Boolean,role:String,tabindex:String,theme:String,tooltip:String,type:{type:String,default:"button"}},mounted(){window.panel.deprecated(" will be removed in a future version. Use instead.")}},(function(){var t=this,e=t._self._c;return e("button",{staticClass:"k-button",attrs:{id:t.id,"aria-current":t.current,autofocus:t.autofocus,"data-theme":t.theme,"data-responsive":t.responsive,role:t.role,tabindex:t.tabindex,title:t.tooltip,type:t.type},on:{click:t.click}},[t.icon?e("k-icon",{staticClass:"k-button-icon",attrs:{type:t.icon,alt:t.tooltip}}):t._e(),t.$slots.default?e("span",{staticClass:"k-button-text"},[t._t("default")],2):t._e()],1)}),[]).exports,Ba={install(t){t.component("k-breadcrumb",ba),t.component("k-browser",va),t.component("k-button",wa),t.component("k-button-group",xa),t.component("k-file-browser",_a),t.component("k-link",$a),t.component("k-model-tabs",Ca),t.component("k-navigate",Sa),t.component("k-page-tree",Ma),t.component("k-pagination",Aa),t.component("k-prev-next",Ia),t.component("k-search-bar",Da),t.component("k-tag",ja),t.component("k-tags",Us),t.component("k-tree",Oa),t.component("k-button-disabled",Ea),t.component("k-button-link",La),t.component("k-button-native",Ta)}};const qa=ct({props:{buttons:Array,headline:String,invalid:Boolean,label:String,link:String,required:Boolean}},(function(){var t=this,e=t._self._c;return e("section",{staticClass:"k-section",attrs:{"data-invalid":t.invalid}},[t.label||t.headline||t.buttons||t.$slots.options?e("header",{staticClass:"k-section-header"},[e("k-label",{attrs:{invalid:t.invalid,link:t.link,required:t.required,title:t.label??t.headline,type:"section"}},[t._v(" "+t._s(t.label??t.headline)+" ")]),t._t("options",(function(){return[t.buttons?e("k-button-group",{staticClass:"k-section-buttons",attrs:{buttons:t.buttons,size:"xs",variant:"filled"}}):t._e()]}))],2):t._e(),t._t("default")],2)}),[]).exports;const Pa=ct({props:{empty:String,blueprint:String,lock:[Boolean,Object],parent:String,tab:Object},emits:["submit"],computed:{content(){return this.$store.getters["content/values"]()}},methods:{exists(t){return this.$helper.isComponent(`k-${t}-section`)}}},(function(){var t=this,e=t._self._c;return 0===t.tab.columns.length?e("k-box",{attrs:{html:!0,text:t.empty,theme:"info"}}):e("k-grid",{staticClass:"k-sections",attrs:{variant:"columns"}},t._l(t.tab.columns,(function(i,s){return e("k-column",{key:t.parent+"-column-"+s,attrs:{width:i.width,sticky:i.sticky}},[t._l(i.sections,(function(n,o){return[t.$helper.field.isVisible(n,t.content)?[t.exists(n.type)?e("k-"+n.type+"-section",t._b({key:t.parent+"-column-"+s+"-section-"+o+"-"+t.blueprint,tag:"component",class:"k-section-name-"+n.name,attrs:{column:i.width,lock:t.lock,name:n.name,parent:t.parent,timestamp:t.$panel.view.timestamp},on:{submit:function(e){return t.$emit("submit",e)}}},"component",n,!1)):[e("k-box",{key:t.parent+"-column-"+s+"-section-"+o,attrs:{text:t.$t("error.section.type.invalid",{type:n.type}),icon:"alert",theme:"negative"}})]]:t._e()]}))],2)})),1)}),[]).exports,Na={props:{blueprint:String,help:String,name:String,parent:String,timestamp:Number},methods:{load(){return this.$api.get(this.parent+"/sections/"+this.name)}}};const za=ct({mixins:[Na],inheritAttrs:!1,props:{lock:[Object,Boolean]},data:()=>({fields:{},isLoading:!0,issue:null}),computed:{values(){return this.$store.getters["content/values"]()}},watch:{timestamp(){this.fetch()}},mounted(){this.onInput=Nt(this.onInput,50),this.fetch()},methods:{async fetch(){try{const t=await this.load();this.fields=t.fields;for(const e in this.fields)this.fields[e].section=this.name,this.fields[e].endpoints={field:this.parent+"/fields/"+e,section:this.parent+"/sections/"+this.name,model:this.parent}}catch(t){this.issue=t}finally{this.isLoading=!1}},onInput(t,e,i){this.$store.dispatch("content/update",[i,t[i]])},onSubmit(t){this.$store.dispatch("content/update",[null,t]),this.$events.emit("keydown.cmd.s",t)}}},(function(){var t=this,e=t._self._c;return t.isLoading?t._e():e("k-section",{staticClass:"k-fields-section",attrs:{headline:t.issue?"Error":null}},[t.issue?e("k-box",{attrs:{text:t.issue.message,html:!1,icon:"alert",theme:"negative"}}):t._e(),e("k-form",{attrs:{fields:t.fields,validate:!0,value:t.values,disabled:t.lock&&"lock"===t.lock.state},on:{input:t.onInput,submit:t.onSubmit}})],1)}),[]).exports;const Fa=ct({mixins:[Na],inheritAttrs:!1,props:{column:String},data:()=>({data:[],error:null,isLoading:!1,isProcessing:!1,options:{columns:{},empty:null,headline:null,help:null,layout:"list",link:null,max:null,min:null,size:null,sortable:null},pagination:{page:null},searchterm:null,searching:!1}),computed:{addIcon:()=>"add",buttons(){let t=[];return this.canSearch&&t.push({icon:"filter",text:this.$t("filter"),click:this.onSearchToggle,responsive:!0}),this.canAdd&&t.push({icon:this.addIcon,text:this.$t("add"),click:this.onAdd,responsive:!0}),t},canAdd:()=>!0,canDrop:()=>!1,canSearch(){return this.options.search},collection(){return{columns:this.options.columns,empty:this.emptyPropsWithSearch,fields:this.options.fields,layout:this.options.layout,help:this.options.help,items:this.items,pagination:this.pagination,sortable:!this.isProcessing&&this.options.sortable,size:this.options.size}},emptyProps(){return{icon:"page",text:this.$t("pages.empty")}},emptyPropsWithSearch(){return{...this.emptyProps,text:this.searching?this.$t("search.results.none"):this.options.empty??this.emptyProps.text}},items(){return this.data},isInvalid(){var t;return!((null==(t=this.searchterm)?void 0:t.length)>0)&&(!!(this.options.min&&this.data.lengththis.options.max))},paginationId(){return"kirby$pagination$"+this.parent+"/"+this.name},type:()=>"models"},watch:{searchterm(){this.search()},timestamp(){this.reload()}},mounted(){this.search=Nt(this.search,200),this.load()},methods:{async load(t){this.isProcessing=!0,t||(this.isLoading=!0);const e=this.pagination.page??sessionStorage.getItem(this.paginationId)??null;try{const t=await this.$api.get(this.parent+"/sections/"+this.name,{page:e,searchterm:this.searchterm});this.options=t.options,this.pagination=t.pagination,this.data=t.data}catch(i){this.error=i.message}finally{this.isProcessing=!1,this.isLoading=!1}},onAction(){},onAdd(){},onChange(){},onDrop(){},onSort(){},onPaginate(t){sessionStorage.setItem(this.paginationId,t.page),this.pagination=t,this.reload()},onSearchToggle(){this.searching=!this.searching,this.searchterm=null},async reload(){await this.load(!0)},async search(){this.pagination.page=0,await this.reload()},update(){this.reload(),this.$events.emit("model.update")}}},(function(){var t=this,e=t._self._c;return!1===t.isLoading?e("k-section",{class:`k-models-section k-${t.type}-section`,attrs:{buttons:t.buttons,"data-processing":t.isProcessing,headline:t.options.headline??" ",invalid:t.isInvalid,link:t.options.link,required:Boolean(t.options.min)}},[t.error?e("k-box",{attrs:{icon:"alert",theme:"negative"}},[e("k-text",{attrs:{size:"small"}},[e("strong",[t._v(" "+t._s(t.$t("error.section.notLoaded",{name:t.name}))+": ")]),t._v(" "+t._s(t.error)+" ")])],1):[e("k-dropzone",{attrs:{disabled:!t.canDrop},on:{drop:t.onDrop}},[t.searching&&t.options.search?e("k-input",{staticClass:"k-models-section-search",attrs:{autofocus:!0,placeholder:t.$t("filter")+" …",value:t.searchterm,icon:"search",type:"text"},on:{input:function(e){t.searchterm=e},keydown:function(e){return!e.type.indexOf("key")&&t._k(e.keyCode,"esc",27,e.key,["Esc","Escape"])?null:t.onSearchToggle.apply(null,arguments)}}}):t._e(),e("k-collection",t._g(t._b({on:{action:t.onAction,change:t.onChange,sort:t.onSort,paginate:t.onPaginate}},"k-collection",t.collection,!1),t.canAdd?{empty:t.onAdd}:{}))],1)]],2):t._e()}),[]).exports;const Ya=ct({extends:Fa,computed:{addIcon:()=>"upload",canAdd(){return this.$panel.permissions.files.create&&!1!==this.options.upload},canDrop(){return!1!==this.canAdd},emptyProps(){return{icon:"image",text:this.$t("files.empty")}},items(){return this.data.map((t=>(t.sortable=this.options.sortable,t.column=this.column,t.options=this.$dropdown(t.link,{query:{view:"list",update:this.options.sortable,delete:this.data.length>this.options.min}}),t.data={"data-id":t.id,"data-template":t.template},t)))},type:()=>"files",uploadOptions(){return{...this.options.upload,url:this.$panel.urls.api+"/"+this.options.upload.api,on:{complete:()=>{this.$panel.notification.success({context:"view"}),this.$events.emit("file.upload")}}}}},mounted(){this.$events.on("model.update",this.reload),this.$events.on("file.sort",this.reload)},destroyed(){this.$events.off("model.update",this.reload),this.$events.off("file.sort",this.reload)},methods:{onAction(t,e){"replace"===t&&this.replace(e)},onAdd(){this.canAdd&&this.$panel.upload.pick(this.uploadOptions)},onDrop(t){this.canAdd&&this.$panel.upload.open(t,this.uploadOptions)},async onSort(t){if(!1===this.options.sortable)return!1;this.isProcessing=!0;try{await this.$api.patch(this.options.apiUrl+"/sort",{files:t.map((t=>t.id)),index:this.pagination.offset}),this.$panel.notification.success(),this.$events.emit("file.sort")}catch(e){this.$panel.error(e),this.reload()}finally{this.isProcessing=!1}},replace(t){this.$panel.upload.replace(t,this.uploadOptions)}}},null,null).exports;const Ra=ct({mixins:[Na],inheritAttrs:!1,data:()=>({icon:null,label:null,text:null,theme:null}),async mounted(){const t=await this.load();this.icon=t.icon,this.label=t.label,this.text=t.text,this.theme=t.theme??"info"}},(function(){var t=this,e=t._self._c;return e("k-section",{staticClass:"k-info-section",attrs:{headline:t.label}},[e("k-box",{attrs:{html:!0,icon:t.icon,text:t.text,theme:t.theme}})],1)}),[]).exports;const Ua=ct({extends:Fa,computed:{canAdd(){return this.options.add&&this.$panel.permissions.pages.create},items(){return this.data.map((t=>{const e=!1===t.permissions.changeStatus,i=this.$helper.page.status(t.status,e);return i.click=()=>this.$dialog(t.link+"/changeStatus"),t.flag={status:t.status,disabled:e,click:()=>this.$dialog(t.link+"/changeStatus")},t.sortable=t.permissions.sort&&this.options.sortable,t.deletable=this.data.length>this.options.min,t.column=this.column,t.buttons=[i,...t.buttons??[]],t.options=this.$dropdown(t.link,{query:{view:"list",delete:t.deletable,sort:t.sortable}}),t.data={"data-id":t.id,"data-status":t.status,"data-template":t.template},t}))},type:()=>"pages"},mounted(){this.$events.on("page.changeStatus",this.reload),this.$events.on("page.sort",this.reload)},destroyed(){this.$events.off("page.changeStatus",this.reload),this.$events.off("page.sort",this.reload)},methods:{onAdd(){this.canAdd&&this.$dialog("pages/create",{query:{parent:this.options.link??this.parent,view:this.parent,section:this.name}})},async onChange(t){let e=null;if(t.added&&(e="added"),t.moved&&(e="moved"),e){this.isProcessing=!0;const s=t[e].element,n=t[e].newIndex+1+this.pagination.offset;try{await this.$api.pages.changeStatus(s.id,"listed",n),this.$panel.notification.success(),this.$events.emit("page.sort",s)}catch(i){this.$panel.error({message:i.message,details:i.details}),await this.reload()}finally{this.isProcessing=!1}}}}},null,null).exports;const Ha=ct({mixins:[Na],data:()=>({headline:null,isLoading:!0,reports:null,size:null}),async mounted(){const t=await this.load();this.isLoading=!1,this.headline=t.headline,this.reports=t.reports,this.size=t.size},methods:{}},(function(){var t=this,e=t._self._c;return!1===t.isLoading?e("k-section",{staticClass:"k-stats-section",attrs:{headline:t.headline}},[t.reports.length>0?e("k-stats",{attrs:{reports:t.reports,size:t.size}}):e("k-empty",{attrs:{icon:"chart"}},[t._v(" "+t._s(t.$t("stats.empty")))])],1):t._e()}),[]).exports,Va={install(t){t.component("k-section",qa),t.component("k-sections",Pa),t.component("k-fields-section",za),t.component("k-files-section",Ya),t.component("k-info-section",Ra),t.component("k-pages-section",Ua),t.component("k-stats-section",Ha)}};const Ka=ct({components:{"k-highlight":()=>Y((()=>import("./Highlight.min.js")),__vite__mapDeps([5,1]),import.meta.url)},props:{language:{type:String}}},(function(){var t=this,e=t._self._c;return e("k-highlight",[e("div",[e("pre",{staticClass:"k-code",attrs:{"data-language":t.language}},[e("code",{key:t.$slots.default[0].text+"-"+t.language,class:t.language?`language-${t.language}`:null},[t._t("default")],2)])])])}),[]).exports;const Wa=ct({props:{link:String,size:{type:String},tag:{type:String,default:"h2"},theme:{type:String}},emits:["click"],mounted(){this.size&&window.panel.deprecated(": the `size` prop will be removed in a future version. Use the `tag` prop instead."),this.theme&&window.panel.deprecated(": the `theme` prop will be removed in a future version.")}},(function(){var t=this,e=t._self._c;return e(t.tag,{tag:"component",staticClass:"k-headline",attrs:{"data-theme":t.theme,"data-size":t.size},on:{click:function(e){return t.$emit("click",e)}}},[t.link?e("k-link",{attrs:{to:t.link}},[t._t("default")],2):t._t("default")],2)}),[]).exports;const Ja=ct({props:{input:{type:[String,Number]},invalid:{type:Boolean},link:{type:String},required:{default:!1,type:Boolean},type:{default:"field",type:String}},computed:{element(){return"section"===this.type?"h2":"label"}}},(function(){var t=this,e=t._self._c;return e(t.element,{tag:"component",staticClass:"k-label",class:"k-"+t.type+"-label",attrs:{for:t.input,"data-invalid":t.invalid}},[t.link?e("k-link",{attrs:{to:t.link}},[e("span",{staticClass:"k-label-text"},[t._t("default")],2)]):e("span",{staticClass:"k-label-text"},[t._t("default")],2),t.required&&!t.invalid?e("abbr",{attrs:{title:t.$t(t.type+".required")}},[t._v("✶")]):t._e(),e("abbr",{staticClass:"k-label-invalid",attrs:{title:t.$t(t.type+".invalid")}},[t._v("×")])],1)}),[]).exports;const Ga=ct({props:{align:String,html:String,size:String,theme:String},computed:{attrs(){return{class:"k-text","data-align":this.align,"data-size":this.size,"data-theme":this.theme}}},mounted(){this.theme&&window.panel.deprecated(': the `theme` prop will be removed in a future version. For help text, add `.k-help "` CSS class instead.')}},(function(){var t=this,e=t._self._c;return t.html?e("div",t._b({domProps:{innerHTML:t._s(t.html)}},"div",t.attrs,!1)):e("div",t._b({},"div",t.attrs,!1),[t._t("default")],2)}),[]).exports,Xa={install(t){t.component("k-code",Ka),t.component("k-headline",Wa),t.component("k-label",Ja),t.component("k-text",Ga)}},Za={props:{back:String,color:String,cover:{type:Boolean,default:!0},icon:String,type:String,url:String}};const Qa=ct({mixins:[Za],computed:{fallbackColor(){var t,e,i;return(null==(t=this.type)?void 0:t.startsWith("image/"))?"orange-500":(null==(e=this.type)?void 0:e.startsWith("audio/"))?"aqua-500":(null==(i=this.type)?void 0:i.startsWith("video/"))?"yellow-500":"white"},fallbackIcon(){var t,e,i;return(null==(t=this.type)?void 0:t.startsWith("image/"))?"image":(null==(e=this.type)?void 0:e.startsWith("audio/"))?"audio":(null==(i=this.type)?void 0:i.startsWith("video/"))?"video":"file"},isPreviewable(){return["image/jpeg","image/jpg","image/gif","image/png","image/webp","image/avif","image/svg+xml"].includes(this.type)}}},(function(){var t=this,e=t._self._c;return e("a",{staticClass:"k-upload-item-preview",attrs:{href:t.url,target:"_blank"}},[t.isPreviewable?e("k-image",{attrs:{cover:t.cover,src:t.url,back:t.back??"pattern"}}):e("k-icon-frame",{attrs:{color:t.color??t.fallbackColor,icon:t.icon??t.fallbackIcon,back:t.back??"black",ratio:"1/1"}})],1)}),[]).exports;const tr=ct({mixins:[Za],props:{completed:Boolean,editable:{type:Boolean,default:!0},error:[String,Boolean],extension:String,id:String,name:String,niceSize:String,progress:Number,removable:{type:Boolean,default:!0}},emits:["remove","rename"]},(function(){var t=this,e=t._self._c;return e("li",{staticClass:"k-upload-item",attrs:{"data-completed":t.completed}},[e("k-upload-item-preview",{attrs:{back:t.back,color:t.color,cover:t.cover,icon:t.icon,type:t.type,url:t.url}}),e("k-input",{staticClass:"k-upload-item-input",attrs:{disabled:t.completed||!t.editable,after:"."+t.extension,novalidate:!0,required:!0,value:t.name,allow:"a-z0-9@._-",type:"slug"},on:{input:function(e){return t.$emit("rename",e)}}}),e("div",{staticClass:"k-upload-item-body"},[e("p",{staticClass:"k-upload-item-meta"},[t._v(" "+t._s(t.niceSize)+" "),t.progress?[t._v(" - "+t._s(t.progress)+"% ")]:t._e()],2),t.error?e("p",{staticClass:"k-upload-item-error"},[t._v(" "+t._s(t.error)+" ")]):t.progress?e("k-progress",{staticClass:"k-upload-item-progress",attrs:{value:t.progress}}):t._e()],1),e("div",{staticClass:"k-upload-item-toggle"},[t.completed||t.progress||!t.removable?!t.completed&&t.progress?e("k-button",{attrs:{disabled:!0,icon:"loader"}}):t.completed?e("k-button",{attrs:{icon:"check",theme:"positive"},on:{click:function(e){return t.$emit("remove")}}}):t._e():e("k-button",{attrs:{icon:"remove"},on:{click:function(e){return t.$emit("remove")}}})],1)],1)}),[]).exports;const er=ct({props:{items:Array},emits:["remove","rename"]},(function(){var t=this,e=t._self._c;return e("ul",{staticClass:"k-upload-items"},t._l(t.items,(function(i){return e("k-upload-item",t._b({key:i.id,on:{rename:function(e){return t.$emit("rename",i,e)},remove:function(e){return t.$emit("remove",i)}}},"k-upload-item",i,!1))})),1)}),[]).exports,ir={install(t){t.component("k-upload-item",tr),t.component("k-upload-item-preview",Qa),t.component("k-upload-items",er)}};const sr=ct({props:{status:{default:"missing",type:String}}},(function(){var t=this,e=t._self._c;return t.$panel.activation.isOpen?e("div",{staticClass:"k-activation"},[e("p",[e("strong",[t._v(t._s(t.$t(`license.status.${t.status}.bubble`)))]),"missing"===t.status?[e("a",{attrs:{href:"https://getkirby.com/buy",target:"_blank"}},[t._v(t._s(t.$t("license.buy")))]),t._v(" & "),e("button",{attrs:{type:"button"},on:{click:function(e){return t.$dialog("registration")}}},[t._v(" "+t._s(t.$t("license.activate"))+" ")])]:t._e()],2),e("k-button",{staticClass:"k-activation-toggle",attrs:{icon:"cancel-small"},on:{click:function(e){return t.$panel.activation.close()}}})],1):t._e()}),[]).exports;const nr=ct({computed:{notification(){return"view"!==this.$panel.notification.context||this.$panel.notification.isFatal?null:this.$panel.notification}}},(function(){var t=this,e=t._self._c;return e("k-panel",{staticClass:"k-panel-inside"},[e("k-panel-menu"),e("main",{staticClass:"k-panel-main"},[e("k-topbar",{attrs:{breadcrumb:t.$panel.view.breadcrumb,view:t.$panel.view}},[t._t("topbar")],2),t._t("default")],2),t.notification&&"error"!==t.notification.type?e("k-button",{staticClass:"k-panel-notification",attrs:{icon:t.notification.icon,text:t.notification.message,theme:t.notification.theme,variant:"filled"},on:{click:function(e){return t.notification.close()}}}):t._e()],1)}),[]).exports;const or=ct({data:()=>({over:!1}),computed:{activationButton(){return"missing"===this.$panel.license?{click:()=>this.$dialog("registration"),text:this.$t("activate")}:"legacy"===this.$panel.license&&{click:()=>this.$dialog("license"),text:this.$t("renew")}},hasSearch(){return this.$helper.object.length(this.$panel.searches)>0},menus(){return this.$helper.array.split(this.$panel.menu.entries,"-")}}},(function(){var t=this,e=t._self._c;return e("nav",{staticClass:"k-panel-menu",attrs:{"aria-label":t.$t("menu"),"data-hover":t.$panel.menu.hover},on:{mouseenter:function(e){t.$panel.menu.hover=!0},mouseleave:function(e){t.$panel.menu.hover=!1}}},[e("div",{staticClass:"k-panel-menu-body"},[t.hasSearch?e("k-button",{staticClass:"k-panel-menu-search k-panel-menu-button",attrs:{text:t.$t("search"),icon:"search"},on:{click:function(e){return t.$panel.search()}}}):t._e(),t._l(t.menus,(function(i,s){return e("menu",{key:s,staticClass:"k-panel-menu-buttons",attrs:{"data-second-last":s===t.menus.length-2}},t._l(i,(function(i){return e("k-button",t._b({key:i.id,staticClass:"k-panel-menu-button",attrs:{title:i.title??i.text}},"k-button",i,!1))})),1)})),t.activationButton?e("menu",[e("k-button",t._b({staticClass:"k-activation-button k-panel-menu-button",attrs:{icon:"key",theme:"love",variant:"filled"}},"k-button",t.activationButton,!1)),e("k-activation",{attrs:{status:t.$panel.license}})],1):t._e()],2),e("k-button",{staticClass:"k-panel-menu-toggle",attrs:{icon:t.$panel.menu.isOpen?"angle-left":"angle-right",title:t.$panel.menu.isOpen?t.$t("collapse"):t.$t("expand"),size:"xs"},on:{click:function(e){return t.$panel.menu.toggle()}}})],1)}),[]).exports;const ar=ct({},(function(){return(0,this._self._c)("k-panel",{staticClass:"k-panel-outside",attrs:{tabindex:"0"}},[this._t("default")],2)}),[]).exports;const rr=ct({},(function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-panel",attrs:{"data-dragging":t.$panel.drag.isDragging,"data-loading":t.$panel.isLoading,"data-language":t.$panel.language.code,"data-language-default":t.$panel.language.isDefault,"data-menu":t.$panel.menu.isOpen?"true":"false","data-role":t.$panel.user.role,"data-translation":t.$panel.translation.code,"data-user":t.$panel.user.id,dir:t.$panel.direction}},[t._t("default"),t.$panel.dialog.isOpen&&!t.$panel.dialog.legacy?e("k-fiber-dialog"):t._e(),t.$panel.drawer.isOpen&&!t.$panel.drawer.legacy?e("k-fiber-drawer"):t._e(),t.$panel.notification.isFatal&&t.$panel.notification.isOpen?e("k-fatal",{attrs:{html:t.$panel.notification.message}}):t._e(),e("k-offline-warning"),e("k-icons"),e("k-overlay",{attrs:{nested:t.$panel.drawer.history.milestones.length>1,visible:t.$panel.drawer.isOpen,type:"drawer"},on:{close:function(e){return t.$panel.drawer.close()}}},[e("portal-target",{staticClass:"k-drawer-portal k-portal",attrs:{name:"drawer",multiple:""}})],1),e("k-overlay",{attrs:{visible:t.$panel.dialog.isOpen,type:"dialog"},on:{close:function(e){return t.$panel.dialog.close()}}},[e("portal-target",{staticClass:"k-dialog-portal k-portal",attrs:{name:"dialog",multiple:""}})],1),e("portal-target",{staticClass:"k-overlay-portal k-portal",attrs:{name:"overlay",multiple:""}})],2)}),[]).exports;const lr=ct({props:{breadcrumb:Array,view:Object},computed:{crumbs(){return[{link:this.view.link,label:this.view.label??this.view.breadcrumbLabel,icon:this.view.icon,loading:this.$panel.isLoading},...this.breadcrumb]}}},(function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-topbar"},[e("k-button",{staticClass:"k-panel-menu-proxy",attrs:{icon:"bars"},on:{click:function(e){return t.$panel.menu.toggle()}}}),e("k-breadcrumb",{staticClass:"k-topbar-breadcrumb",attrs:{crumbs:t.crumbs}}),e("div",{staticClass:"k-topbar-spacer"}),e("div",{staticClass:"k-topbar-signals"},[t._t("default")],2)],1)}),[]).exports,cr={install(t){t.component("k-activation",sr),t.component("k-panel",rr),t.component("k-panel-inside",nr),t.component("k-panel-menu",or),t.component("k-panel-outside",ar),t.component("k-topbar",lr),t.component("k-inside",nr),t.component("k-outside",ar)}};const ur=ct({props:{error:String,layout:String}},(function(){var t=this,e=t._self._c;return e(`k-panel-${t.layout}`,{tag:"component",staticClass:"k-error-view"},["outside"===t.layout?[e("div",[e("k-box",{attrs:{icon:"alert",theme:"negative"}},[t._v(t._s(t.error))])],1)]:[e("k-header",[t._v(t._s(t.$t("error")))]),e("k-box",{attrs:{icon:"alert",theme:"negative"}},[t._v(t._s(t.error))])]],2)}),[]).exports;const dr=ct({mixins:[zt],props:{type:{default:"pages",type:String}},data:()=>({query:new URLSearchParams(window.location.search).get("query"),pagination:{},results:[]}),computed:{currentType(){return this.$panel.searches[this.type]??Object.values(this.$panel.searches)[0]},empty(){return this.isLoading?this.$t("searching")+"…":this.query.length<2?this.$t("search.min",{min:2}):this.$t("search.results.none")},isLoading(){return this.$panel.searcher.isLoading},tabs(){const t=[];for(const e in this.$panel.searches){const i=this.$panel.searches[e];t.push({label:i.label,link:"/search/?type="+e+"&query="+this.query,name:e})}return t}},watch:{isLoading(t){this.$panel.isLoading=t},query:{handler(){this.search(1)},immediate:!0},type(){this.search()}},methods:{focus(){var t;null==(t=this.$refs.input)||t.focus()},onPaginate(t){this.search(t.page)},async search(t){t||(t=new URLSearchParams(window.location.search).get("page")??1);const e=this.$panel.url(window.location,{type:this.currentType.id,query:this.query,page:t});window.history.pushState("","",e.toString());const i=await this.$panel.search(this.currentType.id,this.query,{page:t,limit:15});i&&(this.results=i.results??[],this.pagination=i.pagination)}}},(function(){var t=this,e=t._self._c;return e("k-panel-inside",{staticClass:"k-search-view"},[e("k-header",[t._v(" "+t._s(t.$t("search"))+" "),e("k-input",{ref:"input",staticClass:"k-search-view-input",attrs:{slot:"buttons","aria-label":t.$t("search"),autofocus:!0,icon:t.isLoading?"loader":"search",placeholder:t.$t("search")+" …",spellcheck:!1,value:t.query,type:"text"},on:{input:function(e){t.query=e}},slot:"buttons"})],1),e("k-tabs",{attrs:{tab:t.currentType.id,tabs:t.tabs}}),e("div",{staticClass:"k-search-view-results"},[e("k-collection",{attrs:{items:t.results,empty:{icon:t.isLoading?"loader":"search",text:t.empty},pagination:t.pagination},on:{paginate:t.onPaginate}})],1)],1)}),[]).exports;const pr=ct({props:{blueprint:String,next:Object,prev:Object,permissions:{type:Object,default:()=>({})},lock:{type:[Boolean,Object]},model:{type:Object,default:()=>({})},tab:{type:Object,default:()=>({columns:[]})},tabs:{type:Array,default:()=>[]}},computed:{id(){return this.model.link},isLocked(){var t;return"lock"===(null==(t=this.lock)?void 0:t.state)},protectedFields:()=>[]},watch:{"$panel.view.timestamp":{handler(){this.$store.dispatch("content/create",{id:this.id,api:this.id,content:this.model.content,ignore:this.protectedFields})},immediate:!0}},mounted(){this.$events.on("model.reload",this.$reload),this.$events.on("keydown.left",this.toPrev),this.$events.on("keydown.right",this.toNext)},destroyed(){this.$events.off("model.reload",this.$reload),this.$events.off("keydown.left",this.toPrev),this.$events.off("keydown.right",this.toNext)},methods:{toPrev(t){this.prev&&"body"===t.target.localName&&this.$go(this.prev.link)},toNext(t){this.next&&"body"===t.target.localName&&this.$go(this.next.link)}}},null,null).exports;const hr=ct({extends:pr,props:{preview:Object},computed:{focus(){const t=this.$store.getters["content/values"]().focus;if(!t)return;const[e,i]=t.replaceAll("%","").split(" ");return{x:parseFloat(e),y:parseFloat(i)}}},methods:{action(t){if("replace"===t)return this.$panel.upload.replace({...this.preview,...this.model})},setFocus(t){!0===this.$helper.object.isObject(t)&&(t=`${t.x}% ${t.y}%`),this.$store.dispatch("content/update",["focus",t])}}},(function(){var t=this,e=t._self._c;return e("k-panel-inside",{staticClass:"k-file-view",attrs:{"data-has-tabs":t.tabs.length>1,"data-id":t.model.id,"data-locked":t.isLocked,"data-template":t.blueprint},scopedSlots:t._u([{key:"topbar",fn:function(){return[e("k-prev-next",{attrs:{prev:t.prev,next:t.next}})]},proxy:!0}])},[e("k-header",{staticClass:"k-file-view-header",attrs:{editable:t.permissions.changeName&&!t.isLocked},on:{edit:function(e){return t.$dialog(t.id+"/changeName")}},scopedSlots:t._u([{key:"buttons",fn:function(){return[e("k-button-group",[e("k-button",{staticClass:"k-file-view-options",attrs:{link:t.preview.url,responsive:!0,title:t.$t("open"),icon:"open",size:"sm",target:"_blank",variant:"filled"}}),e("k-button",{staticClass:"k-file-view-options",attrs:{disabled:t.isLocked,dropdown:!0,title:t.$t("settings"),icon:"cog",size:"sm",variant:"filled"},on:{click:function(e){return t.$refs.settings.toggle()}}}),e("k-dropdown-content",{ref:"settings",attrs:{options:t.$dropdown(t.id),"align-x":"end"},on:{action:t.action}}),e("k-languages-dropdown")],1),e("k-form-buttons",{attrs:{lock:t.lock}})]},proxy:!0}])},[t._v(" "+t._s(t.model.filename)+" ")]),e("k-file-preview",t._b({attrs:{focus:t.focus},on:{focus:t.setFocus}},"k-file-preview",t.preview,!1)),e("k-model-tabs",{attrs:{tab:t.tab.name,tabs:t.tabs}}),e("k-sections",{attrs:{blueprint:t.blueprint,empty:t.$t("file.blueprint",{blueprint:t.$esc(t.blueprint)}),lock:t.lock,parent:t.id,tab:t.tab}})],1)}),[]).exports;const mr=ct({props:{focus:Object},emits:["set"],methods:{set(){this.$emit("set",{x:50,y:50})},reset(){this.$emit("set",void 0)}}},(function(){var t=this;return(0,t._self._c)("k-button",{attrs:{icon:t.focus?"cancel-small":"preview",title:t.focus?t.$t("file.focus.reset"):void 0,size:"xs",variant:"filled"},on:{click:function(e){t.focus?t.reset():t.set()}}},[t.focus?[t._v(t._s(t.focus.x)+"% "+t._s(t.focus.y)+"%")]:[t._v(t._s(t.$t("file.focus.placeholder")))]],2)}),[]).exports;const fr=ct({props:{details:{default:()=>[],type:Array},focus:{type:Object},focusable:Boolean,image:{default:()=>({}),type:Object},url:String},emits:["focus"],computed:{options(){return[{icon:"open",text:this.$t("open"),link:this.url,target:"_blank"},{icon:"cancel",text:this.$t("file.focus.reset"),click:()=>this.$refs.focus.reset(),when:this.focusable&&this.focus},{icon:"preview",text:this.$t("file.focus.placeholder"),click:()=>this.$refs.focus.set(),when:this.focusable&&!this.focus}]}},methods:{setFocus(t){if(!t)return this.$emit("focus",null);this.$emit("focus",{x:t.x.toFixed(1),y:t.y.toFixed(1)})}}},(function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-file-preview",attrs:{"data-has-focus":Boolean(t.focus)}},[e("div",{staticClass:"k-file-preview-thumb-column"},[e("div",{staticClass:"k-file-preview-thumb"},[t.image.src?[e("k-coords-input",{attrs:{disabled:!t.focusable,value:t.focus},on:{input:function(e){return t.setFocus(e)}}},[e("img",t._b({on:{dragstart:function(t){t.preventDefault()}}},"img",t.image,!1))]),e("k-button",{staticStyle:{color:"var(--color-gray-500)"},attrs:{icon:"dots",size:"xs"},on:{click:function(e){return t.$refs.dropdown.toggle()}}}),e("k-dropdown-content",{ref:"dropdown",attrs:{options:t.options,theme:"light"}})]:e("k-icon",{staticClass:"k-item-icon",attrs:{color:t.$helper.color(t.image.color),type:t.image.icon}})],2)]),e("div",{staticClass:"k-file-preview-details"},[e("dl",[t._l(t.details,(function(i){return e("div",{key:i.title},[e("dt",[t._v(t._s(i.title))]),e("dd",[i.link?e("k-link",{attrs:{to:i.link,tabindex:"-1",target:"_blank"}},[t._v(" /"+t._s(i.text)+" ")]):[t._v(" "+t._s(i.text)+" ")]],2)])})),t.image.src?e("div",{staticClass:"k-file-preview-focus-info"},[e("dt",[t._v(t._s(t.$t("file.focus.title")))]),e("dd",[t.focusable?e("k-file-focus-button",{ref:"focus",attrs:{focus:t.focus},on:{set:t.setFocus}}):t.focus?[t._v(" "+t._s(t.focus.x)+"% "+t._s(t.focus.y)+"% ")]:[t._v("–")]],2)]):t._e()],2)])])}),[]).exports,gr={install(t){t.component("k-file-view",hr),t.component("k-file-preview",fr),t.component("k-file-focus-button",mr)}};const kr=ct({props:{isInstallable:Boolean,isInstalled:Boolean,isOk:Boolean,requirements:Object,translations:Array},data(){return{user:{name:"",email:"",language:this.$panel.translation.code,password:"",role:"admin"}}},computed:{fields(){return{email:{label:this.$t("email"),type:"email",link:!1,autofocus:!0,required:!0},password:{label:this.$t("password"),type:"password",placeholder:this.$t("password")+" …",required:!0},language:{label:this.$t("language"),type:"select",options:this.translations,icon:"translate",empty:!1,required:!0}}},isReady(){return this.isOk&&this.isInstallable},isComplete(){return this.isOk&&this.isInstalled}},methods:{async install(){try{await this.$api.system.install(this.user),await this.$reload({globals:["$system","$translation"]}),this.$panel.notification.success({message:this.$t("welcome")+"!",icon:"smile"})}catch(t){this.$panel.error(t)}}}},(function(){var t=this,e=t._self._c;return e("k-panel-outside",{staticClass:"k-installation-view"},[e("div",{staticClass:"k-dialog k-installation-dialog"},[e("k-dialog-body",[t.isComplete?e("k-text",[e("k-headline",[t._v(t._s(t.$t("installation.completed")))]),e("k-link",{attrs:{to:"/login"}},[t._v(" "+t._s(t.$t("login"))+" ")])],1):t.isReady?e("form",{on:{submit:function(e){return e.preventDefault(),t.install.apply(null,arguments)}}},[e("h1",{staticClass:"sr-only"},[t._v(" "+t._s(t.$t("installation"))+" ")]),e("k-fieldset",{attrs:{fields:t.fields,novalidate:!0,value:t.user},on:{input:function(e){t.user=e}}}),e("k-button",{attrs:{text:t.$t("install"),icon:"check",size:"lg",theme:"positive",type:"submit",variant:"filled"}})],1):e("div",[e("k-headline",[t._v(" "+t._s(t.$t("installation.issues.headline"))+" ")]),e("ul",{staticClass:"k-installation-issues"},[!1===t.isInstallable?e("li",[e("k-icon",{attrs:{type:"alert"}}),e("span",{domProps:{innerHTML:t._s(t.$t("installation.disabled"))}})],1):t._e(),!1===t.requirements.php?e("li",[e("k-icon",{attrs:{type:"alert"}}),e("span",{domProps:{innerHTML:t._s(t.$t("installation.issues.php"))}})],1):t._e(),!1===t.requirements.server?e("li",[e("k-icon",{attrs:{type:"alert"}}),e("span",{domProps:{innerHTML:t._s(t.$t("installation.issues.server"))}})],1):t._e(),!1===t.requirements.mbstring?e("li",[e("k-icon",{attrs:{type:"alert"}}),e("span",{domProps:{innerHTML:t._s(t.$t("installation.issues.mbstring"))}})],1):t._e(),!1===t.requirements.curl?e("li",[e("k-icon",{attrs:{type:"alert"}}),e("span",{domProps:{innerHTML:t._s(t.$t("installation.issues.curl"))}})],1):t._e(),!1===t.requirements.accounts?e("li",[e("k-icon",{attrs:{type:"alert"}}),e("span",{domProps:{innerHTML:t._s(t.$t("installation.issues.accounts"))}})],1):t._e(),!1===t.requirements.content?e("li",[e("k-icon",{attrs:{type:"alert"}}),e("span",{domProps:{innerHTML:t._s(t.$t("installation.issues.content"))}})],1):t._e(),!1===t.requirements.media?e("li",[e("k-icon",{attrs:{type:"alert"}}),e("span",{domProps:{innerHTML:t._s(t.$t("installation.issues.media"))}})],1):t._e(),!1===t.requirements.sessions?e("li",[e("k-icon",{attrs:{type:"alert"}}),e("span",{domProps:{innerHTML:t._s(t.$t("installation.issues.sessions"))}})],1):t._e()]),e("k-button",{attrs:{text:t.$t("retry"),icon:"refresh",size:"lg",theme:"positive",variant:"filled"},on:{click:t.$reload}})],1)],1)],1)])}),[]).exports,br={install(t){t.component("k-installation-view",kr)}};const vr=ct({props:{languages:{type:Array,default:()=>[]},variables:{type:Boolean,default:!0}},computed:{languagesCollection(){return this.languages.map((t=>({...t,image:{back:"black",color:"gray",icon:"translate"},link:()=>{if(!1===this.variables)return null;this.$go(`languages/${t.id}`)},options:[{icon:"edit",text:this.$t("edit"),disabled:!1===this.variables,click:()=>this.$go(`languages/${t.id}`)},{icon:"cog",text:this.$t("settings"),disabled:!this.$panel.permissions.languages.update,click:()=>this.$dialog(`languages/${t.id}/update`)},{when:t.deletable,icon:"trash",text:this.$t("delete"),disabled:!this.$panel.permissions.languages.delete,click:()=>this.$dialog(`languages/${t.id}/delete`)}]})))},primaryLanguage(){return this.languagesCollection.filter((t=>t.default))},secondaryLanguages(){return this.languagesCollection.filter((t=>!1===t.default))}}},(function(){var t=this,e=t._self._c;return e("k-panel-inside",{staticClass:"k-languages-view"},[e("k-header",[t._v(" "+t._s(t.$t("view.languages"))+" "),e("k-button-group",{attrs:{slot:"buttons"},slot:"buttons"},[e("k-button",{attrs:{disabled:!t.$panel.permissions.languages.create,text:t.$t("language.create"),icon:"add",size:"sm",variant:"filled"},on:{click:function(e){return t.$dialog("languages/create")}}})],1)],1),t.languages.length>0?[e("k-section",{attrs:{headline:t.$t("languages.default")}},[e("k-collection",{attrs:{items:t.primaryLanguage}})],1),e("k-section",{attrs:{headline:t.$t("languages.secondary")}},[t.secondaryLanguages.length?e("k-collection",{attrs:{items:t.secondaryLanguages}}):e("k-empty",{attrs:{icon:"translate",disabled:!t.$panel.permissions.languages.create},on:{click:function(e){return t.$dialog("languages/create")}}},[t._v(" "+t._s(t.$t("languages.secondary.empty"))+" ")])],1)]:0===t.languages.length?[e("k-empty",{attrs:{icon:"translate",disabled:!t.$panel.permissions.languages.create},on:{click:function(e){return t.$dialog("languages/create")}}},[t._v(" "+t._s(t.$t("languages.empty"))+" ")])]:t._e()],2)}),[]).exports;const yr=ct({props:{code:String,deletable:Boolean,direction:String,id:String,info:Array,next:Object,name:String,prev:Object,translations:Array,url:String},computed:{canUpdate(){return this.$panel.permissions.languages.update}},methods:{createTranslation(){this.canUpdate&&this.$dialog(`languages/${this.id}/translations/create`)},option(t,e){this.canUpdate&&this.$dialog(`languages/${this.id}/translations/${window.btoa(encodeURIComponent(e.key))}/${t}`)},remove(){this.$dialog(`languages/${this.id}/delete`)},update(t){this.$dialog(`languages/${this.id}/update`,{on:{ready:()=>{this.$panel.dialog.focus(t)}}})},updateTranslation({row:t}){this.canUpdate&&this.$dialog(`languages/${this.id}/translations/${window.btoa(encodeURIComponent(t.key))}/update`)}}},(function(){var t=this,e=t._self._c;return e("k-panel-inside",{staticClass:"k-language-view",scopedSlots:t._u([{key:"topbar",fn:function(){return[e("k-prev-next",{attrs:{prev:t.prev,next:t.next}})]},proxy:!0}])},[e("k-header",{attrs:{editable:t.canUpdate},on:{edit:function(e){return t.update()}},scopedSlots:t._u([{key:"buttons",fn:function(){return[e("k-button-group",[e("k-button",{attrs:{link:t.url,title:t.$t("open"),icon:"open",size:"sm",target:"_blank",variant:"filled"}}),e("k-button",{attrs:{disabled:!t.canUpdate,title:t.$t("settings"),icon:"cog",size:"sm",variant:"filled"},on:{click:function(e){return t.update()}}}),t.deletable?e("k-button",{attrs:{disabled:!t.$panel.permissions.languages.delete,title:t.$t("delete"),icon:"trash",size:"sm",variant:"filled"},on:{click:function(e){return t.remove()}}}):t._e()],1)]},proxy:!0}])},[t._v(" "+t._s(t.name)+" ")]),e("k-section",{attrs:{headline:t.$t("language.settings")}},[e("k-stats",{attrs:{reports:t.info,size:"small"}})],1),e("k-section",{attrs:{buttons:[{click:t.createTranslation,disabled:!t.canUpdate,icon:"add",text:t.$t("add")}],headline:t.$t("language.variables")}},[t.translations.length?[e("k-table",{attrs:{columns:{key:{label:t.$t("language.variable.key"),mobile:!0,width:"1/4"},value:{label:t.$t("language.variable.value"),mobile:!0}},disabled:!t.canUpdate,rows:t.translations},on:{cell:t.updateTranslation,option:t.option}})]:[e("k-empty",{attrs:{disabled:!t.canUpdate,icon:"translate"},on:{click:t.createTranslation}},[t._v(" "+t._s(t.$t("language.variables.empty"))+" ")])]],2)],1)}),[]).exports,$r={install(t){t.component("k-languages-view",vr),t.component("k-language-view",yr)}};const wr=ct({emits:["click"]},(function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-notification k-login-alert",attrs:{"data-theme":"error"}},[e("p",[t._t("default")],2),e("k-button",{attrs:{icon:"cancel"},on:{click:function(e){return t.$emit("click")}}})],1)}),[]).exports,xr={props:{methods:{type:Array,default:()=>[]},pending:{type:Object,default:()=>({challenge:"email"})},value:String}};const _r=ct({mixins:[xr],emits:["error"],data(){return{code:this.value??"",isLoading:!1}},computed:{mode(){return this.methods.includes("password-reset")?"password-reset":"login"},submitText(){const t=this.isLoading?" …":"";return"password-reset"===this.mode?this.$t("login.reset")+t:this.$t("login")+t}},methods:{async login(){this.$emit("error",null),this.isLoading=!0;try{await this.$api.auth.verifyCode(this.code),this.$panel.notification.success({message:this.$t("welcome")+"!",icon:"smile"}),"password-reset"===this.mode?this.$go("reset-password"):this.$reload()}catch(t){this.$emit("error",t)}finally{this.isLoading=!1}}}},(function(){var t=this,e=t._self._c;return e("form",{staticClass:"k-login-form k-login-code-form",on:{submit:function(e){return e.preventDefault(),t.login.apply(null,arguments)}}},[t.pending.email?e("k-user-info",{attrs:{user:t.pending.email}}):t._e(),e("k-text-field",{attrs:{autofocus:!0,counter:!1,help:t.$t("login.code.text."+t.pending.challenge),label:t.$t("login.code.label."+t.mode),novalidate:!0,placeholder:t.$t("login.code.placeholder."+t.pending.challenge),required:!0,value:t.code,autocomplete:"one-time-code",icon:"unlock",name:"code"},on:{input:function(e){t.code=e}}}),e("footer",{staticClass:"k-login-buttons"},[e("k-button",{staticClass:"k-login-button k-login-back-button",attrs:{text:t.$t("back"),icon:"angle-left",link:"/logout",size:"lg",variant:"filled"}}),e("k-button",{staticClass:"k-login-button",attrs:{text:t.submitText,icon:"check",size:"lg",type:"submit",theme:"positive",variant:"filled"}})],1)],1)}),[]).exports,Cr={props:{methods:{type:Array,default:()=>[]},value:{type:Object,default:()=>({})}}};const Sr=ct({mixins:[Cr],emits:["error"],data(){return{mode:null,isLoading:!1,user:{email:"",password:"",remember:!1,...this.value}}},computed:{alternateMode(){return"email-password"===this.form?"email":"email-password"},canToggle(){return null!==this.codeMode&&(!1!==this.methods.includes("password")&&(!0===this.methods.includes("password-reset")||!0===this.methods.includes("code")))},codeMode(){return!0===this.methods.includes("password-reset")?"password-reset":!0===this.methods.includes("code")?"code":null},fields(){const t={email:{autofocus:!0,label:this.$t("email"),type:"email",required:!0,link:!1}};return"email-password"===this.form&&(t.password={label:this.$t("password"),type:"password",minLength:8,required:!0,autocomplete:"current-password",counter:!1}),t},form(){return this.mode?this.mode:"password"===this.methods[0]?"email-password":"email"},isResetForm(){return"password-reset"===this.codeMode&&"email"===this.form},submitText(){const t=this.isLoading?" …":"";return this.isResetForm?this.$t("login.reset")+t:this.$t("login")+t},toggleText(){return this.$t("login.toggleText."+this.codeMode+"."+this.alternateMode)}},methods:{async login(){this.$emit("error",null),this.isLoading=!0;const t={...this.user};"email"===this.mode&&(t.password=null),!0===this.isResetForm&&(t.remember=!1);try{await this.$api.auth.login(t),this.$reload({globals:["$system","$translation"]}),this.$panel.notification.success({message:this.$t("welcome")+"!",icon:"smile"})}catch(e){this.$emit("error",e)}finally{this.isLoading=!1}},toggle(){this.mode=this.alternateMode,this.$refs.fieldset.focus("email")}}},(function(){var t=this,e=t._self._c;return e("form",{staticClass:"k-login-form",on:{submit:function(e){return e.preventDefault(),t.login.apply(null,arguments)}}},[e("div",{staticClass:"k-login-fields"},[!0===t.canToggle?e("button",{staticClass:"k-login-toggler",attrs:{type:"button"},on:{click:t.toggle}},[t._v(" "+t._s(t.toggleText)+" ")]):t._e(),e("k-fieldset",{ref:"fieldset",attrs:{novalidate:!0,fields:t.fields,value:t.user},on:{input:function(e){t.user=e}}})],1),e("footer",{staticClass:"k-login-buttons"},[!1===t.isResetForm?e("k-checkbox-input",{attrs:{label:t.$t("login.remember"),checked:t.user.remember,value:t.user.remember},on:{input:function(e){t.user.remember=e}}}):t._e(),e("k-button",{staticClass:"k-login-button",attrs:{icon:"check",size:"lg",theme:"positive",type:"submit",variant:"filled"}},[t._v(" "+t._s(t.submitText)+" ")])],1)])}),[]).exports;const Or=ct({components:{"k-login-plugin-form":window.panel.plugins.login},mixins:[xr,Cr],props:{value:{type:Object,default:()=>({code:"",email:"",password:""})}},data:()=>({issue:""}),computed:{component:()=>window.panel.plugins.login?"k-login-plugin-form":"k-login-form",form(){return this.pending.email?"code":"login"}},mounted(){this.$store.dispatch("content/clear")},methods:{async onError(t){null!==t?(!0===t.details.challengeDestroyed&&await this.$reload({globals:["$system"]}),this.issue=t.message):this.issue=null}}},(function(){var t=this,e=t._self._c;return e("k-panel-outside",{class:"code"===t.form?"k-login-code-view":"k-login-view"},[e("div",{staticClass:"k-dialog k-login k-login-dialog"},[e("h1",{staticClass:"sr-only"},[t._v(" "+t._s(t.$t("login"))+" ")]),t.issue?e("k-login-alert",{nativeOn:{click:function(e){t.issue=null}}},[t._v(" "+t._s(t.issue)+" ")]):t._e(),e("k-dialog-body",["code"===t.form?e("k-login-code-form",t._b({on:{error:t.onError}},"k-login-code-form",{methods:t.methods,pending:t.pending,value:t.value.code},!1)):e(t.component,t._b({tag:"component",on:{error:t.onError}},"component",{methods:t.methods,value:t.value},!1))],1)],1)])}),[]).exports,Mr={install(t){t.component("k-login-alert",wr),t.component("k-login-code-form",_r),t.component("k-login-form",Sr),t.component("k-login-view",Or),t.component("k-login",Sr),t.component("k-login-code",_r)}};const Ar=ct({extends:pr,props:{status:Object},computed:{protectedFields:()=>["title"],statusBtn(){return{...this.$helper.page.status.call(this,this.model.status,!this.permissions.changeStatus||this.isLocked),responsive:!0,size:"sm",text:this.status.label,variant:"filled"}}}},(function(){var t=this,e=t._self._c;return e("k-panel-inside",{staticClass:"k-page-view",attrs:{"data-has-tabs":t.tabs.length>1,"data-id":t.model.id,"data-locked":t.isLocked,"data-template":t.blueprint},scopedSlots:t._u([{key:"topbar",fn:function(){return[t.model.id?e("k-prev-next",{attrs:{prev:t.prev,next:t.next}}):t._e()]},proxy:!0}])},[e("k-header",{staticClass:"k-page-view-header",attrs:{editable:t.permissions.changeTitle&&!t.isLocked},on:{edit:function(e){return t.$dialog(t.id+"/changeTitle")}},scopedSlots:t._u([{key:"buttons",fn:function(){return[e("k-button-group",[t.permissions.preview&&t.model.previewUrl?e("k-button",{staticClass:"k-page-view-preview",attrs:{link:t.model.previewUrl,title:t.$t("open"),icon:"open",target:"_blank",variant:"filled",size:"sm"}}):t._e(),e("k-button",{staticClass:"k-page-view-options",attrs:{disabled:!0===t.isLocked,dropdown:!0,title:t.$t("settings"),icon:"cog",variant:"filled",size:"sm"},on:{click:function(e){return t.$refs.settings.toggle()}}}),e("k-dropdown-content",{ref:"settings",attrs:{options:t.$dropdown(t.id),"align-x":"end"}}),e("k-languages-dropdown"),t.status?e("k-button",t._b({staticClass:"k-page-view-status",on:{click:function(e){return t.$dialog(t.id+"/changeStatus")}}},"k-button",t.statusBtn,!1)):t._e()],1),e("k-form-buttons",{attrs:{lock:t.lock}})]},proxy:!0}])},[t._v(" "+t._s(t.model.title)+" ")]),e("k-model-tabs",{attrs:{tab:t.tab.name,tabs:t.tabs}}),e("k-sections",{attrs:{blueprint:t.blueprint,empty:t.$t("page.blueprint",{blueprint:t.$esc(t.blueprint)}),lock:t.lock,parent:t.id,tab:t.tab}})],1)}),[]).exports;const Ir=ct({extends:pr,emits:["submit"],computed:{protectedFields:()=>["title"]}},(function(){var t=this,e=t._self._c;return e("k-panel-inside",{staticClass:"k-site-view",attrs:{"data-has-tabs":t.tabs.length>1,"data-locked":t.isLocked,"data-id":"/","data-template":"site"}},[e("k-header",{staticClass:"k-site-view-header",attrs:{editable:t.permissions.changeTitle&&!t.isLocked},on:{edit:function(e){return t.$dialog("site/changeTitle")}},scopedSlots:t._u([{key:"buttons",fn:function(){return[e("k-button-group",[e("k-button",{staticClass:"k-site-view-preview",attrs:{link:t.model.previewUrl,title:t.$t("open"),icon:"open",target:"_blank",variant:"filled",size:"sm"}}),e("k-languages-dropdown")],1),e("k-form-buttons",{attrs:{lock:t.lock}})]},proxy:!0}])},[t._v(" "+t._s(t.model.title)+" ")]),e("k-model-tabs",{attrs:{tab:t.tab.name,tabs:t.tabs}}),e("k-sections",{attrs:{blueprint:t.blueprint,empty:t.$t("site.blueprint"),lock:t.lock,tab:t.tab,parent:"site"},on:{submit:function(e){return t.$emit("submit",e)}}})],1)}),[]).exports,Dr={install(t){t.component("k-page-view",Ar),t.component("k-site-view",Ir)}};const jr=ct({extends:pr,props:{canChangeEmail:Boolean,canChangeLanguage:Boolean,canChangeName:Boolean,canChangeRole:Boolean}},(function(){var t=this,e=t._self._c;return e("k-panel-inside",{staticClass:"k-user-view",attrs:{"data-has-tabs":t.tabs.length>1,"data-id":t.model.id,"data-locked":t.isLocked,"data-template":t.blueprint},scopedSlots:t._u([{key:"topbar",fn:function(){return[e("k-prev-next",{attrs:{prev:t.prev,next:t.next}})]},proxy:!0}])},[e("k-header",{staticClass:"k-user-view-header",attrs:{editable:t.canChangeName},on:{edit:function(e){return t.$dialog(t.id+"/changeName")}},scopedSlots:t._u([{key:"buttons",fn:function(){return[e("k-button-group",[e("k-button",{staticClass:"k-user-view-options",attrs:{disabled:t.isLocked,dropdown:!0,title:t.$t("settings"),icon:"cog",size:"sm",variant:"filled"},on:{click:function(e){return t.$refs.settings.toggle()}}}),e("k-dropdown-content",{ref:"settings",attrs:{"align-x":"end",options:t.$dropdown(t.id)}}),e("k-languages-dropdown")],1),e("k-form-buttons",{attrs:{lock:t.lock}})]},proxy:!0}])},[t.model.name&&0!==t.model.name.length?[t._v(" "+t._s(t.model.name)+" ")]:e("span",{staticClass:"k-user-name-placeholder"},[t._v(" "+t._s(t.$t("name"))+" … ")])],2),e("k-user-profile",{attrs:{"can-change-email":t.canChangeEmail,"can-change-language":t.canChangeLanguage,"can-change-name":t.canChangeName,"can-change-role":t.canChangeRole,"is-locked":t.isLocked,model:t.model,permissions:t.permissions}}),e("k-model-tabs",{attrs:{tab:t.tab.name,tabs:t.tabs}}),e("k-sections",{attrs:{blueprint:t.blueprint,empty:t.$t("user.blueprint",{blueprint:t.$esc(t.blueprint)}),lock:t.lock,parent:t.id,tab:t.tab}})],1)}),[]).exports;const Er=ct({extends:jr,prevnext:!1},null,null).exports;const Lr=ct({data:()=>({isLoading:!1,values:{password:null,passwordConfirmation:null}}),computed:{fields(){return{password:{autofocus:!0,label:this.$t("user.changePassword.new"),icon:"key",type:"password",width:"1/2"},passwordConfirmation:{label:this.$t("user.changePassword.new.confirm"),icon:"key",type:"password",width:"1/2"}}}},mounted(){this.$panel.title=this.$t("view.resetPassword")},methods:{async submit(){if(!this.values.password||this.values.password.length<8)return this.$panel.notification.error(this.$t("error.user.password.invalid"));if(this.values.password!==this.values.passwordConfirmation)return this.$panel.notification.error(this.$t("error.user.password.notSame"));this.isLoading=!0;try{await this.$api.users.changePassword(this.$panel.user.id,this.values.password),this.$panel.notification.success(),this.$go("/")}catch(t){this.$panel.notification.error(t)}finally{this.isLoading=!1}}}},(function(){var t=this,e=t._self._c;return e("k-panel-inside",{staticClass:"k-password-reset-view"},[e("form",{on:{submit:function(e){return e.preventDefault(),t.submit.apply(null,arguments)}}},[e("k-header",{scopedSlots:t._u([{key:"buttons",fn:function(){return[e("k-button",{attrs:{icon:"check",theme:"notice",type:"submit",variant:"filled",size:"sm"}},[t._v(" "+t._s(t.$t("change"))+" "),t.isLoading?[t._v(" … ")]:t._e()],2)]},proxy:!0}])},[t._v(" "+t._s(t.$t("view.resetPassword"))+" ")]),e("k-user-info",{attrs:{user:t.$panel.user}}),e("k-fieldset",{attrs:{fields:t.fields,value:t.values}})],1)])}),[]).exports;const Tr=ct({props:{isLocked:Boolean,model:Object},methods:{open(){this.model.avatar?this.$refs.dropdown.toggle():this.upload()},async remove(){await this.$api.users.deleteAvatar(this.model.id),this.$panel.notification.success(),this.$reload()},upload(){this.$panel.upload.pick({url:this.$panel.urls.api+"/"+this.model.link+"/avatar",accept:"image/*",immediate:!0,multiple:!1})}}},(function(){var t=this,e=t._self._c;return e("k-button",{staticClass:"k-user-view-image",attrs:{disabled:t.isLocked,title:t.$t("avatar")},on:{click:t.open}},[t.model.avatar?[e("k-image-frame",{attrs:{cover:!0,src:t.model.avatar}}),e("k-dropdown-content",{ref:"dropdown",attrs:{options:[{icon:"upload",text:t.$t("change"),click:t.upload},{icon:"trash",text:t.$t("delete"),click:t.remove}]}})]:e("k-icon-frame",{attrs:{icon:"user"}})],2)}),[]).exports;const Br=ct({props:{user:[Object,String]}},(function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-user-info"},[t.user.avatar?e("k-image-frame",{attrs:{cover:!0,src:t.user.avatar.url,ratio:"1/1"}}):e("k-icon-frame",{attrs:{color:"white",back:"black",icon:"user"}}),t._v(" "+t._s(t.user.name??t.user.email??t.user)+" ")],1)}),[]).exports;const qr=ct({props:{canChangeEmail:Boolean,canChangeLanguage:Boolean,canChangeRole:Boolean,isLocked:Boolean,model:Object,permissions:Object}},(function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-user-profile"},[e("k-user-avatar",{attrs:{disabled:t.isLocked,model:t.model}}),e("k-button-group",{attrs:{buttons:[{icon:"email",text:`${t.model.email}`,title:`${t.$t("email")}: ${t.model.email}`,disabled:!t.canChangeEmail,click:()=>t.$dialog(t.model.link+"/changeEmail")},{icon:"bolt",text:`${t.model.role}`,title:`${t.$t("role")}: ${t.model.role}`,disabled:!t.canChangeRole,click:()=>t.$dialog(t.model.link+"/changeRole")},{icon:"translate",text:`${t.model.language}`,title:`${t.$t("language")}: ${t.model.language}`,disabled:!t.canChangeLanguage,click:()=>t.$dialog(t.model.link+"/changeLanguage")}]}})],1)}),[]).exports;const Pr=ct({props:{canCreate:Boolean,role:Object,roles:Array,search:String,title:String,users:Object},computed:{empty(){return{icon:"users",text:this.$t("role.empty")}},items(){return this.users.data.map((t=>(t.options=this.$dropdown(t.link),t)))},tabs(){const t=[{name:"all",label:this.$t("role.all"),link:"/users"}];for(const e of this.roles)t.push({name:e.id,label:e.title,link:"/users?role="+e.id});return t}},methods:{create(){var t;this.$dialog("users/create",{query:{role:null==(t=this.role)?void 0:t.id}})},paginate(t){this.$reload({query:{page:t.page}})}}},(function(){var t,e=this,i=e._self._c;return i("k-panel-inside",{staticClass:"k-users-view"},[i("k-header",{staticClass:"k-users-view-header",scopedSlots:e._u([{key:"buttons",fn:function(){return[i("k-button",{attrs:{disabled:!e.canCreate,text:e.$t("user.create"),icon:"add",size:"sm",variant:"filled"},on:{click:e.create}})]},proxy:!0}])},[e._v(" "+e._s(e.$t("view.users"))+" ")]),i("k-tabs",{attrs:{tab:(null==(t=e.role)?void 0:t.id)??"all",tabs:e.tabs}}),i("k-collection",{attrs:{empty:e.empty,items:e.items,pagination:e.users.pagination},on:{paginate:e.paginate}})],1)}),[]).exports,Nr={install(t){t.component("k-account-view",Er),t.component("k-reset-password-view",Lr),t.component("k-user-avatar",Tr),t.component("k-user-info",Br),t.component("k-user-profile",qr),t.component("k-user-view",jr),t.component("k-users-view",Pr)}};const zr=ct({components:{Plugins:ct({props:{plugins:Array}},(function(){var t=this,e=t._self._c;return t.plugins.length?e("k-section",{attrs:{headline:t.$t("plugins"),link:"https://getkirby.com/plugins"}},[e("k-table",{attrs:{index:!1,columns:{name:{label:t.$t("name"),type:"url",mobile:!0},author:{label:t.$t("author")},license:{label:t.$t("license")},version:{label:t.$t("version"),type:"update-status",mobile:!0,width:"10rem"}},rows:t.plugins}})],1):t._e()}),[]).exports,Security:ct({props:{exceptions:Array,security:Array,urls:Object},data(){return{issues:structuredClone(this.security)}},async mounted(){console.info("Running system health checks for the Panel system view; failed requests in the following console output are expected behavior.");const t=(Promise.allSettled??Promise.all).bind(Promise),e=Object.entries(this.urls).map(this.check);await t([...e,this.testPatchRequests()]),console.info(`System health checks ended. ${this.issues.length-this.security.length} issues with accessible files/folders found (see the security list in the system view).`)},methods:{async check([t,e]){if(!e)return;const{status:i}=await fetch(e,{cache:"no-store"});i<400&&this.issues.push({id:t,text:this.$t("system.issues."+t),link:"https://getkirby.com/security/"+t,icon:"folder"})},retry(){this.$go(window.location.href)},async testPatchRequests(){const{status:t}=await this.$api.patch("system/method-test");"ok"!==t&&this.issues.push({id:"method-overwrite-text",text:"Your server does not support PATCH requests",link:"https://getkirby.com/docs/reference/system/options/api#methods-overwrite",icon:"protected"})}}},(function(){var t=this,e=t._self._c;return t.issues.length?e("k-section",{attrs:{headline:t.$t("security"),buttons:[{title:t.$t("retry"),icon:"refresh",click:t.retry}]}},[e("k-items",{attrs:{items:t.issues.map((t=>({theme:"negative",image:{back:"var(--theme-color-200)",icon:t.icon??"alert",color:"var(--theme-color-icon)"},target:"_blank",...t})))}})],1):t._e()}),[]).exports},props:{environment:Array,exceptions:Array,info:Object,plugins:Array,security:Array,urls:Object},mounted(){this.exceptions.length>0&&(console.info("The following errors occurred during the update check of Kirby and/or plugins:"),this.exceptions.map((t=>console.warn(t))),console.info("End of errors from the update check."))},methods:{copy(){const t=JSON.stringify({info:this.info,security:this.security.map((t=>t.text)),plugins:this.plugins.map((t=>({name:t.name.text,version:t.version.currentVersion})))},null,2);this.$helper.clipboard.write(t),this.$panel.notification.success({message:this.$t("system.info.copied")})}}},(function(){var t=this,e=t._self._c;return e("k-panel-inside",{staticClass:"k-system-view"},[e("k-header",[t._v(" "+t._s(t.$t("view.system"))+" ")]),e("k-section",{attrs:{headline:t.$t("environment"),buttons:[{text:t.$t("system.info.copy"),icon:"copy",responsive:!0,click:t.copy}]}},[e("k-stats",{staticClass:"k-system-info",attrs:{reports:t.environment,size:"medium"}})],1),e("security",{attrs:{security:t.security,urls:t.urls}}),e("plugins",{attrs:{plugins:t.plugins}})],1)}),[]).exports;const Fr=ct({props:{value:[String,Object]}},(function(){var t=this,e=t._self._c;return e("div",{staticClass:"k-table-update-status-cell"},["string"==typeof t.value?e("span",{staticClass:"k-table-update-status-cell-version"},[t._v(" "+t._s(t.value)+" ")]):[e("k-button",{staticClass:"k-table-update-status-cell-button",attrs:{dropdown:!0,icon:t.value.icon,href:t.value.url,text:t.value.currentVersion,theme:t.value.theme,size:"xs",variant:"filled"},on:{click:function(e){return e.stopPropagation(),t.$refs.dropdown.toggle()}}}),e("k-dropdown-content",{ref:"dropdown",attrs:{"align-x":"end"}},[e("dl",{staticClass:"k-plugin-info"},[e("dt",[t._v(t._s(t.$t("plugin")))]),e("dd",[t._v(t._s(t.value.pluginName))]),e("dt",[t._v(t._s(t.$t("version.current")))]),e("dd",[t._v(t._s(t.value.currentVersion))]),e("dt",[t._v(t._s(t.$t("version.latest")))]),e("dd",[t._v(t._s(t.value.latestVersion))]),e("dt",[t._v(t._s(t.$t("system.updateStatus")))]),e("dd",{attrs:{"data-theme":t.value.theme}},[t._v(t._s(t.value.label))])]),t.value.url?[e("hr"),e("k-button",{attrs:{icon:"open",link:t.value.url}},[t._v(" "+t._s(t.$t("versionInformation"))+" ")])]:t._e()],2)]],2)}),[]).exports,Yr={install(t){t.component("k-system-view",zr),t.component("k-table-update-status-cell",Fr)}};const Rr=ct({props:{id:String},mounted(){window.panel.deprecated(" will be removed in a future version.")}},(function(){var t=this._self._c;return t("k-panel-inside",[t("k-"+this.id+"-plugin-view",{tag:"component"})],1)}),[]).exports,Ur={install(t){t.component("k-error-view",ur),t.component("k-search-view",dr),t.use(gr),t.use(br),t.use($r),t.use(Mr),t.use(Dr),t.use(Yr),t.use(Nr),t.component("k-plugin-view",Rr)}},Hr={install(t){t.use(kt),t.use(ne),t.use(xe),t.use(Be),t.use(Eo),t.use(qo),t.use(aa),t.use(ka),t.use(Ba),t.use(Va),t.use(Xa),t.use(ir),t.use(cr),t.use(Ur),t.use(L)}},Vr={install(t){window.onunhandledrejection=t=>{t.preventDefault(),window.panel.error(t.reason)},t.config.errorHandler=window.panel.error.bind(window.panel)}},Kr=(t={})=>{var e=t.desc?-1:1,i=-e,s=/^0/,n=/\s+/g,o=/^\s+|\s+$/g,a=/[^\x00-\x80]/,r=/^0x[0-9a-f]+$/i,l=/(0x[\da-fA-F]+|(^[\+\-]?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?(?=\D|\s|$))|\d+)/g,c=/(^([\w ]+,?[\w ]+)?[\w ]+,?[\w ]+\d+:\d+(:\d+)?[\w ]?|^\d{1,4}[\/\-]\d{1,4}[\/\-]\d{1,4}|^\w+, \w+ \d+, \d{4})/,u=t.insensitive?function(t){return function(t){if(t.toLocaleLowerCase)return t.toLocaleLowerCase();return t.toLowerCase()}(""+t).replace(o,"")}:function(t){return(""+t).replace(o,"")};function d(t){return t.replace(l,"\0$1\0").replace(/\0$/,"").replace(/^\0/,"").split("\0")}function p(t,e){return(!t.match(s)||1===e)&&parseFloat(t)||t.replace(n," ").replace(o,"")||0}return function(t,s){var n=u(t),o=u(s);if(!n&&!o)return 0;if(!n&&o)return i;if(n&&!o)return e;var l=d(n),h=d(o),m=parseInt(n.match(r),16)||1!==l.length&&Date.parse(n),f=parseInt(o.match(r),16)||m&&o.match(c)&&Date.parse(o)||null;if(f){if(mf)return e}for(var g=l.length,k=h.length,b=0,v=Math.max(g,k);b0)return e;if(w<0)return i;if(b===v-1)return 0}else{if(y<$)return i;if(y>$)return e}}return 0}};function Wr(t){return Array.isArray(t)?t:Object.values(t??{})}RegExp.escape=function(t){return t.replace(new RegExp("[-/\\\\^$*+?.()[\\]{}]","gu"),"\\$&")};function Jr(t,e){return t.reduce(((t,i)=>(i===e?t.push([]):t[t.length-1].push(i),t)),[[]])}function Gr(t){return Array.isArray(t)?t:[t]}Array.fromObject=Wr,Object.defineProperty(Array.prototype,"sortBy",{value:function(t){return t(this,t)},enumerable:!1,writable:!0,configurable:!0}),Object.defineProperty(Array.prototype,"split",{value:function(t){return Jr(this,t)},enumerable:!1,writable:!0,configurable:!0}),Array.wrap=Gr;const Xr={fromObject:Wr,search:(t,e,i={})=>{if((e??"").length<=(i.min??0))return t;const s=new RegExp(RegExp.escape(e),"ig"),n=i.field??"text",o=t.filter((t=>!!t[n]&&null!==t[n].match(s)));return i.limit?o.slice(0,i.limit):o},sortBy:function(t,e){const i=e.split(" "),s=i[0],n=i[1]??"asc",o=Kr({desc:"desc"===n,insensitive:!0});return t.sort(((t,e)=>{const i=String(t[s]??""),n=String(e[s]??"");return o(i,n)}))},split:Jr,wrap:Gr};const Zr={read:function(t,e=!1){if(!t)return null;if("string"==typeof t)return t;if(t instanceof ClipboardEvent){if(t.preventDefault(),!0===e)return t.clipboardData.getData("text/plain");const i=t.clipboardData.getData("text/html")||t.clipboardData.getData("text/plain")||null;if(i)return i.replace(/\u00a0/g," ")}return null},write:function(t,e){if("string"!=typeof t&&(t=JSON.stringify(t,null,2)),e&&e instanceof ClipboardEvent)return e.preventDefault(),e.clipboardData.setData("text/plain",t),!0;const i=document.createElement("textarea");if(i.value=t,document.body.append(i),navigator.userAgent.match(/ipad|ipod|iphone/i)){i.contentEditable=!0,i.readOnly=!0;const t=document.createRange();t.selectNodeContents(i);const e=window.getSelection();e.removeAllRanges(),e.addRange(t),i.setSelectionRange(0,999999)}else i.select();return document.execCommand("copy"),i.remove(),!0}};function Qr(t){if("string"==typeof t){if("pattern"===(t=t.toLowerCase()))return"var(--pattern)";if(!1===t.startsWith("#")&&!1===t.startsWith("var(")){const e="--color-"+t;if(window.getComputedStyle(document.documentElement).getPropertyValue(e))return`var(${e})`}return t}}function tl(t,e=!1){if(!t.match("youtu"))return!1;let i=null;try{i=new URL(t)}catch{return!1}const s=i.pathname.split("/").filter((t=>""!==t)),n=s[0],o=s[1],a="https://"+(!0===e?"www.youtube-nocookie.com":i.host)+"/embed",r=t=>!!t&&null!==t.match(/^[a-zA-Z0-9_-]+$/);let l=i.searchParams,c=null;switch(s.join("/")){case"embed/videoseries":case"playlist":r(l.get("list"))&&(c=a+"/videoseries");break;case"watch":r(l.get("v"))&&(c=a+"/"+l.get("v"),l.has("t")&&l.set("start",l.get("t")),l.delete("v"),l.delete("t"));break;default:i.host.includes("youtu.be")&&r(n)?(c=!0===e?"https://www.youtube-nocookie.com/embed/"+n:"https://www.youtube.com/embed/"+n,l.has("t")&&l.set("start",l.get("t")),l.delete("t")):["embed","shorts"].includes(n)&&r(o)&&(c=a+"/"+o)}if(!c)return!1;const u=l.toString();return u.length&&(c+="?"+u),c}function el(t,e=!1){let i=null;try{i=new URL(t)}catch{return!1}const s=i.pathname.split("/").filter((t=>""!==t));let n=i.searchParams,o=null;switch(!0===e&&n.append("dnt",1),i.host){case"vimeo.com":case"www.vimeo.com":o=s[0];break;case"player.vimeo.com":o=s[1]}if(!o||!o.match(/^[0-9]*$/))return!1;let a="https://player.vimeo.com/video/"+o;const r=n.toString();return r.length&&(a+="?"+r),a}const il={youtube:tl,vimeo:el,video:function(t,e=!1){return!0===t.includes("youtu")?tl(t,e):!0===t.includes("vimeo")&&el(t,e)}};function sl(t){var e;if(void 0!==t.default)return structuredClone(t.default);const i=window.panel.app.$options.components[`k-${t.type}-field`],s=null==(e=null==i?void 0:i.options.props)?void 0:e.value;if(void 0===s)return;const n=null==s?void 0:s.default;return"function"==typeof n?n():void 0!==n?n:null}const nl={defaultValue:sl,form:function(t){const e={};for(const i in t){const s=sl(t[i]);void 0!==s&&(e[i]=s)}return e},isVisible:function(t,e){if("hidden"===t.type||!0===t.hidden)return!1;if(!t.when)return!0;for(const i in t.when){const s=e[i.toLowerCase()],n=t.when[i];if((void 0!==s||!(""===n||Array.isArray(n)&&0===n.length))&&s!==n)return!1}return!0},subfields:function(t,e){let i={};for(const s in e){const n=e[s];n.section=t.name,t.endpoints&&(n.endpoints={field:t.endpoints.field+"+"+s,section:t.endpoints.section,model:t.endpoints.model}),i[s]=n}return i}},ol=t=>t.split(".").slice(-1).join(""),al=t=>t.split(".").slice(0,-1).join("."),rl=t=>Intl.NumberFormat("en",{notation:"compact",style:"unit",unit:"byte",unitDisplay:"narrow"}).format(t),ll={extension:ol,name:al,niceSize:rl};function cl(t,e){if("string"==typeof t&&(t=document.querySelector(t)),!t)return!1;if(!e&&t.contains(document.activeElement)&&t!==document.activeElement)return!1;const i=[":where([autofocus], [data-autofocus])",":where(input, textarea, select, [contenteditable=true], .input-focus)","[type=submit]","button"];e&&i.unshift(`[name="${e}"]`);const s=function(t,e){for(const i of e){const e=t.querySelector(i);if(!0===ul(e))return e}return null}(t,i);return s?(s.focus(),s):!0===ul(t)&&(t.focus(),t)}function ul(t){return!!t&&(!t.matches("[disabled], [aria-disabled], input[type=hidden]")&&(!t.closest("[aria-disabled]")&&!t.closest("[disabled]")&&"function"==typeof t.focus))}const dl=t=>"function"==typeof window.Vue.options.components[t],pl=t=>!!t.dataTransfer&&(!!t.dataTransfer.types&&(!0===t.dataTransfer.types.includes("Files")&&!1===t.dataTransfer.types.includes("text/plain")));const hl={metaKey:function(){return window.navigator.userAgent.indexOf("Mac")>-1?"cmd":"ctrl"}};function ml(t){return!0===t.startsWith("file://")||!0===t.startsWith("/@/file/")}function fl(t){return"site://"===t||!0===t.startsWith("page://")||null!==t.match(/^\/(.*\/)?@\/page\//)}function gl(t=[]){const e={url:{detect:t=>/^(http|https):\/\//.test(t),icon:"url",id:"url",label:window.panel.$t("url"),link:t=>t,placeholder:window.panel.$t("url.placeholder"),input:"url",value:t=>t},page:{detect:t=>!0===fl(t),icon:"page",id:"page",label:window.panel.$t("page"),link:t=>t,placeholder:window.panel.$t("select")+" …",input:"text",value:t=>t},file:{detect:t=>!0===ml(t),icon:"file",id:"file",label:window.panel.$t("file"),link:t=>t,placeholder:window.panel.$t("select")+" …",value:t=>t},email:{detect:t=>t.startsWith("mailto:"),icon:"email",id:"email",label:window.panel.$t("email"),link:t=>t.replace(/^mailto:/,""),placeholder:window.panel.$t("email.placeholder"),input:"email",value:t=>"mailto:"+t},tel:{detect:t=>t.startsWith("tel:"),icon:"phone",id:"tel",label:window.panel.$t("tel"),link:t=>t.replace(/^tel:/,""),pattern:"[+]{0,1}[0-9]+",placeholder:window.panel.$t("tel.placeholder"),input:"tel",value:t=>"tel:"+t},anchor:{detect:t=>t.startsWith("#"),icon:"anchor",id:"anchor",label:"Anchor",link:t=>t,pattern:"^#.+",placeholder:"#element",input:"text",value:t=>t},custom:{detect:()=>!0,icon:"title",id:"custom",label:window.panel.$t("custom"),link:t=>t,input:"text",value:t=>t}};if(!t.length)return e;const i={};for(const s of t)e[s]&&(i[s]=e[s]);return i}const kl={detect:function(t,e){if(t=t??"",e=e??gl(),0===t.length)return{type:Object.keys(e)[0]??"url",link:""};for(const i in e)if(!0===e[i].detect(t))return{type:i,link:e[i].link(t)}},getFileUUID:function(t){return t.replace("/@/file/","file://")},getPageUUID:function(t){return t.replace(/^\/(.*\/)?@\/page\//,"page://")},isFileUUID:ml,isPageUUID:fl,preview:async function({type:t,link:e},i){return"page"===t&&e?await async function(t,e=["title","panelImage"]){if("site://"===t)return{label:window.panel.$t("view.site")};try{const i=await window.panel.api.pages.get(t,{select:e.join(",")});return{label:i.title,image:i.panelImage}}catch{return null}}(e,i):"file"===t&&e?await async function(t,e=["filename","panelImage"]){try{const i=await window.panel.api.files.get(null,t,{select:e.join(",")});return{label:i.filename,image:i.panelImage}}catch{return null}}(e,i):e?{label:e}:null},types:gl};const bl={status:function(t,e=!1){const i={class:"k-status-icon",icon:"status-"+t,title:window.panel.$t("page.status")+": "+window.panel.$t("page.status."+t),disabled:e,size:"xs",style:"--icon-size: 15px"};return e&&(i.title+=` (${window.panel.$t("disabled")})`),i.theme="draft"===t?"negative-icon":"unlisted"===t?"info-icon":"positive-icon",i}},vl=(t="3/2",e="100%",i=!0)=>{const s=String(t).split("/");if(2!==s.length)return e;const n=Number(s[0]),o=Number(s[1]);let a=100;return 0!==n&&0!==o&&(a=i?a/n*o:a/o*n,a=parseFloat(String(a)).toFixed(2)),a+"%"},yl={"&":"&","<":"<",">":">",'"':""","'":"'","/":"/","`":"`","=":"="};function $l(t){return String(t).replace(/[&<>"'`=/]/g,(t=>yl[t]))}function wl(t){return!t||0===String(t).length}function xl(t){const e=String(t);return e.charAt(0).toLowerCase()+e.slice(1)}function _l(t="",e=""){const i=new RegExp(`^(${RegExp.escape(e)})+`,"g");return t.replace(i,"")}function Cl(t="",e=""){const i=new RegExp(`(${RegExp.escape(e)})+$`,"g");return t.replace(i,"")}function Sl(t,e={}){const i=(t,e={})=>{const s=e[$l(t.shift())]??"…";return"…"===s||0===t.length?s:i(t,s)},s="[{]{1,2}[\\s]?",n="[\\s]?[}]{1,2}";return(t=t.replace(new RegExp(`${s}(.*?)${n}`,"gi"),((t,s)=>i(s.split("."),e)))).replace(new RegExp(`${s}.*${n}`,"gi"),"…")}function Ol(t){const e=String(t);return e.charAt(0).toUpperCase()+e.slice(1)}function Ml(){let t,e,i="";for(t=0;t<32;t++)e=16*Math.random()|0,8!=t&&12!=t&&16!=t&&20!=t||(i+="-"),i+=(12==t?4:16==t?3&e|8:e).toString(16);return i}const Al={camelToKebab:function(t){return t.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase()},escapeHTML:$l,hasEmoji:function(t){if("string"!=typeof t)return!1;if(!0===/^[a-z0-9_-]+$/.test(t))return!1;const e=t.match(/(?:[\u2700-\u27bf]|(?:\ud83c[\udde6-\uddff]){2}|[\ud800-\udbff][\udc00-\udfff]|[\u0023-\u0039]\ufe0f?\u20e3|\u3299|\u3297|\u303d|\u3030|\u24c2|\ud83c[\udd70-\udd71]|\ud83c[\udd7e-\udd7f]|\ud83c\udd8e|\ud83c[\udd91-\udd9a]|\ud83c[\udde6-\uddff]|[\ud83c\ude01-\ude02]|\ud83c\ude1a|\ud83c\ude2f|[\ud83c\ude32-\ude3a]|[\ud83c\ude50-\ude51]|\u203c|\u2049|[\u25aa-\u25ab]|\u25b6|\u25c0|[\u25fb-\u25fe]|\u00a9|\u00ae|\u2122|\u2139|\ud83c\udc04|[\u2600-\u26FF]|\u2b05|\u2b06|\u2b07|\u2b1b|\u2b1c|\u2b50|\u2b55|\u231a|\u231b|\u2328|\u23cf|[\u23e9-\u23f3]|[\u23f8-\u23fa]|\ud83c\udccf|\u2934|\u2935|[\u2190-\u21ff])/i);return null!==e&&null!==e.length},isEmpty:wl,lcfirst:xl,ltrim:_l,pad:function(t,e=2){t=String(t);let i="";for(;i.length]+)>)/gi,"")},template:Sl,ucfirst:Ol,ucwords:function(t){return String(t).split(/ /g).map((t=>Ol(t))).join(" ")},unescapeHTML:function(t){for(const e in yl)t=String(t).replaceAll(yl[e],e);return t},uuid:Ml},Il=async(t,e)=>new Promise(((i,s)=>{const n={url:"/",field:"file",method:"POST",filename:t.name,headers:{},attributes:{},complete:()=>{},error:()=>{},success:()=>{},progress:()=>{}},o=Object.assign(n,e),a=new XMLHttpRequest,r=new FormData;r.append(o.field,t,o.filename);for(const t in o.attributes){const e=o.attributes[t];null!=e&&r.append(t,e)}const l=e=>{if(e.lengthComputable&&o.progress){const i=Math.max(0,Math.min(100,Math.ceil(e.loaded/e.total*100)));o.progress(a,t,i)}};a.upload.addEventListener("loadstart",l),a.upload.addEventListener("progress",l),a.addEventListener("load",(e=>{let n=null;try{n=JSON.parse(e.target.response)}catch{n={status:"error",message:"The file could not be uploaded"}}"error"===n.status?(o.error(a,t,n),s(n)):(o.progress(a,t,100),o.success(a,t,n),i(n))})),a.addEventListener("error",(e=>{const i=JSON.parse(e.target.response);o.progress(a,t,100),o.error(a,t,i),s(i)})),a.open(o.method,o.url,!0);for(const t in o.headers)a.setRequestHeader(t,o.headers[t]);a.send(r)}));function Dl(){var t;return new URL((null==(t=document.querySelector("base"))?void 0:t.href)??window.location.origin)}function jl(t={},e={}){e instanceof URL&&(e=e.search);const i=new URLSearchParams(e);for(const[s,n]of Object.entries(t))null!==n&&i.set(s,n);return i}function El(t="",e={},i){return(t=Pl(t,i)).search=jl(e,t.search),t}function Ll(t){return null!==String(t).match(/^https?:\/\//)}function Tl(t){return Pl(t).origin===window.location.origin}function Bl(t,e){if((t instanceof URL||t instanceof Location)&&(t=t.toString()),"string"!=typeof t)return!1;try{new URL(t,window.location)}catch{return!1}if(!0===e){return/^(?:(?:https?|ftp):\/\/)(?:\S+(?::\S*)?@)?(?:(?!10(?:\.\d{1,3}){3})(?!169\.254(?:\.\d{1,3}){2})(?!192\.168(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:localhost)|(?:(?:[a-z\u00a1-\uffff0-9]+-?)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]+-?)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})))(?::\d{2,5})?(?:\/[^\s]*)?$/i.test(t)}return!0}function ql(t,e){return!0===Ll(t)?t:(e=e??Dl(),(e=String(e).replaceAll(/\/$/g,""))+"/"+(t=String(t).replaceAll(/^\//g,"")))}function Pl(t,e){return t instanceof URL?t:new URL(ql(t,e))}const Nl={base:Dl,buildQuery:jl,buildUrl:El,isAbsolute:Ll,isSameOrigin:Tl,isUrl:Bl,makeAbsolute:ql,toObject:Pl},zl={install(t){t.prototype.$helper={array:Xr,clipboard:Zr,clone:wt.clone,color:Qr,embed:il,focus:cl,isComponent:dl,isUploadEvent:pl,debounce:Nt,field:nl,file:ll,keyboard:hl,link:kl,object:wt,page:bl,pad:Al.pad,ratio:vl,slug:Al.slug,sort:Kr,string:Al,upload:Il,url:Nl,uuid:Al.uuid},t.prototype.$esc=Al.escapeHTML}},Fl={install(t){const e=(t,e,i)=>{!0!==i.context.disabled?t.dir=window.panel.language.direction:t.dir=null};t.directive("direction",{bind:e,update:e})}},Yl={install(t){window.panel.redirect=window.panel.redirect.bind(window.panel),window.panel.reload=window.panel.reload.bind(window.panel),window.panel.request=window.panel.request.bind(window.panel),window.panel.search=window.panel.search.bind(window.panel);const e=["api","config","direction","events","language","languages","license","menu","multilang","permissions","search","searches","system","t","translation","url","urls","user","view"];for(const i of e){const e=`$${i}`;t.prototype[e]=window.panel[e]=window.panel[i]}window.panel.$vue=window.panel.app}},Rl=/^#?([\da-f]{3}){1,2}$/i,Ul=/^#?([\da-f]{4}){1,2}$/i,Hl=/^rgba?\(\s*(\d{1,3})(%?)(?:,|\s)+(\d{1,3})(%?)(?:,|\s)+(\d{1,3})(%?)(?:,|\s|\/)*(\d*(?:\.\d+)?)(%?)\s*\)?$/i,Vl=/^hsla?\(\s*(\d{1,3}\.?\d*)(deg|rad|grad|turn)?(?:,|\s)+(\d{1,3})%(?:,|\s)+(\d{1,3})%(?:,|\s|\/)*(\d*(?:\.\d+)?)(%?)\s*\)?$/i;function Kl(t){return"string"==typeof t&&(Rl.test(t)||Ul.test(t))}function Wl(t){return vt(t)&&"r"in t&&"g"in t&&"b"in t}function Jl(t){return vt(t)&&"h"in t&&"s"in t&&"l"in t}function Gl({h:t,s:e,v:i,a:s}){if(0===i)return{h:t,s:0,l:0,a:s};if(0===e&&1===i)return{h:t,s:1,l:1,a:s};const n=i*(2-e)/2;return{h:t,s:e=i*e/(1-Math.abs(2*n-1)),l:n,a:s}}function Xl({h:t,s:e,l:i,a:s}){const n=e*(i<.5?i:1-i);return{h:t,s:e=0===n?0:2*n/(i+n),v:i+n,a:s}}function Zl(t){if(!0===Rl.test(t)||!0===Ul.test(t)){"#"===t[0]&&(t=t.slice(1)),3===t.length&&(t=t.split("").reduce(((t,e)=>t+e+e),""));const e=parseInt(t,16);return!0===Rl.test(t)?{r:e>>16,g:e>>8&255,b:255&e,a:1}:{r:e>>24&255,g:e>>16&255,b:e>>8&255,a:Math.round((255&e)/255*100)/100}}throw new Error(`unknown hex color: ${t}`)}function Ql({r:t,g:e,b:i,a:s=1}){let n="#"+(1<<24|t<<16|e<<8|i).toString(16).slice(1);return s<1&&(n+=(256|Math.round(255*s)).toString(16).slice(1)),n}function tc({h:t,s:e,l:i,a:s}){const n=e*Math.min(i,1-i),o=(e,s=(e+t/30)%12)=>i-n*Math.max(Math.min(s-3,9-s,1),-1);return{r:255*o(0),g:255*o(8),b:255*o(4),a:s}}function ec({r:t,g:e,b:i,a:s}){t/=255,e/=255,i/=255;const n=Math.max(t,e,i),o=n-Math.min(t,e,i),a=1-Math.abs(n+n-o-1);let r=o&&(n==t?(e-i)/o:n==e?2+(i-t)/o:4+(t-e)/o);return r=60*(r<0?r+6:r),{h:r,s:a?o/a:0,l:(n+n-o)/2,a:s}}function ic(t){return Ql(tc(t))}function sc(t){return ec(Zl(t))}function nc(t,e){return t=Number(t),"grad"===e?t*=.9:"rad"===e?t*=180/Math.PI:"turn"===e&&(t*=360),parseInt(t%360)}function oc(t,e){if(!0===Kl(t))switch("#"!==t[0]&&(t="#"+t),e){case"hex":return t;case"rgb":return Zl(t);case"hsl":return sc(t);case"hsv":return Xl(sc(t))}if(!0===Wl(t))switch(e){case"hex":return Ql(t);case"rgb":return t;case"hsl":return ec(t);case"hsv":return function({r:t,g:e,b:i,a:s}){t/=255,e/=255,i/=255;const n=Math.max(t,e,i),o=n-Math.min(t,e,i);let a=o&&(n==t?(e-i)/o:n==e?2+(i-t)/o:4+(t-e)/o);return a=60*(a<0?a+6:a),{h:a,s:n&&o/n,v:n,a:s}}(t)}if(!0===Jl(t))switch(e){case"hex":return ic(t);case"rgb":return tc(t);case"hsl":return t;case"hsv":return Xl(t)}if(!0===function(t){return vt(t)&&"h"in t&&"s"in t&&"v"in t}(t))switch(e){case"hex":return ic(Gl(t));case"rgb":return function({h:t,s:e,v:i,a:s}){const n=(s,n=(s+t/60)%6)=>i-i*e*Math.max(Math.min(n,4-n,1),0);return{r:255*n(5),g:255*n(3),b:255*n(1),a:s}}(t);case"hsl":return Gl(t);case"hsv":return t}throw new Error(`Invalid color conversion: ${JSON.stringify(t)} -> ${e}`)}function ac(t){let e;if(!t||"string"!=typeof t)return!1;if(!0===Kl(t))return"#"!==t[0]&&(t="#"+t),t;if(e=t.match(Hl)){const t={r:Number(e[1]),g:Number(e[3]),b:Number(e[5]),a:Number(e[7]||1)};return"%"===e[2]&&(t.r=Math.ceil(2.55*t.r)),"%"===e[4]&&(t.g=Math.ceil(2.55*t.g)),"%"===e[6]&&(t.b=Math.ceil(2.55*t.b)),"%"===e[8]&&(t.a=t.a/100),t}if(e=t.match(Vl)){let[t,i,s,n,o]=e.slice(1);const a={h:nc(t,i),s:Number(s)/100,l:Number(n)/100,a:Number(o||1)};return"%"===e[6]&&(a.a=a.a/100),a}return null}const rc={convert:oc,parse:ac,parseAs:function(t,e){const i=ac(t);return i&&e?oc(i,e):i},toString:function(t,e,i=!0){var s,n;let o=t;if("string"==typeof o&&(o=ac(t)),o&&e&&(o=oc(o,e)),!0===Kl(o))return!0!==i&&(5===o.length?o=o.slice(0,4):o.length>7&&(o=o.slice(0,7))),o.toLowerCase();if(!0===Wl(o)){const t=o.r.toFixed(),e=o.g.toFixed(),n=o.b.toFixed(),a=null==(s=o.a)?void 0:s.toFixed(2);return i&&a&&a<1?`rgb(${t} ${e} ${n} / ${a})`:`rgb(${t} ${e} ${n})`}if(!0===Jl(o)){const t=o.h.toFixed(),e=(100*o.s).toFixed(),s=(100*o.l).toFixed(),a=null==(n=o.a)?void 0:n.toFixed(2);return i&&a&&a<1?`hsl(${t} ${e}% ${s}% / ${a})`:`hsl(${t} ${e}% ${s}%)`}throw new Error(`Unsupported color: ${JSON.stringify(t)}`)}};T.extend(B),T.extend(((t,e,i)=>{i.interpret=(t,e="date")=>{const s={date:{"YYYY-MM-DD":!0,"YYYY-MM-D":!0,"YYYY-MM-":!0,"YYYY-MM":!0,"YYYY-M-DD":!0,"YYYY-M-D":!0,"YYYY-M-":!0,"YYYY-M":!0,"YYYY-":!0,YYYYMMDD:!0,"MMM DD YYYY":!1,"MMM D YYYY":!1,"MMM DD YY":!1,"MMM D YY":!1,"MMM YYYY":!0,"MMM DD":!1,"MMM D":!1,"MM YYYY":!0,"M YYYY":!0,"MMMM DD YYYY":!0,"MMMM D YYYY":!0,"MMMM DD YY":!0,"MMMM D YY":!0,"MMMM DD, YYYY":!0,"MMMM D, YYYY":!0,"MMMM DD, YY":!0,"MMMM D, YY":!0,"MMMM DD. YYYY":!0,"MMMM D. YYYY":!0,"MMMM DD. YY":!0,"MMMM D. YY":!0,DDMMYYYY:!0,DDMMYY:!0,"DD MMMM YYYY":!1,"DD MMMM YY":!1,"DD MMMM":!1,"D MMMM YYYY":!1,"D MMMM YY":!1,"D MMMM":!1,"DD MMM YYYY":!1,"D MMM YYYY":!1,"DD MMM YY":!1,"D MMM YY":!1,"DD MMM":!1,"D MMM":!1,"DD MM YYYY":!1,"DD M YYYY":!1,"D MM YYYY":!1,"D M YYYY":!1,"DD MM YY":!1,"D MM YY":!1,"DD M YY":!1,"D M YY":!1,YYYY:!0,MMMM:!0,MMM:!0,"DD MM":!1,"DD M":!1,"D MM":!1,"D M":!1,DD:!1,D:!1},time:{"HHmmss a":!1,"HHmm a":!1,"HH a":!1,HHmmss:!1,HHmm:!1,"HH:mm:ss a":!1,"HH:mm:ss":!1,"HH:mm a":!1,"HH:mm":!1,HH:!1}};if("string"==typeof t&&""!==t)for(const n in s[e]){const o=i(t,n,s[e][n]);if(!0===o.isValid())return o}return null}})),T.extend(((t,e,i)=>{const s=t=>"date"===t?"YYYY-MM-DD":"time"===t?"HH:mm:ss":"YYYY-MM-DD HH:mm:ss";e.prototype.toISO=function(t="datetime"){return this.format(s(t))},i.iso=function(t,e){e&&(e=s(e)),e??(e=[s("datetime"),s("date"),s("time")]);const n=i(t,e);return n&&n.isValid()?n:null}})),T.extend(((t,e)=>{e.prototype.merge=function(t,e="date"){let i=this.clone();if(!t||!t.isValid())return this;if("string"==typeof e){const t={date:["year","month","date"],time:["hour","minute","second"]};if(!1===Object.hasOwn(t,e))throw new Error("Invalid merge unit alias");e=t[e]}for(const s of e)i=i.set(s,t.get(s));return i}})),T.extend(((t,e,i)=>{i.pattern=t=>new class{constructor(t,e){this.dayjs=t,this.pattern=e;const i={year:["YY","YYYY"],month:["M","MM","MMM","MMMM"],day:["D","DD"],hour:["h","hh","H","HH"],minute:["m","mm"],second:["s","ss"],meridiem:["a"]};this.parts=this.pattern.split(/\W/).map(((t,e)=>{const s=this.pattern.indexOf(t);return{index:e,unit:Object.keys(i)[Object.values(i).findIndex((e=>e.includes(t)))],start:s,end:s+(t.length-1)}}))}at(t,e=t){const i=this.parts.filter((i=>i.start<=t&&i.end>=e-1));return i[0]?i[0]:this.parts.filter((e=>e.start<=t)).pop()}format(t){return t&&t.isValid()?t.format(this.pattern):null}}(i,t)})),T.extend(((t,e)=>{e.prototype.round=function(t="date",e=1){const i=["second","minute","hour","date","month","year"];if("day"===t&&(t="date"),!1===i.includes(t))throw new Error("Invalid rounding unit");if(["date","month","year"].includes(t)&&1!==e||"hour"===t&&24%e!=0||["second","minute"].includes(t)&&60%e!=0)throw"Invalid rounding size for "+t;let s=this.clone();const n=i.indexOf(t),o=i.slice(0,n),a=o.pop();for(const r of o)s=s.startOf(r);if(a){const e={month:12,date:s.daysInMonth(),hour:24,minute:60,second:60}[a];Math.round(s.get(a)/e)*e===e&&(s=s.add(1,"date"===t?"day":t)),s=s.startOf(t)}return s=s.set(t,Math.round(s.get(t)/e)*e),s}})),T.extend(((t,e,i)=>{e.prototype.validate=function(t,e,s="day"){if(!this.isValid())return!1;if(!t)return!0;t=i.iso(t);const n={min:"isAfter",max:"isBefore"}[e];return this.isSame(t,s)||this[n](t,s)}}));const lc={install(t){t.prototype.$library={autosize:q,colors:rc,dayjs:T}}},cc=()=>({close(){sessionStorage.setItem("kirby$activation$card","true"),this.isOpen=!1},isOpen:"true"!==sessionStorage.getItem("kirby$activation$card"),open(){sessionStorage.removeItem("kirby$activation$card"),this.isOpen=!0}}),uc=t=>({async changeName(e,i,s){return t.patch(this.url(e,i,"name"),{name:s})},async delete(e,i){return t.delete(this.url(e,i))},async get(e,i,s){let n=await t.get(this.url(e,i),s);return!0===Array.isArray(n.content)&&(n.content={}),n},id:t=>!0===t.startsWith("/@/file/")?t.replace("/@/file/","@"):!0===t.startsWith("file://")?t.replace("file://","@"):t,link(t,e,i){return"/"+this.url(t,e,i)},async update(e,i,s){return t.patch(this.url(e,i),s)},url(t,e,i){let s="files/"+this.id(e);return t&&(s=t+"/"+s),i&&(s+="/"+i),s}}),dc=t=>({async blueprint(e){return t.get("pages/"+this.id(e)+"/blueprint")},async blueprints(e,i){return t.get("pages/"+this.id(e)+"/blueprints",{section:i})},async changeSlug(e,i){return t.patch("pages/"+this.id(e)+"/slug",{slug:i})},async changeStatus(e,i,s){return t.patch("pages/"+this.id(e)+"/status",{status:i,position:s})},async changeTemplate(e,i){return t.patch("pages/"+this.id(e)+"/template",{template:i})},async changeTitle(e,i){return t.patch("pages/"+this.id(e)+"/title",{title:i})},async children(e,i){return t.post("pages/"+this.id(e)+"/children/search",i)},async create(e,i){return null===e||"/"===e?t.post("site/children",i):t.post("pages/"+this.id(e)+"/children",i)},async delete(e,i){return t.delete("pages/"+this.id(e),i)},async duplicate(e,i,s){return t.post("pages/"+this.id(e)+"/duplicate",{slug:i,children:s.children??!1,files:s.files??!1})},async get(e,i){let s=await t.get("pages/"+this.id(e),i);return!0===Array.isArray(s.content)&&(s.content={}),s},id:t=>!0===t.match(/^\/(.*\/)?@\/page\//)?t.replace(/^\/(.*\/)?@\/page\//,"@"):!0===t.startsWith("page://")?t.replace("page://","@"):t.replace(/\//g,"+"),async files(e,i){return t.post("pages/"+this.id(e)+"/files/search",i)},link(t){return"/"+this.url(t)},async preview(t){return(await this.get(this.id(t),{select:"previewUrl"})).previewUrl},async search(e,i){return e?t.post("pages/"+this.id(e)+"/children/search?select=id,title,hasChildren",i):t.post("site/children/search?select=id,title,hasChildren",i)},async update(e,i){return t.patch("pages/"+this.id(e),i)},url(t,e){let i=null===t?"pages":"pages/"+String(t).replace(/\//g,"+");return e&&(i+="/"+e),i}});class pc extends Error{constructor(t,{request:e,response:i,cause:s}){super(i.json.message??t,{cause:s}),this.request=e,this.response=i}state(){return this.response.json}}class hc extends pc{}class mc extends pc{state(){return{message:this.message,text:this.response.text}}}const fc=t=>(window.location.href=ql(t),!1),gc=async(t,e={})=>{var i;(e={cache:"no-store",credentials:"same-origin",mode:"same-origin",...e}).body=((i=e.body)instanceof HTMLFormElement&&(i=new FormData(i)),i instanceof FormData&&(i=Object.fromEntries(i)),"object"==typeof i?JSON.stringify(i):i),e.headers=((t={},e={})=>{return{"content-type":"application/json","x-csrf":e.csrf??!1,"x-fiber":!0,"x-fiber-globals":(i=e.globals,!!i&&(!1===Array.isArray(i)?String(i):i.join(","))),"x-fiber-referrer":e.referrer??!1,...$t(t)};var i})(e.headers,e),e.url=El(t,e.query);const s=new Request(e.url,e);return!1===Tl(s.url)?fc(s.url):await kc(s,await fetch(s))},kc=async(t,e)=>{var i;if(!1===e.headers.get("Content-Type").includes("application/json"))return fc(e.url);try{e.text=await e.text(),e.json=JSON.parse(e.text)}catch(s){throw new mc("Invalid JSON response",{cause:s,request:t,response:e})}if(401===e.status)throw new hc("Unauthenticated",{request:t,response:e});if("error"===(null==(i=e.json)?void 0:i.status))throw e.json;if(!1===e.ok)throw new pc(`The request to ${e.url} failed`,{request:t,response:e});return{request:t,response:e}},bc=t=>({blueprint:async e=>t.get("users/"+e+"/blueprint"),blueprints:async(e,i)=>t.get("users/"+e+"/blueprints",{section:i}),changeEmail:async(e,i)=>t.patch("users/"+e+"/email",{email:i}),changeLanguage:async(e,i)=>t.patch("users/"+e+"/language",{language:i}),changeName:async(e,i)=>t.patch("users/"+e+"/name",{name:i}),changePassword:async(e,i)=>t.patch("users/"+e+"/password",{password:i}),changeRole:async(e,i)=>t.patch("users/"+e+"/role",{role:i}),create:async e=>t.post("users",e),delete:async e=>t.delete("users/"+e),deleteAvatar:async e=>t.delete("users/"+e+"/avatar"),link(t,e){return"/"+this.url(t,e)},async list(e){return t.post(this.url(null,"search"),e)},get:async(e,i)=>t.get("users/"+e,i),async roles(e){return(await t.get(this.url(e,"roles"))).data.map((t=>({info:t.description??`(${window.panel.$t("role.description.placeholder")})`,text:t.title,value:t.name})))},search:async e=>t.post("users/search",e),update:async(e,i)=>t.patch("users/"+e,i),url(t,e){let i=t?"users/"+t:"users";return e&&(i+="/"+e),i}}),vc=t=>{var e;const i={csrf:t.system.csrf,endpoint:Cl(t.urls.api,"/"),methodOverwrite:(null==(e=t.config.api)?void 0:e.methodOverwrite)??!1,ping:null,requests:[],running:0},s=()=>{clearInterval(i.ping),i.ping=setInterval(i.auth.ping,3e5)};return i.request=async(e,n={},o=!1)=>{const a=e+"/"+JSON.stringify(n);i.requests.push(a),!1===o&&(t.isLoading=!0),i.language=t.language.code;try{return await(t=>async(e,i={})=>{i={cache:"no-store",credentials:"same-origin",mode:"same-origin",headers:{"content-type":"application/json","x-csrf":t.csrf,"x-language":t.language,...$t(i.headers??{})},...i},t.methodOverwrite&&"GET"!==i.method&&"POST"!==i.method&&(i.headers["x-http-method-override"]=i.method,i.method="POST"),i.url=Cl(t.endpoint,"/")+"/"+_l(e,"/");const s=new Request(i.url,i),{response:n}=await kc(s,await fetch(s));let o=n.json;return o.data&&"model"===o.type&&(o=o.data),o})(i)(e,n)}finally{s(),i.requests=i.requests.filter((t=>t!==a)),0===i.requests.length&&(t.isLoading=!1)}},i.auth=(t=>({async login(e){const i={long:e.remember??!1,email:e.email,password:e.password};return t.post("auth/login",i)},logout:async()=>t.post("auth/logout"),ping:async()=>t.post("auth/ping"),user:async e=>t.get("auth",e),verifyCode:async e=>t.post("auth/code",{code:e})}))(i),i.delete=(t=>async(e,i,s,n=!1)=>t.post(e,i,s,"DELETE",n))(i),i.files=uc(i),i.get=(t=>async(e,i,s,n=!1)=>(i&&(e+="?"+Object.keys(i).filter((t=>void 0!==i[t]&&null!==i[t])).map((t=>t+"="+i[t])).join("&")),t.request(e,Object.assign(s??{},{method:"GET"}),n)))(i),i.languages=(t=>({create:async e=>t.post("languages",e),delete:async e=>t.delete("languages/"+e),get:async e=>t.get("languages/"+e),list:async()=>t.get("languages"),update:async(e,i)=>t.patch("languages/"+e,i)}))(i),i.pages=dc(i),i.patch=(t=>async(e,i,s,n=!1)=>t.post(e,i,s,"PATCH",n))(i),i.post=(t=>async(e,i,s,n="POST",o=!1)=>t.request(e,Object.assign(s??{},{method:n,body:JSON.stringify(i)}),o))(i),i.roles=(t=>({list:async e=>t.get("roles",e),get:async e=>t.get("roles/"+e)}))(i),i.system=(t=>({get:async(e={view:"panel"})=>t.get("system",e),install:async e=>(await t.post("system/install",e)).user,register:async e=>t.post("system/register",e)}))(i),i.site=(t=>({blueprint:async()=>t.get("site/blueprint"),blueprints:async()=>t.get("site/blueprints"),changeTitle:async e=>t.patch("site/title",{title:e}),children:async e=>t.post("site/children/search",e),get:async(e={view:"panel"})=>t.get("site",e),update:async e=>t.post("site",e)}))(i),i.translations=(t=>({list:async()=>t.get("translations"),get:async e=>t.get("translations/"+e)}))(i),i.users=bc(i),s(),i},yc=()=>({addEventListener(t,e){"function"==typeof e&&(this.on[t]=e)},addEventListeners(t){if(!1!==vt(t))for(const e in t)this.addEventListener(e,t[e])},emit(t,...e){return this.hasEventListener(t)?this.on[t](...e):()=>{}},hasEventListener(t){return"function"==typeof this.on[t]},listeners(){return this.on},on:{}}),$c=(t,e={})=>({...e,key:()=>t,defaults:()=>e,reset(){return this.set(this.defaults())},set(t){this.validateState(t);for(const e in this.defaults())this[e]=t[e]??this.defaults()[e];return this.state()},state(){const t={};for(const e in this.defaults())t[e]=this[e]??this.defaults()[e];return t},validateState(t){if(!1===vt(t))throw new Error(`Invalid ${this.key()} state`);return!0}}),wc=(t,e,i)=>{const s=$c(e,i);return{...s,...yc(),async load(e,i={}){return!0!==i.silent&&(this.isLoading=!0),await t.open(e,i),this.isLoading=!1,this.addEventListeners(i.on),this.state()},async open(t,e={}){return"function"==typeof e&&(e={on:{submit:e}}),!0===Bl(t)?this.load(t,e):(this.set(t),this.addEventListeners(e.on),this.emit("open",t,e),this.state())},async post(e,i={}){var s;if(!this.path)throw new Error(`The ${this.key()} cannot be posted`);this.isLoading=!0,e=e??(null==(s=this.props)?void 0:s.value)??{};try{return await t.post(this.path,e,i)}catch(n){t.error(n)}finally{this.isLoading=!1}return!1},async refresh(e={}){e.url=e.url??this.url();const i=(await t.get(e.url,e))["$"+this.key()];if(i&&i.component===this.component)return this.props=i.props,this.state()},async reload(t={}){if(!this.path)return!1;this.open(this.url(),t)},set(t){return s.set.call(this,t),this.on={},this.addEventListeners(t.on??{}),this.state()},url(){return t.url(this.path,this.query)}}},xc=(t,e,i)=>{const s=wc(t,e,i);return{...s,async cancel(){this.isOpen&&this.emit("cancel"),this.close()},async close(){!1!==this.isOpen&&(this.isOpen=!1,this.emit("close"),this.reset(),0===t.overlays().length&&(document.documentElement.removeAttribute("data-overlay"),document.documentElement.style.removeProperty("--scroll-top")))},focus(t){cl(`.k-${this.key()}-portal`,t)},input(t){!1!==this.isOpen&&(Vue.set(this.props,"value",t),this.emit("input",t))},isOpen:!1,listeners(){return{...this.on,cancel:this.cancel.bind(this),close:this.close.bind(this),input:this.input.bind(this),submit:this.submit.bind(this),success:this.success.bind(this)}},async open(e,i){return!1===this.isOpen&&t.notification.close(),await s.open.call(this,e,i),this.component&&(document.documentElement.setAttribute("data-overlay","true"),document.documentElement.style.setProperty("--scroll-top",window.scrollY+"px"),this.isOpen=!0),this.state()},async submit(t,e={}){if(t=t??this.props.value,this.hasEventListener("submit"))return this.emit("submit",t,e);if(!this.path)return this.close();const i=await this.post(t,e);return!1===vt(i)?i:this.success(i["$"+this.key()]??{})},success(e){return this.hasEventListener("success")?this.emit("success",e):("string"==typeof e&&t.notification.success(e),this.close(),this.successNotification(e),this.successEvents(e),this.successDispatch(e),e.route||e.redirect?this.successRedirect(e):t.view.reload(e.reload),e)},successDispatch(e){if(!1!==vt(e.dispatch))for(const i in e.dispatch){const s=e.dispatch[i];t.app.$store.dispatch(i,!0===Array.isArray(s)?[...s]:s)}},successEvents(e){if(e.event){const i=Array.wrap(e.event);for(const s of i)"string"==typeof s&&t.events.emit(s,e)}!1!==e.emit&&t.events.emit("success",e)},successNotification(e){e.message&&t.notification.success(e.message)},successRedirect(e){const i=e.route??e.redirect;return!!i&&("string"==typeof i?t.open(i):t.open(i.url,i.options))},get value(){var t;return null==(t=this.props)?void 0:t.value}}},_c=t=>{t.events.on("dialog.save",(e=>{var i;null==(i=null==e?void 0:e.preventDefault)||i.call(e),t.dialog.submit()}));const e=xc(t,"dialog",{component:null,isLoading:!1,on:{},path:null,props:{},query:{},referrer:null,timestamp:null,legacy:!1,ref:null});return{...e,async close(){this.ref&&(this.ref.visible=!1),e.close.call(this)},async open(t,i={}){return t instanceof window.Vue?this.openComponent(t):("string"==typeof t&&(t=`/dialogs/${t}`),e.open.call(this,t,i))},async openComponent(i){t.deprecated("Dialog components should no longer be used in your templates");const s=await e.open.call(this,{component:i.$options._componentTag,legacy:!0,props:{...i.$attrs,...i.$props},ref:i}),n=this.listeners();for(const t in n)i.$on(t,n[t]);return i.visible=!0,s}}},Cc=()=>({...$c("drag",{type:null,data:{}}),get isDragging(){return null!==this.type},start(t,e){this.type=t,this.data=e},stop(){this.type=null,this.data={}}}),Sc=()=>({add(t){if(!t.id)throw new Error("The state needs an ID");!0!==this.has(t.id)&&this.milestones.push(t)},at(t){return this.milestones.at(t)},clear(){this.milestones=[]},get(t=null){return null===t?this.milestones:this.milestones.find((e=>e.id===t))},goto(t){const e=this.index(t);if(-1!==e)return this.milestones=this.milestones.slice(0,e+1),this.milestones[e]},has(t){return void 0!==this.get(t)},index(t){return this.milestones.findIndex((e=>e.id===t))},isEmpty(){return 0===this.milestones.length},last(){return this.milestones.at(-1)},milestones:[],remove(t=null){return null===t?this.removeLast():this.milestones=this.milestones.filter((e=>e.id!==t))},removeLast(){return this.milestones=this.milestones.slice(0,-1)},replace(t,e){-1===t&&(t=this.milestones.length-1),Vue.set(this.milestones,t,e)}}),Oc=t=>{const e=xc(t,"drawer",{component:null,isLoading:!1,on:{},path:null,props:{},query:{},referrer:null,timestamp:null,id:null});return t.events.on("drawer.save",(e=>{e.preventDefault(),t.drawer.submit()})),{...e,get breadcrumb(){return this.history.milestones},async close(t){if(!1!==this.isOpen&&(!0===t&&this.history.clear(),void 0===t||t===this.id)){if(this.history.removeLast(),!0!==this.history.isEmpty())return this.open(this.history.last());e.close.call(this)}},goTo(t){const e=this.history.goto(t);void 0!==e&&this.open(e)},history:Sc(),get icon(){return this.props.icon??"box"},input(t){Vue.set(this.props,"value",t),this.emit("input",this.props.value)},listeners(){return{...this.on,cancel:this.cancel.bind(this),close:this.close.bind(this),crumb:this.goTo.bind(this),input:this.input.bind(this),submit:this.submit.bind(this),success:this.success.bind(this),tab:this.tab.bind(this)}},async open(t,i={}){"string"==typeof t&&(t=`/drawers/${t}`),await e.open.call(this,t,i),this.tab(t.tab);const s=this.state();return!0===t.replace?this.history.replace(-1,s):this.history.add(s),this.focus(),s},set(t){return e.set.call(this,t),this.id=this.id??Ml(),this.state()},tab(t){const e=this.props.tabs??{};if(!(t=t??Object.keys(e??{})[0]))return!1;Vue.set(this.props,"fields",e[t].fields),Vue.set(this.props,"tab",t),this.emit("tab",t),setTimeout((()=>{this.focus()}))}}},Mc=t=>{const e=wc(t,"dropdown",{component:null,isLoading:!1,on:{},path:null,props:{},query:{},referrer:null,timestamp:null});return{...e,close(){this.emit("close"),this.reset()},open(t,i={}){return"string"==typeof t&&(t=`/dropdowns/${t}`),e.open.call(this,t,i)},openAsync(t,e={}){return async i=>{await this.open(t,e);const s=this.options();if(0===s.length)throw Error("The dropdown is empty");i(s)}},options(){return!1===Array.isArray(this.props.options)?[]:this.props.options.map((e=>e.dialog?(e.click=()=>{const i="string"==typeof e.dialog?e.dialog:e.dialog.url,s="object"==typeof e.dialog?e.dialog:{};return t.app.$dialog(i,s)},e):e))},set(t){return t.options&&(t.props={options:t.options}),e.set.call(this,t)}}},Ac=t=>{const e=P();e.on("online",(()=>{t.isOffline=!1})),e.on("offline",(()=>{t.isOffline=!0})),e.on("keydown.cmd.s",(i=>{e.emit(t.context+".save",i)})),e.on("keydown.cmd.shift.f",(()=>t.search())),e.on("keydown.cmd./",(()=>t.search()));const i={document:{blur:!0,click:!1,copy:!0,focus:!0,paste:!0},window:{dragenter:!1,dragexit:!1,dragleave:!1,dragover:!1,drop:!1,keydown:!1,keyup:!1,offline:!1,online:!1,popstate:!1}};return{blur(t){this.emit("blur",t)},click(t){this.emit("click",t)},copy(t){this.emit("copy",t)},dragenter(t){this.entered=t.target,this.prevent(t),this.emit("dragenter",t)},dragexit(t){this.prevent(t),this.entered=null,this.emit("dragexit",t)},dragleave(t){this.prevent(t),this.entered===t.target&&(this.entered=null,this.emit("dragleave",t))},dragover(t){this.prevent(t),this.emit("dragover",t)},drop(t){this.prevent(t),this.entered=null,this.emit("drop",t)},emit:e.emit,entered:null,focus(t){this.emit("focus",t)},keychain(t,e){let i=[t];(e.metaKey||e.ctrlKey)&&i.push("cmd"),!0===e.altKey&&i.push("alt"),!0===e.shiftKey&&i.push("shift");let s=e.key?xl(e.key):null;const n={escape:"esc",arrowUp:"up",arrowDown:"down",arrowLeft:"left",arrowRight:"right"};return n[s]&&(s=n[s]),s&&!1===["alt","control","shift","meta"].includes(s)&&i.push(s),i.join(".")},keydown(t){this.emit(this.keychain("keydown",t),t),this.emit("keydown",t)},keyup(t){this.emit(this.keychain("keyup",t),t),this.emit("keyup",t)},off:e.off,offline(t){this.emit("offline",t)},on:e.on,online(t){this.emit("online",t)},paste(t){this.emit("paste",t)},popstate(t){this.emit("popstate",t)},prevent(t){t.stopPropagation(),t.preventDefault()},subscribe(){for(const t in i.document)document.addEventListener(t,this[t].bind(this),i.document[t]);for(const t in i.window)window.addEventListener(t,this[t].bind(this),i.window[t])},unsubscribe(){for(const t in i.document)document.removeEventListener(t,this[t]);for(const t in i.window)window.removeEventListener(t,this[t])},$on(...t){window.panel.deprecated("`events.$on` will be removed in a future version. Use `events.on` instead."),e.on(...t)},$emit(...t){window.panel.deprecated("`events.$emit` will be removed in a future version. Use `events.emit` instead."),e.emit(...t)},$off(...t){window.panel.deprecated("`events.$off` will be removed in a future version. Use `events.off` instead."),e.off(...t)}}},Ic={interval:null,start(t,e){this.stop(),t&&(this.interval=setInterval(e,t))},stop(){clearInterval(this.interval),this.interval=null}},Dc=(t={})=>({...$c("notification",{context:null,details:null,icon:null,isOpen:!1,message:null,theme:null,timeout:null,type:null}),close(){return this.timer.stop(),this.reset(),this.state()},deprecated(t){console.warn("Deprecated: "+t)},error(e){if(e instanceof hc&&t.user.id)return t.redirect("logout");if(e instanceof mc)return this.fatal(e);if(e instanceof pc){const t=Object.values(e.response.json).find((t=>"string"==typeof(null==t?void 0:t.error)));t&&(e.message=t.error)}return"string"==typeof e&&(e={message:e}),e={message:e.message??"Something went wrong",details:e.details??{}},"view"===t.context&&t.dialog.open({component:"k-error-dialog",props:e}),this.open({message:e.message,icon:"alert",theme:"negative",type:"error"})},info(t={}){return"string"==typeof t&&(t={message:t}),this.open({icon:"info",theme:"info",...t})},get isFatal(){return"fatal"===this.type},fatal(t){return"string"==typeof t?this.open({message:t,type:"fatal"}):t instanceof mc?this.open({message:t.response.text,type:"fatal"}):this.open({message:t.message??"Something went wrong",type:"fatal"})},open(e){return this.timer.stop(),"string"==typeof e?this.success(e):("error"!==e.type&&"fatal"!==e.type&&(e.timeout??(e.timeout=4e3)),this.set({context:t.context,...e}),this.isOpen=!0,this.timer.start(this.timeout,(()=>this.close())),this.state())},success(t={}){return"string"==typeof t&&(t={message:t}),this.open({icon:"check",theme:"positive",...t})},timer:Ic}),jc=()=>({...$c("language",{code:null,default:!1,direction:"ltr",name:null,rules:null}),get isDefault(){return this.default}}),Ec=(t,e,i)=>{if(!i.template&&!i.render&&!i.extends)throw new Error(`Neither template nor render method provided. Nor extending a component when loading plugin component "${e}". The component has not been registered.`);return(i=Lc(t,e,i)).template&&(i.render=null),i=Tc(i),!0===dl(e)&&window.console.warn(`Plugin is replacing "${e}"`),t.component(e,i),i},Lc=(t,e,i)=>"string"!=typeof(null==i?void 0:i.extends)?i:!1===dl(i.extends)?(window.console.warn(`Problem with plugin trying to register component "${e}": cannot extend non-existent component "${i.extends}"`),i.extends=null,i):(i.extends=t.options.components[i.extends].extend({options:i,components:{...t.options.components,...i.components??{}}}),i),Tc=t=>{if(!1===Array.isArray(t.mixins))return t;const e={dialog:Et,drawer:fe,section:Na};return t.mixins=t.mixins.map((i=>{if("string"==typeof i&&void 0!==e[i]){if(t.extends){if(!0===((new t.extends).$options.mixins??[]).includes(e[i]))return}return e[i]}return i})).filter((t=>void 0!==t)),t},Bc=(t,e={})=>((e={components:{},created:[],icons:{},login:null,textareaButtons:{},use:[],thirdParty:{},writerMarks:{},writerNodes:{},...e}).use=((t,e)=>{if(!1===Array.isArray(e))return[];for(const i of e)t.use(i);return e})(t,e.use),e.components=((t,e)=>{if(!1===vt(e))return;const i={};for(const[n,o]of Object.entries(e))try{i[n]=Ec(t,n,o)}catch(s){window.console.warn(s.message)}return i})(t,e.components),e),qc=t=>{var e;const i=$c("menu",{entries:[],hover:!1,isOpen:!1}),s=null==(e=window.matchMedia)?void 0:e.call(window,"(max-width: 60rem)"),n={...i,blur(t){const e=document.querySelector(".k-panel-menu");if(!e||!1===s.matches)return!1;!1===document.querySelector(".k-panel-menu-proxy").contains(t.target)&&!1===e.contains(t.target)&&this.close()},close(){this.isOpen=!1,!1===s.matches&&localStorage.setItem("kirby$menu",!0)},escape(){if(!1===s.matches)return!1;this.close()},open(){this.isOpen=!0,!1===s.matches&&localStorage.removeItem("kirby$menu")},resize(){if(s.matches)return this.close();null!==localStorage.getItem("kirby$menu")?this.isOpen=!1:this.isOpen=!0},set(t){return this.entries=t,this.resize(),this.state()},toggle(){this.isOpen?this.close():this.open()}};return t.events.on("keydown.esc",n.escape.bind(n)),t.events.on("click",n.blur.bind(n)),null==s||s.addEventListener("change",n.resize.bind(n)),n},Pc=t=>({controller:null,requests:0,get isLoading(){return this.requests>0},open(e){t.menu.escape(),t.dialog.open({component:"k-search-dialog",props:{type:e}})},async query(e,i,s){var n;if(null==(n=this.controller)||n.abort(),this.controller=new AbortController,i.length<2)return{results:null,pagination:{}};this.requests++;try{const{$search:n}=await t.get(`/search/${e}`,{query:{query:i,...s},signal:this.controller.signal});return n}catch(o){if("AbortError"!==o.name)return{results:[],pagination:{}}}finally{this.requests--}}}),Nc=()=>{const t=$c("translation",{code:null,data:{},direction:"ltr",name:null});return{...t,set(e){return t.set.call(this,e),document.documentElement.lang=this.code,document.body.dir=this.direction,this.state()},translate(t,e,i=null){if("string"!=typeof t)return;const s=this.data[t]??i;return"string"!=typeof s?s:Sl(s,e)}}};const zc=t=>{const e=$c("upload",{accept:"*",attributes:{},files:[],max:null,multiple:!0,preview:{},replacing:null,url:null});return{...e,...yc(),input:null,cancel(){this.emit("cancel"),this.completed.length>0&&(this.emit("complete",this.completed),t.view.reload()),this.reset()},get completed(){return this.files.filter((t=>t.completed)).map((t=>t.model))},done(){t.dialog.close(),this.completed.length>0&&(this.emit("done",this.completed),!1===t.drawer.isOpen&&(t.notification.success({context:"view"}),t.view.reload())),this.reset()},findDuplicate(t){return this.files.findLastIndex((e=>e.src.name===t.src.name&&e.src.type===t.src.type&&e.src.size===t.src.size&&e.src.lastModified===t.src.lastModified))},hasUniqueName(t){return this.files.filter((e=>e.name===t.name&&e.extension===t.extension)).length<2},file(t){const e=URL.createObjectURL(t);return{...this.preview,completed:!1,error:null,extension:ol(t.name),filename:t.name,id:Ml(),model:null,name:al(t.name),niceSize:rl(t.size),progress:0,size:t.size,src:t,type:t.type,url:e}},open(e,i){e instanceof FileList?(this.set(i),this.select(e)):this.set(e);const s={component:"k-upload-dialog",props:{preview:this.preview},on:{open:t=>this.emit("open",t),cancel:()=>this.cancel(),submit:async()=>{t.dialog.isLoading=!0,await this.submit(),t.dialog.isLoading=!1}}};this.replacing&&(s.component="k-upload-replace-dialog",s.props.original=this.replacing),t.dialog.open(s)},pick(t){this.set(t),this.input=document.createElement("input"),this.input.type="file",this.input.classList.add("sr-only"),this.input.value=null,this.input.accept=this.accept,this.input.multiple=this.multiple,this.input.click(),this.input.addEventListener("change",(e=>{!0===(null==t?void 0:t.immediate)?(this.set(t),this.select(e.target.files),this.submit()):this.open(e.target.files,t),this.input.remove()}))},remove(t){this.files=this.files.filter((e=>e.id!==t))},replace(e,i){this.pick({...i,url:t.urls.api+"/"+e.link,accept:"."+e.extension+","+e.mime,multiple:!1,replacing:e})},reset(){e.reset.call(this),this.files.splice(0)},select(t,e){if(this.set(e),t instanceof Event&&(t=t.target.files),t instanceof FileList==!1)throw new Error("Please provide a FileList");t=(t=[...t]).map((t=>this.file(t))),this.files=[...this.files,...t],this.files=this.files.filter(((t,e)=>this.findDuplicate(t)===e)),null!==this.max&&(this.files=this.files.slice(-1*this.max)),this.emit("select",this.files)},set(t){if(t)return e.set.call(this,t),this.on={},this.addEventListeners(t.on??{}),1===this.max&&(this.multiple=!1),!1===this.multiple&&(this.max=1),this.state()},async submit(){var e;if(!this.url)throw new Error("The upload URL is missing");const i=[];for(const s of this.files){if(!0===s.completed)continue;if(!1===this.hasUniqueName(s)){s.error=t.t("error.file.name.unique");continue}s.error=null,s.progress=0;const n={...this.attributes};i.push((async()=>await this.upload(s,n)));const o=null==(e=this.attributes)?void 0:e.sort;null!=o&&this.attributes.sort++}if(await async function(t,e=20){let i=0,s=0;return new Promise((n=>{const o=e=>s=>{t[e]=s,i--,a()},a=()=>{if(i{e.progress=s}});e.completed=!0,e.model=s.data}catch(s){t.error(s,!1),e.error=s.message,e.progress=0}}}},Fc=t=>{const e=wc(t,"view",{component:null,isLoading:!1,on:{},path:null,props:{},query:{},referrer:null,timestamp:null,breadcrumb:[],breadcrumbLabel:null,icon:null,id:null,link:null,search:"pages",title:null});return{...e,set(i){e.set.call(this,i),t.title=this.title;const s=this.url().toString();window.location.toString()!==s&&(window.history.pushState(null,null,s),window.scrollTo(0,0))},async submit(){throw new Error("Not yet implemented")}}},Yc={config:{},languages:[],license:"missing",multilang:!1,permissions:{},searches:{},urls:{}},Rc=["dialog","drawer"],Uc=["dropdown","language","menu","notification","system","translation","user"],Hc={create(t={}){return this.isLoading=!1,this.isOffline=!1,this.activation=cc(),this.drag=Cc(),this.events=Ac(this),this.searcher=Pc(this),this.upload=zc(this),this.language=jc(),this.menu=qc(this),this.notification=Dc(this),this.system=$c("system",{ascii:{},csrf:null,isLocal:null,locales:{},slugs:[],title:null}),this.translation=Nc(),this.user=$c("user",{email:null,id:null,language:null,role:null,username:null}),this.dropdown=Mc(this),this.view=Fc(this),this.drawer=Oc(this),this.dialog=_c(this),this.redirect=fc,this.reload=this.view.reload.bind(this.view),this.t=this.translation.translate.bind(this.translation),this.plugins=Bc(window.Vue,t),this.set(window.fiber),this.api=vc(this),Vue.reactive(this)},get context(){return this.dialog.isOpen?"dialog":this.drawer.isOpen?"drawer":"view"},get debug(){return!0===this.config.debug},deprecated(t){this.notification.deprecated(t)},get direction(){return this.translation.direction},error(t,e=!0){if(!0===this.debug&&console.error(t),!0===e)return this.notification.error(t)},async get(t,e={}){const{response:i}=await this.request(t,{method:"GET",...e});return(null==i?void 0:i.json)??{}},async open(t,e={}){try{if(!1===Bl(t))this.set(t);else{this.isLoading=!0;const i=await this.get(t,e);this.set(i),this.isLoading=!1}return this.state()}catch(i){return this.error(i)}},overlays(){const t=[];return!0===this.drawer.isOpen&&t.push("drawer"),!0===this.dialog.isOpen&&t.push("dialog"),t},async post(t,e={},i={}){const{response:s}=await this.request(t,{method:"POST",body:e,...i});return s.json},async request(t,e={}){return gc(t,{referrer:this.view.path,csrf:this.system.csrf,...e})},async search(t,e,i){return void 0===e?this.searcher.open(t):this.searcher.query(t,e,i)},set(t={}){t=Object.fromEntries(Object.entries(t).map((([t,e])=>[t.replace("$",""),e])));for(const e in Yc){const i=t[e]??this[e]??Yc[e];typeof i==typeof Yc[e]&&(this[e]=i)}for(const e of Uc)(vt(t[e])||Array.isArray(t[e]))&&this[e].set(t[e]);for(const e of Rc)if(!0===vt(t[e])){if(t[e].redirect)return this.open(t[e].redirect);this[e].open(t[e])}else void 0!==t[e]&&this[e].close(!0);!0===vt(t.dropdown)?this.dropdown.open(t.dropdown):void 0!==t.dropdown&&this.dropdown.close(),!0===vt(t.view)&&this.view.open(t.view)},state(){const t={};for(const e in Yc)t[e]=this[e]??Yc[e];for(const e of Uc)t[e]=this[e].state();for(const e of Rc)t[e]=this[e].state();return t.dropdown=this.dropdown.state(),t.view=this.view.state(),t},get title(){return document.title},set title(t){!1===wl(this.system.title)&&(t+=" | "+this.system.title),document.title=t},url:(t="",e={},i)=>El(t,e,i)},Vc=(t,e)=>{localStorage.setItem("kirby$content$"+t,JSON.stringify(e))},Kc={namespaced:!0,state:{current:null,models:{},status:{enabled:!0}},getters:{exists:t=>e=>Object.hasOwn(t.models,e),hasChanges:(t,e)=>t=>yt(e.model(t).changes)>0,isCurrent:t=>e=>t.current===e,id:t=>e=>((e=e??t.current)&&!1===e.includes("?language=")&&(e+="?language="+window.panel.language.code),e),model:(t,e)=>i=>(i=e.id(i),!0===e.exists(i)?t.models[i]:{api:null,originals:{},values:{},changes:{}}),originals:(t,e)=>t=>structuredClone(e.model(t).originals),values:(t,e)=>t=>({...e.originals(t),...e.changes(t)}),changes:(t,e)=>t=>structuredClone(e.model(t).changes)},mutations:{CLEAR(t){for(const e in t.models)t.models[e].changes={};for(const e in localStorage)e.startsWith("kirby$content$")&&localStorage.removeItem(e)},CREATE(t,[e,i]){if(!i)return!1;let s=t.models[e]?t.models[e].changes:i.changes;Vue.set(t.models,e,{api:i.api,originals:i.originals,changes:s??{}})},CURRENT(t,e){t.current=e},MOVE(t,[e,i]){const s=structuredClone(t.models[e]);Vue.set(t.models,i,s);const n=localStorage.getItem("kirby$content$"+e);localStorage.setItem("kirby$content$"+i,n),t.current=i,localStorage.removeItem("kirby$content$"+e),Vue.del(t.models,e)},REMOVE(t,e){Vue.del(t.models,e),localStorage.removeItem("kirby$content$"+e)},REVERT(t,e){t.models[e]&&(Vue.set(t.models[e],"changes",{}),localStorage.removeItem("kirby$content$"+e))},STATUS(t,e){Vue.set(t.status,"enabled",e)},UPDATE(t,[e,i,s]){if(!t.models[e])return!1;void 0===s&&(s=null),s=structuredClone(s);const n=JSON.stringify(s);JSON.stringify(t.models[e].originals[i]??null)==n?Vue.del(t.models[e].changes,i):Vue.set(t.models[e].changes,i,s),Vc(e,{api:t.models[e].api,originals:t.models[e].originals,changes:t.models[e].changes})}},actions:{init(t){for(const e in localStorage)if(e.startsWith("kirby$content$")){const i=e.split("kirby$content$")[1],s=localStorage.getItem("kirby$content$"+i);t.commit("CREATE",[i,JSON.parse(s)])}else if(e.startsWith("kirby$form$")){const i=e.split("kirby$form$")[1],s=localStorage.getItem("kirby$form$"+i);let n=null;try{n=JSON.parse(s)}catch{}if(!n||!n.api)return localStorage.removeItem("kirby$form$"+i),!1;const o={api:n.api,originals:n.originals,changes:n.values};t.commit("CREATE",[i,o]),Vc(i,o),localStorage.removeItem("kirby$form$"+i)}},clear(t){t.commit("CLEAR")},create(t,e){const i=structuredClone(e.content);if(Array.isArray(e.ignore))for(const n of e.ignore)delete i[n];e.id=t.getters.id(e.id);const s={api:e.api,originals:i,changes:{}};t.commit("CREATE",[e.id,s]),t.dispatch("current",e.id)},current(t,e){e=t.getters.id(e),t.commit("CURRENT",e)},disable(t){t.commit("STATUS",!1)},enable(t){t.commit("STATUS",!0)},move(t,[e,i]){e=t.getters.id(e),i=t.getters.id(i),t.commit("MOVE",[e,i])},remove(t,e){e=t.getters.id(e),t.commit("REMOVE",e),t.getters.isCurrent(e)&&t.commit("CURRENT",null)},revert(t,e){e=t.getters.id(e),t.commit("REVERT",e)},async save(t,e){if(e=t.getters.id(e),t.getters.isCurrent(e)&&!1===t.state.status.enabled)return!1;t.dispatch("disable");const i=t.getters.model(e),s={...i.originals,...i.changes};try{await window.panel.api.patch(i.api,s),t.commit("CREATE",[e,{...i,originals:s}]),t.dispatch("revert",e)}finally{t.dispatch("enable")}},update(t,[e,i,s]){if(s=s??t.state.current,null===e)for(const n in i)t.commit("UPDATE",[s,n,i[n]]);else t.commit("UPDATE",[s,e,i])}}},Wc={namespaced:!0,actions:{close(t,e){window.panel.deprecated("`$store.drawer` will be removed in a future version. Use `$panel.drawer` instead."),window.panel.drawer.close(e)},goto(t,e){window.panel.deprecated("`$store.drawer` will be removed in a future version. Use `$panel.drawer` instead."),window.panel.drawer.goto(e)},open(t,e){window.panel.deprecated("`$store.drawer` will be removed in a future version. Use `$panel.drawer` instead."),window.panel.drawer.goto(e)}}},Jc={namespaced:!0,actions:{close(){window.panel.deprecated("`$store.notification` will be removed in a future version. Use `$panel.notification` instead."),window.panel.notification.close()},deprecated(t,e){window.panel.deprecated("`$store.notification.deprecated` will be removed in a future version. Use `$panel.deprecated` instead."),window.panel.notification.deprecated(e)},error(t,e){window.panel.deprecated("`$store.notification` will be removed in a future version. Use `$panel.notification` instead."),window.panel.notification.error(e)},open(t,e){window.panel.deprecated("`$store.notification` will be removed in a future version. Use `$panel.notification` instead."),window.panel.notification.open(e)},success(t,e){window.panel.deprecated("`$store.notification` will be removed in a future version. Use `$panel.notification` instead."),window.panel.notification.success(e)}}};Vue.use(N);const Gc=new N.Store({strict:!1,actions:{dialog(t,e){window.panel.deprecated("`$store.dialog` will be removed in a future version. Use `$panel.dialog.open()` instead."),window.panel.dialog.open(e)},drag(t,e){window.panel.deprecated("`$store.drag` will be removed in a future version. Use `$panel.drag.start(type, data)` instead."),window.panel.drag.start(...e)},fatal(t,e){window.panel.deprecated("`$store.fatal` will be removed in a future version. Use `$panel.notification.fatal()` instead."),window.panel.notification.fatal(e)},isLoading(t,e){window.panel.deprecated("`$store.isLoading` will be removed in a future version. Use `$panel.isLoading` instead."),window.panel.isLoading=e},navigate(){window.panel.deprecated("`$store.navigate` will be removed in a future version."),window.panel.dialog.close(),window.panel.drawer.close()}},modules:{content:Kc,drawers:Wc,notification:Jc}});Vue.config.productionTip=!1,Vue.config.devtools=!0,Vue.use(zl),Vue.use(lc),Vue.use(z),Vue.use(Hr),window.panel=Vue.prototype.$panel=Hc.create(window.panel.plugins),Vue.prototype.$dialog=window.panel.dialog.open.bind(window.panel.dialog),Vue.prototype.$drawer=window.panel.drawer.open.bind(window.panel.drawer),Vue.prototype.$dropdown=window.panel.dropdown.openAsync.bind(window.panel.dropdown),Vue.prototype.$go=window.panel.view.open.bind(window.panel.view),Vue.prototype.$reload=window.panel.reload.bind(window.panel),window.panel.app=new Vue({store:Gc,render:()=>Vue.h(R)}),Vue.use(Fl),Vue.use(Vr),Vue.use(Yl),!1===CSS.supports("container","foo / inline-size")&&Y((()=>import("./container-query-polyfill.modern.min.js")),[],import.meta.url),window.panel.app.$mount("#app");export{ct as n}; diff --git a/kirby/panel/dist/js/plugins.js b/kirby/panel/dist/js/plugins.js index 4cf1612..64f6547 100644 --- a/kirby/panel/dist/js/plugins.js +++ b/kirby/panel/dist/js/plugins.js @@ -1,75 +1,91 @@ -window.panel = window.panel || {}; +window.panel = window.panel ?? {}; window.panel.plugins = { - components: {}, - created: [], - icons: {}, - routes: [], - use: [], - views: {}, - thirdParty: {} + components: {}, + created: [], + icons: {}, + routes: [], + textareaButtons: {}, + thirdParty: {}, + use: [], + views: {}, + writerMarks: {}, + writerNodes: {} }; -window.panel.plugin = function (plugin, parts) { - // Blocks - resolve(parts, "blocks", function (name, options) { - if (typeof options === "string") { - options = { template: options }; - } +window.panel.plugin = function (plugin, extensions) { + // Blocks + resolve(extensions, "blocks", (name, options) => { + if (typeof options === "string") { + options = { template: options }; + } - window.panel.plugins.components[`k-block-type-${name}`] = { - extends: "k-block-type", - ...options - }; - }); + window.panel.plugins.components[`k-block-type-${name}`] = { + extends: "k-block-type-default", + ...options + }; + }); - // Components - resolve(parts, "components", function (name, options) { - window.panel.plugins.components[name] = options; - }); + // Components + resolve(extensions, "components", (name, options) => { + window.panel.plugins.components[name] = options; + }); - // Fields - resolve(parts, "fields", function (name, options) { - window.panel.plugins.components[`k-${name}-field`] = options; - }); + // Fields + resolve(extensions, "fields", (name, options) => { + window.panel.plugins.components[`k-${name}-field`] = options; + }); - // Icons - resolve(parts, "icons", function (name, options) { - window.panel.plugins.icons[name] = options; - }); + // Icons + resolve(extensions, "icons", (name, options) => { + window.panel.plugins.icons[name] = options; + }); - // Sections - resolve(parts, "sections", function (name, options) { - window.panel.plugins.components[`k-${name}-section`] = { - ...options, - mixins: ["section", ...(options.mixins || [])] - }; - }); + // Sections + resolve(extensions, "sections", (name, options) => { + window.panel.plugins.components[`k-${name}-section`] = { + ...options, + mixins: ["section", ...(options.mixins ?? [])] + }; + }); - // `Vue.use` - resolve(parts, "use", function (name, options) { - window.panel.plugins.use.push(options); - }); + // `Vue.use` + resolve(extensions, "use", (name, options) => { + window.panel.plugins.use.push(options); + }); - // Vue `created` callback - if (parts["created"]) { - window.panel.plugins.created.push(parts["created"]); - } + // Vue `created` callback + if (extensions["created"]) { + window.panel.plugins.created.push(extensions["created"]); + } - // Login - if (parts.login) { - window.panel.plugins.login = parts.login; - } + // Login + if (extensions.login) { + window.panel.plugins.login = extensions.login; + } - // Third-party plugins - resolve(parts, "thirdParty", function (name, options) { - window.panel.plugins.thirdParty[name] = options; - }); + // Textarea custom toolbar buttons + resolve(extensions, "textareaButtons", (name, options) => { + window.panel.plugins.textareaButtons[name] = options; + }); + + // Third-party plugins + resolve(extensions, "thirdParty", (name, options) => { + window.panel.plugins.thirdParty[name] = options; + }); + + // Writer custom marks + resolve(extensions, "writerMarks", (name, options) => { + window.panel.plugins.writerMarks[name] = options; + }); + + // Writer custom nodes + resolve(extensions, "writerNodes", function (name, options) { + window.panel.plugins.writerNodes[name] = options; + }); }; -function resolve(object, type, callback) { - if (object[type]) { - for (const [name, options] of Object.entries(object[type])) { - callback(name, options); - } - } -} +const resolve = (extensions, type, callback) => { + for (const [name, options] of Object.entries(extensions[type] ?? {})) { + callback(name, options); + } +}; diff --git a/kirby/panel/dist/js/vendor.js b/kirby/panel/dist/js/vendor.js deleted file mode 100644 index c7b159f..0000000 --- a/kirby/panel/dist/js/vendor.js +++ /dev/null @@ -1,6 +0,0 @@ -function e(e){this.content=e}function t(e,n,r){for(let i=0;;i++){if(i==e.childCount||i==n.childCount)return e.childCount==n.childCount?null:r;let o=e.child(i),s=n.child(i);if(o!=s){if(!o.sameMarkup(s))return r;if(o.isText&&o.text!=s.text){for(let e=0;o.text[e]==s.text[e];e++)r++;return r}if(o.content.size||s.content.size){let e=t(o.content,s.content,r+1);if(null!=e)return e}r+=o.nodeSize}else r+=o.nodeSize}}function n(e,t,r,i){for(let o=e.childCount,s=t.childCount;;){if(0==o||0==s)return o==s?null:{a:r,b:i};let l=e.child(--o),a=t.child(--s),c=l.nodeSize;if(l!=a){if(!l.sameMarkup(a))return{a:r,b:i};if(l.isText&&l.text!=a.text){let e=0,t=Math.min(l.text.length,a.text.length);for(;e>1}},e.from=function(t){if(t instanceof e)return t;var n=[];if(t)for(var r in t)n.push(r,t[r]);return new e(n)};class r{constructor(e,t){if(this.content=e,this.size=t||0,null==t)for(let n=0;ne&&!1!==n(l,r+s,i||null,o)&&l.content.size){let i=s+1;l.nodesBetween(Math.max(0,e-i),Math.min(l.content.size,t-i),n,r+i)}s=a}}descendants(e){this.nodesBetween(0,this.size,e)}textBetween(e,t,n,r){let i="",o=!0;return this.nodesBetween(e,t,((s,l)=>{s.isText?(i+=s.text.slice(Math.max(e,l)-l,t-l),o=!n):s.isLeaf?(r?i+="function"==typeof r?r(s):r:s.type.spec.leafText&&(i+=s.type.spec.leafText(s)),o=!n):!o&&s.isBlock&&(i+=n,o=!0)}),0),i}append(e){if(!e.size)return this;if(!this.size)return e;let t=this.lastChild,n=e.firstChild,i=this.content.slice(),o=0;for(t.isText&&t.sameMarkup(n)&&(i[i.length-1]=t.withText(t.text+n.text),o=1);oe)for(let r=0,o=0;oe&&((ot)&&(s=s.isText?s.cut(Math.max(0,e-o),Math.min(s.text.length,t-o)):s.cut(Math.max(0,e-o-1),Math.min(s.content.size,t-o-1))),n.push(s),i+=s.nodeSize),o=l}return new r(n,i)}cutByIndex(e,t){return e==t?r.empty:0==e&&t==this.content.length?this:new r(this.content.slice(e,t))}replaceChild(e,t){let n=this.content[e];if(n==t)return this;let i=this.content.slice(),o=this.size+t.nodeSize-n.nodeSize;return i[e]=t,new r(i,o)}addToStart(e){return new r([e].concat(this.content),this.size+e.nodeSize)}addToEnd(e){return new r(this.content.concat(e),this.size+e.nodeSize)}eq(e){if(this.content.length!=e.content.length)return!1;for(let t=0;tthis.size||e<0)throw new RangeError(`Position ${e} outside of fragment (${this})`);for(let n=0,r=0;;n++){let i=r+this.child(n).nodeSize;if(i>=e)return i==e||t>0?o(n+1,i):o(n,r);r=i}}toString(){return"<"+this.toStringInner()+">"}toStringInner(){return this.content.join(", ")}toJSON(){return this.content.length?this.content.map((e=>e.toJSON())):null}static fromJSON(e,t){if(!t)return r.empty;if(!Array.isArray(t))throw new RangeError("Invalid input for Fragment.fromJSON");return new r(t.map(e.nodeFromJSON))}static fromArray(e){if(!e.length)return r.empty;let t,n=0;for(let r=0;rthis.type.rank&&(t||(t=e.slice(0,r)),t.push(this),n=!0),t&&t.push(i)}}return t||(t=e.slice()),n||t.push(this),t}removeFromSet(e){for(let t=0;te.type.rank-t.type.rank)),t}}l.none=[];class a extends Error{}class c{constructor(e,t,n){this.content=e,this.openStart=t,this.openEnd=n}get size(){return this.content.size-this.openStart-this.openEnd}insertAt(e,t){let n=d(this.content,e+this.openStart,t);return n&&new c(n,this.openStart,this.openEnd)}removeBetween(e,t){return new c(h(this.content,e+this.openStart,t+this.openStart),this.openStart,this.openEnd)}eq(e){return this.content.eq(e.content)&&this.openStart==e.openStart&&this.openEnd==e.openEnd}toString(){return this.content+"("+this.openStart+","+this.openEnd+")"}toJSON(){if(!this.content.size)return null;let e={content:this.content.toJSON()};return this.openStart>0&&(e.openStart=this.openStart),this.openEnd>0&&(e.openEnd=this.openEnd),e}static fromJSON(e,t){if(!t)return c.empty;let n=t.openStart||0,i=t.openEnd||0;if("number"!=typeof n||"number"!=typeof i)throw new RangeError("Invalid input for Slice.fromJSON");return new c(r.fromJSON(e,t.content),n,i)}static maxOpen(e,t=!0){let n=0,r=0;for(let i=e.firstChild;i&&!i.isLeaf&&(t||!i.type.spec.isolating);i=i.firstChild)n++;for(let i=e.lastChild;i&&!i.isLeaf&&(t||!i.type.spec.isolating);i=i.lastChild)r++;return new c(e,n,r)}}function h(e,t,n){let{index:r,offset:i}=e.findIndex(t),o=e.maybeChild(r),{index:s,offset:l}=e.findIndex(n);if(i==t||o.isText){if(l!=n&&!e.child(s).isText)throw new RangeError("Removing non-flat range");return e.cut(0,t).append(e.cut(n))}if(r!=s)throw new RangeError("Removing non-flat range");return e.replaceChild(r,o.copy(h(o.content,t-i-1,n-i-1)))}function d(e,t,n,r){let{index:i,offset:o}=e.findIndex(t),s=e.maybeChild(i);if(o==t||s.isText)return r&&!r.canReplace(i,i,n)?null:e.cut(0,t).append(n).append(e.cut(t));let l=d(s.content,t-o-1,n);return l&&e.replaceChild(i,s.copy(l))}function u(e,t,n){if(n.openStart>e.depth)throw new a("Inserted content deeper than insertion position");if(e.depth-n.openStart!=t.depth-n.openEnd)throw new a("Inconsistent open depths");return f(e,t,n,0)}function f(e,t,n,i){let o=e.index(i),s=e.node(i);if(o==t.index(i)&&i=0;o--)i=t.node(o).copy(r.from(i));return{start:i.resolveNoCache(e.openStart+n),end:i.resolveNoCache(i.content.size-e.openEnd-n)}}(n,e);return v(s,w(e,o,l,t,i))}{let r=e.parent,i=r.content;return v(r,i.cut(0,e.parentOffset).append(n.content).append(i.cut(t.parentOffset)))}}return v(s,b(e,t,i))}function p(e,t){if(!t.type.compatibleContent(e.type))throw new a("Cannot join "+t.type.name+" onto "+e.type.name)}function m(e,t,n){let r=e.node(n);return p(r,t.node(n)),r}function g(e,t){let n=t.length-1;n>=0&&e.isText&&e.sameMarkup(t[n])?t[n]=e.withText(t[n].text+e.text):t.push(e)}function y(e,t,n,r){let i=(t||e).node(n),o=0,s=t?t.index(n):i.childCount;e&&(o=e.index(n),e.depth>n?o++:e.textOffset&&(g(e.nodeAfter,r),o++));for(let l=o;lo&&m(e,t,o+1),l=i.depth>o&&m(n,i,o+1),a=[];return y(null,e,o,a),s&&l&&t.index(o)==n.index(o)?(p(s,l),g(v(s,w(e,t,n,i,o+1)),a)):(s&&g(v(s,b(e,t,o+1)),a),y(t,n,o,a),l&&g(v(l,b(n,i,o+1)),a)),y(i,null,o,a),new r(a)}function b(e,t,n){let i=[];if(y(null,e,n,i),e.depth>n){g(v(m(e,t,n+1),b(e,t,n+1)),i)}return y(t,null,n,i),new r(i)}c.empty=new c(r.empty,0,0);class x{constructor(e,t,n){this.pos=e,this.path=t,this.parentOffset=n,this.depth=t.length/3-1}resolveDepth(e){return null==e?this.depth:e<0?this.depth+e:e}get parent(){return this.node(this.depth)}get doc(){return this.node(0)}node(e){return this.path[3*this.resolveDepth(e)]}index(e){return this.path[3*this.resolveDepth(e)+1]}indexAfter(e){return e=this.resolveDepth(e),this.index(e)+(e!=this.depth||this.textOffset?1:0)}start(e){return 0==(e=this.resolveDepth(e))?0:this.path[3*e-1]+1}end(e){return e=this.resolveDepth(e),this.start(e)+this.node(e).content.size}before(e){if(!(e=this.resolveDepth(e)))throw new RangeError("There is no position before the top-level node");return e==this.depth+1?this.pos:this.path[3*e-1]}after(e){if(!(e=this.resolveDepth(e)))throw new RangeError("There is no position after the top-level node");return e==this.depth+1?this.pos:this.path[3*e-1]+this.path[3*e].nodeSize}get textOffset(){return this.pos-this.path[this.path.length-1]}get nodeAfter(){let e=this.parent,t=this.index(this.depth);if(t==e.childCount)return null;let n=this.pos-this.path[this.path.length-1],r=e.child(t);return n?e.child(t).cut(n):r}get nodeBefore(){let e=this.index(this.depth),t=this.pos-this.path[this.path.length-1];return t?this.parent.child(e).cut(0,t):0==e?null:this.parent.child(e-1)}posAtIndex(e,t){t=this.resolveDepth(t);let n=this.path[3*t],r=0==t?0:this.path[3*t-1]+1;for(let i=0;i0;t--)if(this.start(t)<=e&&this.end(t)>=e)return t;return 0}blockRange(e=this,t){if(e.pos=0;n--)if(e.pos<=this.end(n)&&(!t||t(this.node(n))))return new O(this,e,n);return null}sameParent(e){return this.pos-this.parentOffset==e.pos-e.parentOffset}max(e){return e.pos>this.pos?e:this}min(e){return e.pos=0&&t<=e.content.size))throw new RangeError("Position "+t+" out of range");let n=[],r=0,i=t;for(let o=e;;){let{index:e,offset:t}=o.content.findIndex(i),s=i-t;if(n.push(o,e,r+t),!s)break;if(o=o.child(e),o.isText)break;i=s-1,r+=t+1}return new x(t,n,i)}static resolveCached(e,t){for(let r=0;re&&this.nodesBetween(e,t,(e=>(n.isInSet(e.marks)&&(r=!0),!r))),r}get isBlock(){return this.type.isBlock}get isTextblock(){return this.type.isTextblock}get inlineContent(){return this.type.inlineContent}get isInline(){return this.type.isInline}get isText(){return this.type.isText}get isLeaf(){return this.type.isLeaf}get isAtom(){return this.type.isAtom}toString(){if(this.type.spec.toDebugString)return this.type.spec.toDebugString(this);let e=this.type.name;return this.content.size&&(e+="("+this.content.toStringInner()+")"),T(this.marks,e)}contentMatchAt(e){let t=this.type.contentMatch.matchFragment(this.content,0,e);if(!t)throw new Error("Called contentMatchAt on a node with invalid content");return t}canReplace(e,t,n=r.empty,i=0,o=n.childCount){let s=this.contentMatchAt(e).matchFragment(n,i,o),l=s&&s.matchFragment(this.content,t);if(!l||!l.validEnd)return!1;for(let r=i;re.type.name))}`);this.content.forEach((e=>e.check()))}toJSON(){let e={type:this.type.name};for(let t in this.attrs){e.attrs=this.attrs;break}return this.content.size&&(e.content=this.content.toJSON()),this.marks.length&&(e.marks=this.marks.map((e=>e.toJSON()))),e}static fromJSON(e,t){if(!t)throw new RangeError("Invalid input for Node.fromJSON");let n=null;if(t.marks){if(!Array.isArray(t.marks))throw new RangeError("Invalid mark data for Node.fromJSON");n=t.marks.map(e.markFromJSON)}if("text"==t.type){if("string"!=typeof t.text)throw new RangeError("Invalid text node in JSON");return e.text(t.text,n)}let i=r.fromJSON(e,t.content);return e.nodeType(t.type).create(t.attrs,i,n)}}N.prototype.text=void 0;class D extends N{constructor(e,t,n,r){if(super(e,t,null,r),!n)throw new RangeError("Empty text nodes are not allowed");this.text=n}toString(){return this.type.spec.toDebugString?this.type.spec.toDebugString(this):T(this.marks,JSON.stringify(this.text))}get textContent(){return this.text}textBetween(e,t){return this.text.slice(e,t)}get nodeSize(){return this.text.length}mark(e){return e==this.marks?this:new D(this.type,this.attrs,this.text,e)}withText(e){return e==this.text?this:new D(this.type,this.attrs,e,this.marks)}cut(e=0,t=this.text.length){return 0==e&&t==this.text.length?this:this.withText(this.text.slice(e,t))}eq(e){return this.sameMarkup(e)&&this.text==e.text}toJSON(){let e=super.toJSON();return e.text=this.text,e}}function T(e,t){for(let n=e.length-1;n>=0;n--)t=e[n].type.name+"("+t+")";return t}class E{constructor(e){this.validEnd=e,this.next=[],this.wrapCache=[]}static parse(e,t){let n=new A(e,t);if(null==n.next)return E.empty;let r=$(n);n.next&&n.err("Unexpected trailing text");let i=function(e){let t=Object.create(null);return n(B(e,0));function n(r){let i=[];r.forEach((t=>{e[t].forEach((({term:t,to:n})=>{if(!t)return;let r;for(let e=0;e{r||i.push([t,r=[]]),-1==r.indexOf(e)&&r.push(e)}))}))}));let o=t[r.join(",")]=new E(r.indexOf(e.length-1)>-1);for(let e=0;ee.to=t))}function o(e,t){if("choice"==e.type)return e.exprs.reduce(((e,n)=>e.concat(o(n,t))),[]);if("seq"!=e.type){if("star"==e.type){let s=n();return r(t,s),i(o(e.expr,s),s),[r(s)]}if("plus"==e.type){let s=n();return i(o(e.expr,t),s),i(o(e.expr,s),s),[r(s)]}if("opt"==e.type)return[r(t)].concat(o(e.expr,t));if("range"==e.type){let s=t;for(let t=0;te.createAndFill())));for(let e=0;e=this.next.length)throw new RangeError(`There's no ${e}th edge in this content match`);return this.next[e]}toString(){let e=[];return function t(n){e.push(n);for(let r=0;r{let r=n+(t.validEnd?"*":" ")+" ";for(let i=0;i"+e.indexOf(t.next[i].next);return r})).join("\n")}}E.empty=new E(!0);class A{constructor(e,t){this.string=e,this.nodeTypes=t,this.inline=null,this.pos=0,this.tokens=e.split(/\s*(?=\b|\W|$)/),""==this.tokens[this.tokens.length-1]&&this.tokens.pop(),""==this.tokens[0]&&this.tokens.shift()}get next(){return this.tokens[this.pos]}eat(e){return this.next==e&&(this.pos++||!0)}err(e){throw new SyntaxError(e+" (in content expression '"+this.string+"')")}}function $(e){let t=[];do{t.push(P(e))}while(e.eat("|"));return 1==t.length?t[0]:{type:"choice",exprs:t}}function P(e){let t=[];do{t.push(I(e))}while(e.next&&")"!=e.next&&"|"!=e.next);return 1==t.length?t[0]:{type:"seq",exprs:t}}function I(e){let t=function(e){if(e.eat("(")){let t=$(e);return e.eat(")")||e.err("Missing closing paren"),t}if(!/\W/.test(e.next)){let t=function(e,t){let n=e.nodeTypes,r=n[t];if(r)return[r];let i=[];for(let o in n){let e=n[o];e.groups.indexOf(t)>-1&&i.push(e)}0==i.length&&e.err("No node type or group '"+t+"' found");return i}(e,e.next).map((t=>(null==e.inline?e.inline=t.isInline:e.inline!=t.isInline&&e.err("Mixing inline and block content"),{type:"name",value:t})));return e.pos++,1==t.length?t[0]:{type:"choice",exprs:t}}e.err("Unexpected token '"+e.next+"'")}(e);for(;;)if(e.eat("+"))t={type:"plus",expr:t};else if(e.eat("*"))t={type:"star",expr:t};else if(e.eat("?"))t={type:"opt",expr:t};else{if(!e.eat("{"))break;t=R(e,t)}return t}function z(e){/\D/.test(e.next)&&e.err("Expected number, got '"+e.next+"'");let t=Number(e.next);return e.pos++,t}function R(e,t){let n=z(e),r=n;return e.eat(",")&&(r="}"!=e.next?z(e):-1),e.eat("}")||e.err("Unclosed braced range"),{type:"range",min:n,max:r,expr:t}}function _(e,t){return t-e}function B(e,t){let n=[];return function t(r){let i=e[r];if(1==i.length&&!i[0].term)return t(i[0].to);n.push(r);for(let e=0;e-1}allowsMarks(e){if(null==this.markSet)return!0;for(let t=0;tr[t]=new e(t,n,i)));let i=n.spec.topNode||"doc";if(!r[i])throw new RangeError("Schema is missing its top node type ('"+i+"')");if(!r.text)throw new RangeError("Every schema needs a 'text' type");for(let e in r.text.attrs)throw new RangeError("The text node type should not have attributes");return r}};class q{constructor(e){this.hasDefault=Object.prototype.hasOwnProperty.call(e,"default"),this.default=e.default}get isRequired(){return!this.hasDefault}}class W{constructor(e,t,n,r){this.name=e,this.rank=t,this.schema=n,this.spec=r,this.attrs=F(r.attrs),this.excluded=null;let i=V(this.attrs);this.instance=i?new l(this,i):null}create(e=null){return!e&&this.instance?this.instance:new l(this,j(this.attrs,e))}static compile(e,t){let n=Object.create(null),r=0;return e.forEach(((e,i)=>n[e]=new W(e,r++,t,i))),n}removeFromSet(e){for(var t=0;t-1}}class J{constructor(t){this.cached=Object.create(null);let n=this.spec={};for(let e in t)n[e]=t[e];n.nodes=e.from(t.nodes),n.marks=e.from(t.marks||{}),this.nodes=L.compile(this.spec.nodes,this),this.marks=W.compile(this.spec.marks,this);let r=Object.create(null);for(let e in this.nodes){if(e in this.marks)throw new RangeError(e+" can not be both a node and a mark");let t=this.nodes[e],n=t.spec.content||"",i=t.spec.marks;t.contentMatch=r[n]||(r[n]=E.parse(n,this.nodes)),t.inlineContent=t.contentMatch.inlineContent,t.markSet="_"==i?null:i?K(this,i.split(" ")):""!=i&&t.inlineContent?null:[]}for(let e in this.marks){let t=this.marks[e],n=t.spec.excludes;t.excluded=null==n?[t]:""==n?[]:K(this,n.split(" "))}this.nodeFromJSON=this.nodeFromJSON.bind(this),this.markFromJSON=this.markFromJSON.bind(this),this.topNodeType=this.nodes[this.spec.topNode||"doc"],this.cached.wrappings=Object.create(null)}node(e,t=null,n,r){if("string"==typeof e)e=this.nodeType(e);else{if(!(e instanceof L))throw new RangeError("Invalid node type: "+e);if(e.schema!=this)throw new RangeError("Node type from different schema used ("+e.name+")")}return e.createChecked(t,n,r)}text(e,t){let n=this.nodes.text;return new D(n,n.defaultAttrs,e,l.setFrom(t))}mark(e,t){return"string"==typeof e&&(e=this.marks[e]),e.create(t)}nodeFromJSON(e){return N.fromJSON(this,e)}markFromJSON(e){return l.fromJSON(this,e)}nodeType(e){let t=this.nodes[e];if(!t)throw new RangeError("Unknown node type: "+e);return t}}function K(e,t){let n=[];for(let r=0;r-1)&&n.push(s=r)}if(!s)throw new SyntaxError("Unknown mark type: '"+t[r]+"'")}return n}class H{constructor(e,t){this.schema=e,this.rules=t,this.tags=[],this.styles=[],t.forEach((e=>{e.tag?this.tags.push(e):e.style&&this.styles.push(e)})),this.normalizeLists=!this.tags.some((t=>{if(!/^(ul|ol)\b/.test(t.tag)||!t.node)return!1;let n=e.nodes[t.node];return n.contentMatch.matchType(n)}))}parse(e,t={}){let n=new ne(this,t,!1);return n.addAll(e,t.from,t.to),n.finish()}parseSlice(e,t={}){let n=new ne(this,t,!0);return n.addAll(e,t.from,t.to),c.maxOpen(n.finish())}matchTag(e,t,n){for(let r=n?this.tags.indexOf(n)+1:0;re.length&&(61!=o.charCodeAt(e.length)||o.slice(e.length+1)!=t))){if(r.getAttrs){let e=r.getAttrs(t);if(!1===e)continue;r.attrs=e||void 0}return r}}}static schemaRules(e){let t=[];function n(e){let n=null==e.priority?50:e.priority,r=0;for(;r{n(e=ie(e)),e.mark||e.ignore||e.clearMark||(e.mark=r)}))}for(let r in e.nodes){let t=e.nodes[r].spec.parseDOM;t&&t.forEach((e=>{n(e=ie(e)),e.node||e.ignore||e.mark||(e.node=r)}))}return t}static fromSchema(e){return e.cached.domParser||(e.cached.domParser=new H(e,H.schemaRules(e)))}}const Y={address:!0,article:!0,aside:!0,blockquote:!0,canvas:!0,dd:!0,div:!0,dl:!0,fieldset:!0,figcaption:!0,figure:!0,footer:!0,form:!0,h1:!0,h2:!0,h3:!0,h4:!0,h5:!0,h6:!0,header:!0,hgroup:!0,hr:!0,li:!0,noscript:!0,ol:!0,output:!0,p:!0,pre:!0,section:!0,table:!0,tfoot:!0,ul:!0},U={head:!0,noscript:!0,object:!0,script:!0,style:!0,title:!0},G={ol:!0,ul:!0},Z=1,X=2,Q=4;function ee(e,t,n){return null!=t?(t?Z:0)|("full"===t?X:0):e&&"pre"==e.whitespace?Z|X:n&~Q}class te{constructor(e,t,n,r,i,o,s){this.type=e,this.attrs=t,this.marks=n,this.pendingMarks=r,this.solid=i,this.options=s,this.content=[],this.activeMarks=l.none,this.stashMarks=[],this.match=o||(s&Q?null:e.contentMatch)}findWrapping(e){if(!this.match){if(!this.type)return[];let t=this.type.contentMatch.fillBefore(r.from(e));if(!t){let t,n=this.type.contentMatch;return(t=n.findWrapping(e.type))?(this.match=n,t):null}this.match=this.type.contentMatch.matchFragment(t)}return this.match.findWrapping(e.type)}finish(e){if(!(this.options&Z)){let e,t=this.content[this.content.length-1];if(t&&t.isText&&(e=/[ \t\r\n\u000c]+$/.exec(t.text))){let n=t;t.text.length==e[0].length?this.content.pop():this.content[this.content.length-1]=n.withText(n.text.slice(0,n.text.length-e[0].length))}}let t=r.from(this.content);return!e&&this.match&&(t=t.append(this.match.fillBefore(r.empty,!0))),this.type?this.type.create(this.attrs,t,this.marks):t}popFromStashMark(e){for(let t=this.stashMarks.length-1;t>=0;t--)if(e.eq(this.stashMarks[t]))return this.stashMarks.splice(t,1)[0]}applyPending(e){for(let t=0,n=this.pendingMarks;t{o.clearMark(e)&&(n=e.addToSet(n))})):t=this.parser.schema.marks[o.mark].create(o.attrs).addToSet(t),!1!==o.consuming)break;i=o}return[t,n]}addElementByRule(e,t,n){let r,i,o;if(t.node)i=this.parser.schema.nodes[t.node],i.isLeaf?this.insertNode(i.create(t.attrs))||this.leafFallback(e):r=this.enter(i,t.attrs||null,t.preserveWhitespace);else{o=this.parser.schema.marks[t.mark].create(t.attrs),this.addPendingMark(o)}let s=this.top;if(i&&i.isLeaf)this.findInside(e);else if(n)this.addElement(e,n);else if(t.getContent)this.findInside(e),t.getContent(e,this.parser.schema).forEach((e=>this.insertNode(e)));else{let n=e;"string"==typeof t.contentElement?n=e.querySelector(t.contentElement):"function"==typeof t.contentElement?n=t.contentElement(e):t.contentElement&&(n=t.contentElement),this.findAround(e,n,!0),this.addAll(n)}r&&this.sync(s)&&this.open--,o&&this.removePendingMark(o,s)}addAll(e,t,n){let r=t||0;for(let i=t?e.childNodes[t]:e.firstChild,o=null==n?null:e.childNodes[n];i!=o;i=i.nextSibling,++r)this.findAtPoint(e,r),this.addDOM(i);this.findAtPoint(e,r)}findPlace(e){let t,n;for(let r=this.open;r>=0;r--){let i=this.nodes[r],o=i.findWrapping(e);if(o&&(!t||t.length>o.length)&&(t=o,n=i,!o.length))break;if(i.solid)break}if(!t)return!1;this.sync(n);for(let r=0;rthis.open){for(;t>this.open;t--)this.nodes[t-1].content.push(this.nodes[t].finish(e));this.nodes.length=this.open+1}}finish(){return this.open=0,this.closeExtra(this.isOpen),this.nodes[0].finish(this.isOpen||this.options.topOpen)}sync(e){for(let t=this.open;t>=0;t--)if(this.nodes[t]==e)return this.open=t,!0;return!1}get currentPos(){this.closeExtra();let e=0;for(let t=this.open;t>=0;t--){let n=this.nodes[t].content;for(let t=n.length-1;t>=0;t--)e+=n[t].nodeSize;t&&e++}return e}findAtPoint(e,t){if(this.find)for(let n=0;n-1)return e.split(/\s*\|\s*/).some(this.matchesContext,this);let t=e.split("/"),n=this.options.context,r=!(this.isOpen||n&&n.parent.type!=this.nodes[0].type),i=-(n?n.depth+1:0)+(r?0:1),o=(e,s)=>{for(;e>=0;e--){let l=t[e];if(""==l){if(e==t.length-1||0==e)continue;for(;s>=i;s--)if(o(e-1,s))return!0;return!1}{let e=s>0||0==s&&r?this.nodes[s].type:n&&s>=i?n.node(s-i).type:null;if(!e||e.name!=l&&-1==e.groups.indexOf(l))return!1;s--}}return!0};return o(t.length-1,this.open)}textblockFromContext(){let e=this.options.context;if(e)for(let t=e.depth;t>=0;t--){let n=e.node(t).contentMatchAt(e.indexAfter(t)).defaultType;if(n&&n.isTextblock&&n.defaultAttrs)return n}for(let t in this.parser.schema.nodes){let e=this.parser.schema.nodes[t];if(e.isTextblock&&e.defaultAttrs)return e}}addPendingMark(e){let t=function(e,t){for(let n=0;n=0;n--){let r=this.nodes[n];if(r.pendingMarks.lastIndexOf(e)>-1)r.pendingMarks=e.removeFromSet(r.pendingMarks);else{r.activeMarks=e.removeFromSet(r.activeMarks);let t=r.popFromStashMark(e);t&&r.type&&r.type.allowsMarkType(t.type)&&(r.activeMarks=t.addToSet(r.activeMarks))}if(r==t)break}}}function re(e,t){return(e.matches||e.msMatchesSelector||e.webkitMatchesSelector||e.mozMatchesSelector).call(e,t)}function ie(e){let t={};for(let n in e)t[n]=e[n];return t}function oe(e,t){let n=t.schema.nodes;for(let r in n){let i=n[r];if(!i.allowsMarkType(e))continue;let o=[],s=e=>{o.push(e);for(let n=0;n{if(i.length||e.marks.length){let n=0,o=0;for(;n=0;r--){let i=this.serializeMark(e.marks[r],e.isInline,t);i&&((i.contentDOM||i.dom).appendChild(n),n=i.dom)}return n}serializeMark(e,t,n={}){let r=this.marks[e.type.name];return r&&se.renderSpec(ae(n),r(e,t))}static renderSpec(e,t,n=null){if("string"==typeof t)return{dom:e.createTextNode(t)};if(null!=t.nodeType)return{dom:t};if(t.dom&&null!=t.dom.nodeType)return t;let r,i=t[0],o=i.indexOf(" ");o>0&&(n=i.slice(0,o),i=i.slice(o+1));let s=n?e.createElementNS(n,i):e.createElement(i),l=t[1],a=1;if(l&&"object"==typeof l&&null==l.nodeType&&!Array.isArray(l)){a=2;for(let e in l)if(null!=l[e]){let t=e.indexOf(" ");t>0?s.setAttributeNS(e.slice(0,t),e.slice(t+1),l[e]):s.setAttribute(e,l[e])}}for(let c=a;ca)throw new RangeError("Content hole must be the only child of its parent node");return{dom:s,contentDOM:s}}{let{dom:t,contentDOM:o}=se.renderSpec(e,i,n);if(s.appendChild(t),o){if(r)throw new RangeError("Multiple content holes");r=o}}}return{dom:s,contentDOM:r}}static fromSchema(e){return e.cached.domSerializer||(e.cached.domSerializer=new se(this.nodesFromSchema(e),this.marksFromSchema(e)))}static nodesFromSchema(e){let t=le(e.nodes);return t.text||(t.text=e=>e.text),t}static marksFromSchema(e){return le(e.marks)}}function le(e){let t={};for(let n in e){let r=e[n].spec.toDOM;r&&(t[n]=r)}return t}function ae(e){return e.document||window.document}const ce=Math.pow(2,16);function he(e){return 65535&e}class de{constructor(e,t,n){this.pos=e,this.delInfo=t,this.recover=n}get deleted(){return(8&this.delInfo)>0}get deletedBefore(){return(5&this.delInfo)>0}get deletedAfter(){return(6&this.delInfo)>0}get deletedAcross(){return(4&this.delInfo)>0}}class ue{constructor(e,t=!1){if(this.ranges=e,this.inverted=t,!e.length&&ue.empty)return ue.empty}recover(e){let t=0,n=he(e);if(!this.inverted)for(let r=0;re)break;let a=this.ranges[s+i],c=this.ranges[s+o],h=l+a;if(e<=h){let i=l+r+((a?e==l?-1:e==h?1:t:t)<0?0:c);if(n)return i;let o=e==(t<0?l:h)?null:s/3+(e-l)*ce,d=e==l?2:e==h?1:4;return(t<0?e!=l:e!=h)&&(d|=8),new de(i,d,o)}r+=c-a}return n?e+r:new de(e+r,0,null)}touches(e,t){let n=0,r=he(t),i=this.inverted?2:1,o=this.inverted?1:2;for(let s=0;se)break;let l=this.ranges[s+i];if(e<=t+l&&s==3*r)return!0;n+=this.ranges[s+o]-l}return!1}forEach(e){let t=this.inverted?2:1,n=this.inverted?1:2;for(let r=0,i=0;r=0;t--){let r=e.getMirror(t);this.appendMap(e.maps[t].invert(),null!=r&&r>t?n-r-1:void 0)}}invert(){let e=new fe;return e.appendMappingInverted(this),e}map(e,t=1){if(this.mirror)return this._map(e,t,!0);for(let n=this.from;ni&&te.isAtom&&t.type.allowsMarkType(this.mark.type)?e.mark(this.mark.addToSet(e.marks)):e),r),t.openStart,t.openEnd);return ge.fromReplace(e,this.from,this.to,i)}invert(){return new we(this.from,this.to,this.mark)}map(e){let t=e.mapResult(this.from,1),n=e.mapResult(this.to,-1);return t.deleted&&n.deleted||t.pos>=n.pos?null:new ve(t.pos,n.pos,this.mark)}merge(e){return e instanceof ve&&e.mark.eq(this.mark)&&this.from<=e.to&&this.to>=e.from?new ve(Math.min(this.from,e.from),Math.max(this.to,e.to),this.mark):null}toJSON(){return{stepType:"addMark",mark:this.mark.toJSON(),from:this.from,to:this.to}}static fromJSON(e,t){if("number"!=typeof t.from||"number"!=typeof t.to)throw new RangeError("Invalid input for AddMarkStep.fromJSON");return new ve(t.from,t.to,e.markFromJSON(t.mark))}}me.jsonID("addMark",ve);class we extends me{constructor(e,t,n){super(),this.from=e,this.to=t,this.mark=n}apply(e){let t=e.slice(this.from,this.to),n=new c(ye(t.content,(e=>e.mark(this.mark.removeFromSet(e.marks))),e),t.openStart,t.openEnd);return ge.fromReplace(e,this.from,this.to,n)}invert(){return new ve(this.from,this.to,this.mark)}map(e){let t=e.mapResult(this.from,1),n=e.mapResult(this.to,-1);return t.deleted&&n.deleted||t.pos>=n.pos?null:new we(t.pos,n.pos,this.mark)}merge(e){return e instanceof we&&e.mark.eq(this.mark)&&this.from<=e.to&&this.to>=e.from?new we(Math.min(this.from,e.from),Math.max(this.to,e.to),this.mark):null}toJSON(){return{stepType:"removeMark",mark:this.mark.toJSON(),from:this.from,to:this.to}}static fromJSON(e,t){if("number"!=typeof t.from||"number"!=typeof t.to)throw new RangeError("Invalid input for RemoveMarkStep.fromJSON");return new we(t.from,t.to,e.markFromJSON(t.mark))}}me.jsonID("removeMark",we);class be extends me{constructor(e,t){super(),this.pos=e,this.mark=t}apply(e){let t=e.nodeAt(this.pos);if(!t)return ge.fail("No node at mark step's position");let n=t.type.create(t.attrs,null,this.mark.addToSet(t.marks));return ge.fromReplace(e,this.pos,this.pos+1,new c(r.from(n),0,t.isLeaf?0:1))}invert(e){let t=e.nodeAt(this.pos);if(t){let e=this.mark.addToSet(t.marks);if(e.length==t.marks.length){for(let n=0;nn.pos?null:new ke(t.pos,n.pos,r,i,this.slice,this.insert,this.structure)}toJSON(){let e={stepType:"replaceAround",from:this.from,to:this.to,gapFrom:this.gapFrom,gapTo:this.gapTo,insert:this.insert};return this.slice.size&&(e.slice=this.slice.toJSON()),this.structure&&(e.structure=!0),e}static fromJSON(e,t){if("number"!=typeof t.from||"number"!=typeof t.to||"number"!=typeof t.gapFrom||"number"!=typeof t.gapTo||"number"!=typeof t.insert)throw new RangeError("Invalid input for ReplaceAroundStep.fromJSON");return new ke(t.from,t.to,t.gapFrom,t.gapTo,c.fromJSON(e,t.slice),t.insert,!!t.structure)}}function Me(e,t,n){let r=e.resolve(t),i=n-t,o=r.depth;for(;i>0&&o>0&&r.indexAfter(o)==r.node(o).childCount;)o--,i--;if(i>0){let e=r.node(o).maybeChild(r.indexAfter(o));for(;i>0;){if(!e||e.isLeaf)return!0;e=e.firstChild,i--}}return!1}function Oe(e,t,n){return(0==t||e.canReplace(t,e.childCount))&&(n==e.childCount||e.canReplace(0,n))}function Ce(e){let t=e.parent.content.cutByIndex(e.startIndex,e.endIndex);for(let n=e.depth;;--n){let r=e.$from.node(n),i=e.$from.index(n),o=e.$to.indexAfter(n);if(no;c--,h--){let e=i.node(c),t=i.index(c);if(e.type.spec.isolating)return!1;let n=e.content.cutByIndex(t,e.childCount),o=r&&r[h]||e;if(o!=e&&(n=n.replaceChild(0,o.type.create(o.attrs))),!e.canReplace(t+1,e.childCount)||!o.type.validContent(n))return!1}let l=i.indexAfter(o),a=r&&r[0];return i.node(o).canReplaceWith(l,l,a?a.type:i.node(o+1).type)}function Ee(e,t){let n=e.resolve(t),r=n.index();return i=n.nodeBefore,o=n.nodeAfter,!(!i||!o||i.isLeaf||!i.canAppend(o))&&n.parent.canReplace(r,r+1);var i,o}function Ae(e,t,n=t,r=c.empty){if(t==n&&!r.size)return null;let i=e.resolve(t),o=e.resolve(n);return $e(i,o,r)?new Se(t,n,r):new Pe(i,o,r).fit()}function $e(e,t,n){return!n.openStart&&!n.openEnd&&e.start()==t.start()&&e.parent.canReplace(e.index(),t.index(),n.content)}me.jsonID("replaceAround",ke);class Pe{constructor(e,t,n){this.$from=e,this.$to=t,this.unplaced=n,this.frontier=[],this.placed=r.empty;for(let r=0;r<=e.depth;r++){let t=e.node(r);this.frontier.push({type:t.type,match:t.contentMatchAt(e.indexAfter(r))})}for(let i=e.depth;i>0;i--)this.placed=r.from(e.node(i).copy(this.placed))}get depth(){return this.frontier.length-1}fit(){for(;this.unplaced.size;){let e=this.findFittable();e?this.placeNodes(e):this.openMore()||this.dropNode()}let e=this.mustMoveInline(),t=this.placed.size-this.depth-this.$from.depth,n=this.$from,r=this.close(e<0?this.$to:n.doc.resolve(e));if(!r)return null;let i=this.placed,o=n.depth,s=r.depth;for(;o&&s&&1==i.childCount;)i=i.firstChild.content,o--,s--;let l=new c(i,o,s);return e>-1?new ke(n.pos,e,this.$to.pos,this.$to.end(),l,t):l.size||n.pos!=this.$to.pos?new Se(n.pos,r.pos,l):null}findFittable(){for(let e=1;e<=2;e++)for(let t=this.unplaced.openStart;t>=0;t--){let n,i=null;t?(i=Re(this.unplaced.content,t-1).firstChild,n=i.content):n=this.unplaced.content;let o=n.firstChild;for(let s=this.depth;s>=0;s--){let n,{type:l,match:a}=this.frontier[s],c=null;if(1==e&&(o?a.matchType(o.type)||(c=a.fillBefore(r.from(o),!1)):i&&l.compatibleContent(i.type)))return{sliceDepth:t,frontierDepth:s,parent:i,inject:c};if(2==e&&o&&(n=a.findWrapping(o.type)))return{sliceDepth:t,frontierDepth:s,parent:i,wrap:n};if(i&&a.matchType(i.type))break}}}openMore(){let{content:e,openStart:t,openEnd:n}=this.unplaced,r=Re(e,t);return!(!r.childCount||r.firstChild.isLeaf)&&(this.unplaced=new c(e,t+1,Math.max(n,r.size+t>=e.size-n?t+1:0)),!0)}dropNode(){let{content:e,openStart:t,openEnd:n}=this.unplaced,r=Re(e,t);if(r.childCount<=1&&t>0){let i=e.size-t<=t+r.size;this.unplaced=new c(Ie(e,t-1,1),t-1,i?t-1:n)}else this.unplaced=new c(Ie(e,t,1),t,n)}placeNodes({sliceDepth:e,frontierDepth:t,parent:n,inject:i,wrap:o}){for(;this.depth>t;)this.closeFrontierNode();if(o)for(let r=0;r1||0==a||e.content.size)&&(u=t,d.push(_e(e.mark(f.allowedMarks(e.marks)),1==h?a:0,h==l.childCount?p:-1)))}let m=h==l.childCount;m||(p=-1),this.placed=ze(this.placed,t,r.from(d)),this.frontier[t].match=u,m&&p<0&&n&&n.type==this.frontier[this.depth].type&&this.frontier.length>1&&this.closeFrontierNode();for(let r=0,c=l;r1&&r==this.$to.end(--n);)++r;return r}findCloseLevel(e){e:for(let t=Math.min(this.depth,e.depth);t>=0;t--){let{match:n,type:r}=this.frontier[t],i=t=0;n--){let{match:t,type:r}=this.frontier[n],i=Be(e,n,r,t,!0);if(!i||i.childCount)continue e}return{depth:t,fit:o,move:i?e.doc.resolve(e.after(t+1)):e}}}}close(e){let t=this.findCloseLevel(e);if(!t)return null;for(;this.depth>t.depth;)this.closeFrontierNode();t.fit.childCount&&(this.placed=ze(this.placed,t.depth,t.fit)),e=t.move;for(let n=t.depth+1;n<=e.depth;n++){let t=e.node(n),r=t.type.contentMatch.fillBefore(t.content,!0,e.index(n));this.openFrontierNode(t.type,t.attrs,r)}return e}openFrontierNode(e,t=null,n){let i=this.frontier[this.depth];i.match=i.match.matchType(e),this.placed=ze(this.placed,this.depth,r.from(e.create(t,n))),this.frontier.push({type:e,match:e.contentMatch})}closeFrontierNode(){let e=this.frontier.pop().match.fillBefore(r.empty,!0);e.childCount&&(this.placed=ze(this.placed,this.frontier.length,e))}}function Ie(e,t,n){return 0==t?e.cutByIndex(n,e.childCount):e.replaceChild(0,e.firstChild.copy(Ie(e.firstChild.content,t-1,n)))}function ze(e,t,n){return 0==t?e.append(n):e.replaceChild(e.childCount-1,e.lastChild.copy(ze(e.lastChild.content,t-1,n)))}function Re(e,t){for(let n=0;n1&&(i=i.replaceChild(0,_e(i.firstChild,t-1,1==i.childCount?n-1:0))),t>0&&(i=e.type.contentMatch.fillBefore(i).append(i),n<=0&&(i=i.append(e.type.contentMatch.matchFragment(i).fillBefore(r.empty,!0)))),e.copy(i)}function Be(e,t,n,r,i){let o=e.node(t),s=i?e.indexAfter(t):e.index(t);if(s==o.childCount&&!n.compatibleContent(o.type))return null;let l=r.fillBefore(o.content,!0,s);return l&&!function(e,t,n){for(let r=n;ri){let t=o.contentMatchAt(0),n=t.fillBefore(e).append(e);e=n.append(t.matchFragment(n).fillBefore(r.empty,!0))}return e}function Fe(e,t){let n=[];for(let r=Math.min(e.depth,t.depth);r>=0;r--){let i=e.start(r);if(it.pos+(t.depth-r)||e.node(r).type.spec.isolating||t.node(r).type.spec.isolating)break;(i==t.start(r)||r==e.depth&&r==t.depth&&e.parent.inlineContent&&t.parent.inlineContent&&r&&t.start(r-1)==i-1)&&n.push(r)}return n}class Le extends me{constructor(e,t,n){super(),this.pos=e,this.attr=t,this.value=n}apply(e){let t=e.nodeAt(this.pos);if(!t)return ge.fail("No node at attribute step's position");let n=Object.create(null);for(let r in t.attrs)n[r]=t.attrs[r];n[this.attr]=this.value;let i=t.type.create(n,null,t.marks);return ge.fromReplace(e,this.pos,this.pos+1,new c(r.from(i),0,t.isLeaf?0:1))}getMap(){return ue.empty}invert(e){return new Le(this.pos,this.attr,e.nodeAt(this.pos).attrs[this.attr])}map(e){let t=e.mapResult(this.pos,1);return t.deletedAfter?null:new Le(t.pos,this.attr,this.value)}toJSON(){return{stepType:"attr",pos:this.pos,attr:this.attr,value:this.value}}static fromJSON(e,t){if("number"!=typeof t.pos||"string"!=typeof t.attr)throw new RangeError("Invalid input for AttrStep.fromJSON");return new Le(t.pos,t.attr,t.value)}}me.jsonID("attr",Le);let qe=class extends Error{};qe=function e(t){let n=Error.call(this,t);return n.__proto__=e.prototype,n},(qe.prototype=Object.create(Error.prototype)).constructor=qe,qe.prototype.name="TransformError";class We{constructor(e){this.doc=e,this.steps=[],this.docs=[],this.mapping=new fe}get before(){return this.docs.length?this.docs[0]:this.doc}step(e){let t=this.maybeStep(e);if(t.failed)throw new qe(t.failed);return this}maybeStep(e){let t=e.apply(this.doc);return t.failed||this.addStep(e,t.doc),t}get docChanged(){return this.steps.length>0}addStep(e,t){this.docs.push(this.doc),this.steps.push(e),this.mapping.appendMap(e.getMap()),this.doc=t}replace(e,t=e,n=c.empty){let r=Ae(this.doc,e,t,n);return r&&this.step(r),this}replaceWith(e,t,n){return this.replace(e,t,new c(r.from(n),0,0))}delete(e,t){return this.replace(e,t,c.empty)}insert(e,t){return this.replaceWith(e,e,t)}replaceRange(e,t,n){return function(e,t,n,r){if(!r.size)return e.deleteRange(t,n);let i=e.doc.resolve(t),o=e.doc.resolve(n);if($e(i,o,r))return e.step(new Se(t,n,r));let s=Fe(i,e.doc.resolve(n));0==s[s.length-1]&&s.pop();let l=-(i.depth+1);s.unshift(l);for(let c=i.depth,f=i.pos-1;c>0;c--,f--){let e=i.node(c).type.spec;if(e.defining||e.definingAsContext||e.isolating)break;s.indexOf(c)>-1?l=c:i.before(c)==f&&s.splice(1,0,-c)}let a=s.indexOf(l),h=[],d=r.openStart;for(let c=r.content,f=0;;f++){let e=c.firstChild;if(h.push(e),f==r.openStart)break;c=e.content}for(let c=d-1;c>=0;c--){let e=h[c].type,t=Ve(e);if(t&&i.node(a).type!=e)d=c;else if(t||!e.isTextblock)break}for(let f=r.openStart;f>=0;f--){let t=(f+d+1)%(r.openStart+1),l=h[t];if(l)for(let h=0;h=0&&(e.replace(t,n,r),!(e.steps.length>u));c--){let e=s[c];e<0||(t=i.before(e),n=o.after(e))}}(this,e,t,n),this}replaceRangeWith(e,t,n){return function(e,t,n,i){if(!i.isInline&&t==n&&e.doc.resolve(t).parent.content.size){let r=function(e,t,n){let r=e.resolve(t);if(r.parent.canReplaceWith(r.index(),r.index(),n))return t;if(0==r.parentOffset)for(let i=r.depth-1;i>=0;i--){let e=r.index(i);if(r.node(i).canReplaceWith(e,e,n))return r.before(i+1);if(e>0)return null}if(r.parentOffset==r.parent.content.size)for(let i=r.depth-1;i>=0;i--){let e=r.indexAfter(i);if(r.node(i).canReplaceWith(e,e,n))return r.after(i+1);if(e0&&(n||r.node(t-1).canReplace(r.index(t-1),i.indexAfter(t-1))))return e.delete(r.before(t),i.after(t))}for(let s=1;s<=r.depth&&s<=i.depth;s++)if(t-r.start(s)==r.depth-s&&n>r.end(s)&&i.end(s)-n!=i.depth-s)return e.delete(r.before(s),n);e.delete(t,n)}(this,e,t),this}lift(e,t){return function(e,t,n){let{$from:i,$to:o,depth:s}=t,l=i.before(s+1),a=o.after(s+1),h=l,d=a,u=r.empty,f=0;for(let c=s,g=!1;c>n;c--)g||i.index(c)>0?(g=!0,u=r.from(i.node(c).copy(u)),f++):h--;let p=r.empty,m=0;for(let c=s,g=!1;c>n;c--)g||o.after(c+1)=0;l--){if(i.size){let e=n[l].type.contentMatch.matchFragment(i);if(!e||!e.validEnd)throw new RangeError("Wrapper type given to Transform.wrap does not form valid content of its parent wrapper")}i=r.from(n[l].type.create(n[l].attrs,i))}let o=t.start,s=t.end;e.step(new ke(o,s,o,s,new c(i,0,0),n.length,!0))}(this,e,t),this}setBlockType(e,t=e,n,i=null){return function(e,t,n,i,o){if(!i.isTextblock)throw new RangeError("Type given to setBlockType should be a textblock");let s=e.steps.length;e.doc.nodesBetween(t,n,((t,n)=>{if(t.isTextblock&&!t.hasMarkup(i,o)&&function(e,t,n){let r=e.resolve(t),i=r.index();return r.parent.canReplaceWith(i,i+1,n)}(e.doc,e.mapping.slice(s).map(n),i)){e.clearIncompatible(e.mapping.slice(s).map(n,1),i);let l=e.mapping.slice(s),a=l.map(n,1),h=l.map(n+t.nodeSize,1);return e.step(new ke(a,h,a+1,h-1,new c(r.from(i.create(o,null,t.marks)),0,0),1,!0)),!1}}))}(this,e,t,n,i),this}setNodeMarkup(e,t,n=null,i=[]){return function(e,t,n,i,o){let s=e.doc.nodeAt(t);if(!s)throw new RangeError("No node at given position");n||(n=s.type);let l=n.create(i,null,o||s.marks);if(s.isLeaf)return e.replaceWith(t,t+s.nodeSize,l);if(!n.validContent(s.content))throw new RangeError("Invalid content for node type "+n.name);e.step(new ke(t,t+s.nodeSize,t+1,t+s.nodeSize-1,new c(r.from(l),0,0),1,!0))}(this,e,t,n,i),this}setNodeAttribute(e,t,n){return this.step(new Le(e,t,n)),this}addNodeMark(e,t){return this.step(new be(e,t)),this}removeNodeMark(e,t){if(!(t instanceof l)){let n=this.doc.nodeAt(e);if(!n)throw new RangeError("No node at position "+e);if(!(t=t.isInSet(n.marks)))return this}return this.step(new xe(e,t)),this}split(e,t=1,n){return function(e,t,n=1,i){let o=e.doc.resolve(t),s=r.empty,l=r.empty;for(let a=o.depth,c=o.depth-n,h=n-1;a>c;a--,h--){s=r.from(o.node(a).copy(s));let e=i&&i[h];l=r.from(e?e.type.create(e.attrs,l):o.node(a).copy(l))}e.step(new Se(t,t,new c(s.append(l),n,n),!0))}(this,e,t,n),this}addMark(e,t,n){return function(e,t,n,r){let i,o,s=[],l=[];e.doc.nodesBetween(t,n,((e,a,c)=>{if(!e.isInline)return;let h=e.marks;if(!r.isInSet(h)&&c.type.allowsMarkType(r.type)){let c=Math.max(a,t),d=Math.min(a+e.nodeSize,n),u=r.addToSet(h);for(let e=0;ee.step(t))),l.forEach((t=>e.step(t)))}(this,e,t,n),this}removeMark(e,t,n){return function(e,t,n,r){let i=[],o=0;e.doc.nodesBetween(t,n,((e,s)=>{if(!e.isInline)return;o++;let l=null;if(r instanceof W){let t,n=e.marks;for(;t=r.isInSet(n);)(l||(l=[])).push(t),n=t.removeFromSet(n)}else r?r.isInSet(e.marks)&&(l=[r]):l=e.marks;if(l&&l.length){let r=Math.min(s+e.nodeSize,n);for(let e=0;ee.step(new we(t.from,t.to,t.style))))}(this,e,t,n),this}clearIncompatible(e,t,n){return function(e,t,n,i=n.contentMatch){let o=e.doc.nodeAt(t),s=[],l=t+1;for(let r=0;r=0;r--)e.step(s[r])}(this,e,t,n),this}}const Je=Object.create(null);class Ke{constructor(e,t,n){this.$anchor=e,this.$head=t,this.ranges=n||[new He(e.min(t),e.max(t))]}get anchor(){return this.$anchor.pos}get head(){return this.$head.pos}get from(){return this.$from.pos}get to(){return this.$to.pos}get $from(){return this.ranges[0].$from}get $to(){return this.ranges[0].$to}get empty(){let e=this.ranges;for(let t=0;t=0;i--){let r=t<0?nt(e.node(0),e.node(i),e.before(i+1),e.index(i),t,n):nt(e.node(0),e.node(i),e.after(i+1),e.index(i)+1,t,n);if(r)return r}return null}static near(e,t=1){return this.findFrom(e,t)||this.findFrom(e,-t)||new et(e.node(0))}static atStart(e){return nt(e,e,0,0,1)||new et(e)}static atEnd(e){return nt(e,e,e.content.size,e.childCount,-1)||new et(e)}static fromJSON(e,t){if(!t||!t.type)throw new RangeError("Invalid input for Selection.fromJSON");let n=Je[t.type];if(!n)throw new RangeError(`No selection type ${t.type} defined`);return n.fromJSON(e,t)}static jsonID(e,t){if(e in Je)throw new RangeError("Duplicate use of selection JSON ID "+e);return Je[e]=t,t.prototype.jsonID=e,t}getBookmark(){return Ge.between(this.$anchor,this.$head).getBookmark()}}Ke.prototype.visible=!0;class He{constructor(e,t){this.$from=e,this.$to=t}}let Ye=!1;function Ue(e){Ye||e.parent.inlineContent||(Ye=!0,console.warn("TextSelection endpoint not pointing into a node with inline content ("+e.parent.type.name+")"))}class Ge extends Ke{constructor(e,t=e){Ue(e),Ue(t),super(e,t)}get $cursor(){return this.$anchor.pos==this.$head.pos?this.$head:null}map(e,t){let n=e.resolve(t.map(this.head));if(!n.parent.inlineContent)return Ke.near(n);let r=e.resolve(t.map(this.anchor));return new Ge(r.parent.inlineContent?r:n,n)}replace(e,t=c.empty){if(super.replace(e,t),t==c.empty){let t=this.$from.marksAcross(this.$to);t&&e.ensureMarks(t)}}eq(e){return e instanceof Ge&&e.anchor==this.anchor&&e.head==this.head}getBookmark(){return new Ze(this.anchor,this.head)}toJSON(){return{type:"text",anchor:this.anchor,head:this.head}}static fromJSON(e,t){if("number"!=typeof t.anchor||"number"!=typeof t.head)throw new RangeError("Invalid input for TextSelection.fromJSON");return new Ge(e.resolve(t.anchor),e.resolve(t.head))}static create(e,t,n=t){let r=e.resolve(t);return new this(r,n==t?r:e.resolve(n))}static between(e,t,n){let r=e.pos-t.pos;if(n&&!r||(n=r>=0?1:-1),!t.parent.inlineContent){let e=Ke.findFrom(t,n,!0)||Ke.findFrom(t,-n,!0);if(!e)return Ke.near(t,n);t=e.$head}return e.parent.inlineContent||(0==r||(e=(Ke.findFrom(e,-n,!0)||Ke.findFrom(e,n,!0)).$anchor).posnew et(e)};function nt(e,t,n,r,i,o=!1){if(t.inlineContent)return Ge.create(e,n);for(let s=r-(i>0?0:1);i>0?s=0;s+=i){let r=t.child(s);if(r.isAtom){if(!o&&Xe.isSelectable(r))return Xe.create(e,n-(i<0?r.nodeSize:0))}else{let t=nt(e,r,n+i,i<0?r.childCount:0,i,o);if(t)return t}n+=r.nodeSize*i}return null}function rt(e,t,n){let r=e.steps.length-1;if(r{null==i&&(i=r)})),e.setSelection(Ke.near(e.doc.resolve(i),n)))}class it extends We{constructor(e){super(e.doc),this.curSelectionFor=0,this.updated=0,this.meta=Object.create(null),this.time=Date.now(),this.curSelection=e.selection,this.storedMarks=e.storedMarks}get selection(){return this.curSelectionFor0}setStoredMarks(e){return this.storedMarks=e,this.updated|=2,this}ensureMarks(e){return l.sameSet(this.storedMarks||this.selection.$from.marks(),e)||this.setStoredMarks(e),this}addStoredMark(e){return this.ensureMarks(e.addToSet(this.storedMarks||this.selection.$head.marks()))}removeStoredMark(e){return this.ensureMarks(e.removeFromSet(this.storedMarks||this.selection.$head.marks()))}get storedMarksSet(){return(2&this.updated)>0}addStep(e,t){super.addStep(e,t),this.updated=-3&this.updated,this.storedMarks=null}setTime(e){return this.time=e,this}replaceSelection(e){return this.selection.replace(this,e),this}replaceSelectionWith(e,t=!0){let n=this.selection;return t&&(e=e.mark(this.storedMarks||(n.empty?n.$from.marks():n.$from.marksAcross(n.$to)||l.none))),n.replaceWith(this,e),this}deleteSelection(){return this.selection.replace(this),this}insertText(e,t,n){let r=this.doc.type.schema;if(null==t)return e?this.replaceSelectionWith(r.text(e),!0):this.deleteSelection();{if(null==n&&(n=t),n=null==n?t:n,!e)return this.deleteRange(t,n);let i=this.storedMarks;if(!i){let e=this.doc.resolve(t);i=n==t?e.marks():e.marksAcross(this.doc.resolve(n))}return this.replaceRangeWith(t,n,r.text(e,i)),this.selection.empty||this.setSelection(Ke.near(this.selection.$to)),this}}setMeta(e,t){return this.meta["string"==typeof e?e:e.key]=t,this}getMeta(e){return this.meta["string"==typeof e?e:e.key]}get isGeneric(){for(let e in this.meta)return!1;return!0}scrollIntoView(){return this.updated|=4,this}get scrolledIntoView(){return(4&this.updated)>0}}function ot(e,t){return t&&e?e.bind(t):e}class st{constructor(e,t,n){this.name=e,this.init=ot(t.init,n),this.apply=ot(t.apply,n)}}const lt=[new st("doc",{init:e=>e.doc||e.schema.topNodeType.createAndFill(),apply:e=>e.doc}),new st("selection",{init:(e,t)=>e.selection||Ke.atStart(t.doc),apply:e=>e.selection}),new st("storedMarks",{init:e=>e.storedMarks||null,apply:(e,t,n,r)=>r.selection.$cursor?e.storedMarks:null}),new st("scrollToSelection",{init:()=>0,apply:(e,t)=>e.scrolledIntoView?t+1:t})];class at{constructor(e,t){this.schema=e,this.plugins=[],this.pluginsByKey=Object.create(null),this.fields=lt.slice(),t&&t.forEach((e=>{if(this.pluginsByKey[e.key])throw new RangeError("Adding different instances of a keyed plugin ("+e.key+")");this.plugins.push(e),this.pluginsByKey[e.key]=e,e.spec.state&&this.fields.push(new st(e.key,e.spec.state,e))}))}}class ct{constructor(e){this.config=e}get schema(){return this.config.schema}get plugins(){return this.config.plugins}apply(e){return this.applyTransaction(e).state}filterTransaction(e,t=-1){for(let n=0;ne.toJSON()))),e&&"object"==typeof e)for(let n in e){if("doc"==n||"selection"==n)throw new RangeError("The JSON fields `doc` and `selection` are reserved");let r=e[n],i=r.spec.state;i&&i.toJSON&&(t[n]=i.toJSON.call(r,this[r.key]))}return t}static fromJSON(e,t,n){if(!t)throw new RangeError("Invalid input for EditorState.fromJSON");if(!e.schema)throw new RangeError("Required config field 'schema' missing");let r=new at(e.schema,e.plugins),i=new ct(r);return r.fields.forEach((r=>{if("doc"==r.name)i.doc=N.fromJSON(e.schema,t.doc);else if("selection"==r.name)i.selection=Ke.fromJSON(i.doc,t.selection);else if("storedMarks"==r.name)t.storedMarks&&(i.storedMarks=t.storedMarks.map(e.schema.markFromJSON));else{if(n)for(let o in n){let s=n[o],l=s.spec.state;if(s.key==r.name&&l&&l.fromJSON&&Object.prototype.hasOwnProperty.call(t,o))return void(i[r.name]=l.fromJSON.call(s,e,t[o],i))}i[r.name]=r.init(e,i)}})),i}}function ht(e,t,n){for(let r in e){let i=e[r];i instanceof Function?i=i.bind(t):"handleDOMEvents"==r&&(i=ht(i,t,{})),n[r]=i}return n}class dt{constructor(e){this.spec=e,this.props={},e.props&&ht(e.props,this,this.props),this.key=e.key?e.key.key:ft("plugin")}getState(e){return e[this.key]}}const ut=Object.create(null);function ft(e){return e in ut?e+"$"+ ++ut[e]:(ut[e]=0,e+"$")}class pt{constructor(e="key"){this.key=ft(e)}get(e){return e.config.pluginsByKey[this.key]}getState(e){return e[this.key]}}const mt=function(e){for(var t=0;;t++)if(!(e=e.previousSibling))return t},gt=function(e){let t=e.assignedSlot||e.parentNode;return t&&11==t.nodeType?t.host:t};let yt=null;const vt=function(e,t,n){let r=yt||(yt=document.createRange());return r.setEnd(e,null==n?e.nodeValue.length:n),r.setStart(e,t||0),r},wt=function(e,t,n,r){return n&&(xt(e,t,n,r,-1)||xt(e,t,n,r,1))},bt=/^(img|br|input|textarea|hr)$/i;function xt(e,t,n,r,i){for(;;){if(e==n&&t==r)return!0;if(t==(i<0?0:St(e))){let n=e.parentNode;if(!n||1!=n.nodeType||kt(e)||bt.test(e.nodeName)||"false"==e.contentEditable)return!1;t=mt(e)+(i<0?0:1),e=n}else{if(1!=e.nodeType)return!1;if("false"==(e=e.childNodes[t+(i<0?-1:0)]).contentEditable)return!1;t=i<0?St(e):0}}}function St(e){return 3==e.nodeType?e.nodeValue.length:e.childNodes.length}function kt(e){let t;for(let n=e;n&&!(t=n.pmViewDesc);n=n.parentNode);return t&&t.node&&t.node.isBlock&&(t.dom==e||t.contentDOM==e)}const Mt=function(e){return e.focusNode&&wt(e.focusNode,e.focusOffset,e.anchorNode,e.anchorOffset)};function Ot(e,t){let n=document.createEvent("Event");return n.initEvent("keydown",!0,!0),n.keyCode=e,n.key=n.code=t,n}const Ct="undefined"!=typeof navigator?navigator:null,Nt="undefined"!=typeof document?document:null,Dt=Ct&&Ct.userAgent||"",Tt=/Edge\/(\d+)/.exec(Dt),Et=/MSIE \d/.exec(Dt),At=/Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(Dt),$t=!!(Et||At||Tt),Pt=Et?document.documentMode:At?+At[1]:Tt?+Tt[1]:0,It=!$t&&/gecko\/(\d+)/i.test(Dt);It&&(/Firefox\/(\d+)/.exec(Dt)||[0,0])[1];const zt=!$t&&/Chrome\/(\d+)/.exec(Dt),Rt=!!zt,_t=zt?+zt[1]:0,Bt=!$t&&!!Ct&&/Apple Computer/.test(Ct.vendor),Vt=Bt&&(/Mobile\/\w+/.test(Dt)||!!Ct&&Ct.maxTouchPoints>2),jt=Vt||!!Ct&&/Mac/.test(Ct.platform),Ft=/Android \d/.test(Dt),Lt=!!Nt&&"webkitFontSmoothing"in Nt.documentElement.style,qt=Lt?+(/\bAppleWebKit\/(\d+)/.exec(navigator.userAgent)||[0,0])[1]:0;function Wt(e){return{left:0,right:e.documentElement.clientWidth,top:0,bottom:e.documentElement.clientHeight}}function Jt(e,t){return"number"==typeof e?e:e[t]}function Kt(e){let t=e.getBoundingClientRect(),n=t.width/e.offsetWidth||1,r=t.height/e.offsetHeight||1;return{left:t.left,right:t.left+e.clientWidth*n,top:t.top,bottom:t.top+e.clientHeight*r}}function Ht(e,t,n){let r=e.someProp("scrollThreshold")||0,i=e.someProp("scrollMargin")||5,o=e.dom.ownerDocument;for(let s=n||e.dom;s;s=gt(s)){if(1!=s.nodeType)continue;let e=s,n=e==o.body,l=n?Wt(o):Kt(e),a=0,c=0;if(t.topl.bottom-Jt(r,"bottom")&&(c=t.bottom-l.bottom+Jt(i,"bottom")),t.leftl.right-Jt(r,"right")&&(a=t.right-l.right+Jt(i,"right")),a||c)if(n)o.defaultView.scrollBy(a,c);else{let n=e.scrollLeft,r=e.scrollTop;c&&(e.scrollTop+=c),a&&(e.scrollLeft+=a);let i=e.scrollLeft-n,o=e.scrollTop-r;t={left:t.left-i,top:t.top-o,right:t.right-i,bottom:t.bottom-o}}if(n)break}}function Yt(e){let t=[],n=e.ownerDocument;for(let r=e;r&&(t.push({dom:r,top:r.scrollTop,left:r.scrollLeft}),e!=n);r=gt(r));return t}function Ut(e,t){for(let n=0;n=l){s=Math.max(d.bottom,s),l=Math.min(d.top,l);let e=d.left>t.left?d.left-t.left:d.right=(d.left+d.right)/2?1:0));continue}}!n&&(t.left>=d.right&&t.top>=d.top||t.left>=d.left&&t.top>=d.bottom)&&(o=c+1)}}return n&&3==n.nodeType?function(e,t){let n=e.nodeValue.length,r=document.createRange();for(let i=0;i=(n.left+n.right)/2?1:0)}}return{node:e,offset:0}}(n,r):!n||i&&1==n.nodeType?{node:e,offset:o}:Zt(n,r)}function Xt(e,t){return e.left>=t.left-1&&e.left<=t.right+1&&e.top>=t.top-1&&e.top<=t.bottom+1}function Qt(e,t,n){let r=e.childNodes.length;if(r&&n.topt.top&&i++}n==e.dom&&i==n.childNodes.length-1&&1==n.lastChild.nodeType&&t.top>n.lastChild.getBoundingClientRect().bottom?o=e.state.doc.content.size:0!=i&&1==n.nodeType&&"BR"==n.childNodes[i-1].nodeName||(o=function(e,t,n,r){let i=-1;for(let o=t,s=!1;o!=e.dom;){let t=e.docView.nearestDesc(o,!0);if(!t)return null;if(1==t.dom.nodeType&&(t.node.isBlock&&t.parent&&!s||!t.contentDOM)){let e=t.dom.getBoundingClientRect();if(t.node.isBlock&&t.parent&&!s&&(s=!0,e.left>r.left||e.top>r.top?i=t.posBefore:(e.right-1?i:e.docView.posFromDOM(t,n,-1)}(e,n,i,t))}null==o&&(o=function(e,t,n){let{node:r,offset:i}=Zt(t,n),o=-1;if(1==r.nodeType&&!r.firstChild){let e=r.getBoundingClientRect();o=e.left!=e.right&&n.left>(e.left+e.right)/2?1:-1}return e.docView.posFromDOM(r,i,o)}(e,s,t));let l=e.docView.nearestDesc(s,!0);return{pos:o,inside:l?l.posAtStart-l.border:-1}}function tn(e,t){let n=e.getClientRects();return n.length?n[t<0?0:n.length-1]:e.getBoundingClientRect()}const nn=/[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;function rn(e,t,n){let{node:r,offset:i,atom:o}=e.docView.domFromPos(t,n<0?-1:1),s=Lt||It;if(3==r.nodeType){if(!s||!nn.test(r.nodeValue)&&(n<0?i:i!=r.nodeValue.length)){let e=i,t=i,o=n<0?1:-1;return n<0&&!i?(t++,o=-1):n>=0&&i==r.nodeValue.length?(e--,o=1):n<0?e--:t++,on(tn(vt(r,e,t),1),o<0)}{let e=tn(vt(r,i,i),n);if(It&&i&&/\s/.test(r.nodeValue[i-1])&&i=0)}if(null==o&&i&&(n<0||i==St(r))){let e=r.childNodes[i-1],t=3==e.nodeType?vt(e,St(e)-(s?0:1)):1!=e.nodeType||"BR"==e.nodeName&&e.nextSibling?null:e;if(t)return on(tn(t,1),!1)}if(null==o&&i=0)}function on(e,t){if(0==e.width)return e;let n=t?e.left:e.right;return{top:e.top,bottom:e.bottom,left:n,right:n}}function sn(e,t){if(0==e.height)return e;let n=t?e.top:e.bottom;return{top:n,bottom:n,left:e.left,right:e.right}}function ln(e,t,n){let r=e.state,i=e.root.activeElement;r!=t&&e.updateState(t),i!=e.dom&&e.focus();try{return n()}finally{r!=t&&e.updateState(r),i!=e.dom&&i&&i.focus()}}const an=/[\u0590-\u08ac]/;let cn=null,hn=null,dn=!1;function un(e,t,n){return cn==t&&hn==n?dn:(cn=t,hn=n,dn="up"==n||"down"==n?function(e,t,n){let r=t.selection,i="up"==n?r.$from:r.$to;return ln(e,t,(()=>{let{node:t}=e.docView.domFromPos(i.pos,"up"==n?-1:1);for(;;){let n=e.docView.nearestDesc(t,!0);if(!n)break;if(n.node.isBlock){t=n.contentDOM||n.dom;break}t=n.dom.parentNode}let r=rn(e,i.pos,1);for(let e=t.firstChild;e;e=e.nextSibling){let t;if(1==e.nodeType)t=e.getClientRects();else{if(3!=e.nodeType)continue;t=vt(e,0,e.nodeValue.length).getClientRects()}for(let e=0;ei.top+1&&("up"==n?r.top-i.top>2*(i.bottom-r.top):i.bottom-r.bottom>2*(r.bottom-i.top)))return!1}}return!0}))}(e,t,n):function(e,t,n){let{$head:r}=t.selection;if(!r.parent.isTextblock)return!1;let i=r.parentOffset,o=!i,s=i==r.parent.content.size,l=e.domSelection();return an.test(r.parent.textContent)&&l.modify?ln(e,t,(()=>{let{focusNode:t,focusOffset:i,anchorNode:o,anchorOffset:s}=e.domSelectionRange(),a=l.caretBidiLevel;l.modify("move",n,"character");let c=r.depth?e.docView.domAfterPos(r.before()):e.dom,{focusNode:h,focusOffset:d}=e.domSelectionRange(),u=h&&!c.contains(1==h.nodeType?h:h.parentNode)||t==h&&i==d;try{l.collapse(o,s),t&&(t!=o||i!=s)&&l.extend&&l.extend(t,i)}catch(f){}return null!=a&&(l.caretBidiLevel=a),u})):"left"==n||"backward"==n?o:s}(e,t,n))}class fn{constructor(e,t,n,r){this.parent=e,this.children=t,this.dom=n,this.contentDOM=r,this.dirty=0,n.pmViewDesc=this}matchesWidget(e){return!1}matchesMark(e){return!1}matchesNode(e,t,n){return!1}matchesHack(e){return!1}parseRule(){return null}stopEvent(e){return!1}get size(){let e=0;for(let t=0;tmt(this.contentDOM);else if(this.contentDOM&&this.contentDOM!=this.dom&&this.dom.contains(this.contentDOM))r=2&e.compareDocumentPosition(this.contentDOM);else if(this.dom.firstChild){if(0==t)for(let t=e;;t=t.parentNode){if(t==this.dom){r=!1;break}if(t.previousSibling)break}if(null==r&&t==e.childNodes.length)for(let t=e;;t=t.parentNode){if(t==this.dom){r=!0;break}if(t.nextSibling)break}}return(null==r?n>0:r)?this.posAtEnd:this.posAtStart}nearestDesc(e,t=!1){for(let n=!0,r=e;r;r=r.parentNode){let i,o=this.getDesc(r);if(o&&(!t||o.node)){if(!n||!(i=o.nodeDOM)||(1==i.nodeType?i.contains(1==e.nodeType?e:e.parentNode):i==e))return o;n=!1}}}getDesc(e){let t=e.pmViewDesc;for(let n=t;n;n=n.parent)if(n==this)return t}posFromDOM(e,t,n){for(let r=e;r;r=r.parentNode){let i=this.getDesc(r);if(i)return i.localPosFromDOM(e,t,n)}return-1}descAt(e){for(let t=0,n=0;te||t instanceof bn){r=e-i;break}i=o}if(r)return this.children[n].domFromPos(r-this.children[n].border,t);for(let i;n&&!(i=this.children[n-1]).size&&i instanceof pn&&i.side>=0;n--);if(t<=0){let e,r=!0;for(;e=n?this.children[n-1]:null,e&&e.dom.parentNode!=this.contentDOM;n--,r=!1);return e&&t&&r&&!e.border&&!e.domAtom?e.domFromPos(e.size,t):{node:this.contentDOM,offset:e?mt(e.dom)+1:0}}{let e,r=!0;for(;e=n=i&&t<=l-n.border&&n.node&&n.contentDOM&&this.contentDOM.contains(n.contentDOM))return n.parseRange(e,t,i);e=o;for(let t=s;t>0;t--){let n=this.children[t-1];if(n.size&&n.dom.parentNode==this.contentDOM&&!n.emptyChildAt(1)){r=mt(n.dom)+1;break}e-=n.size}-1==r&&(r=0)}if(r>-1&&(l>t||s==this.children.length-1)){t=l;for(let e=s+1;ef&&ot){let e=s;s=l,l=e}let n=document.createRange();n.setEnd(l.node,l.offset),n.setStart(s.node,s.offset),a.removeAllRanges(),a.addRange(n)}}ignoreMutation(e){return!this.contentDOM&&"selection"!=e.type}get contentLost(){return this.contentDOM&&this.contentDOM!=this.dom&&!this.dom.contains(this.contentDOM)}markDirty(e,t){for(let n=0,r=0;r=n:en){let r=n+i.border,s=o-i.border;if(e>=r&&t<=s)return this.dirty=e==n||t==o?2:1,void(e!=r||t!=s||!i.contentLost&&i.dom.parentNode==this.contentDOM?i.markDirty(e-r,t-r):i.dirty=3);i.dirty=i.dom!=i.contentDOM||i.dom.parentNode!=this.contentDOM||i.children.length?3:2}n=o}this.dirty=2}markParentsDirty(){let e=1;for(let t=this.parent;t;t=t.parent,e++){let n=1==e?2:1;t.dirtyi?i.parent?i.parent.posBeforeChild(i):void 0:r))),!t.type.spec.raw){if(1!=o.nodeType){let e=document.createElement("span");e.appendChild(o),o=e}o.contentEditable="false",o.classList.add("ProseMirror-widget")}super(e,[],o,null),this.widget=t,this.widget=t,i=this}matchesWidget(e){return 0==this.dirty&&e.type.eq(this.widget.type)}parseRule(){return{ignore:!0}}stopEvent(e){let t=this.widget.spec.stopEvent;return!!t&&t(e)}ignoreMutation(e){return"selection"!=e.type||this.widget.spec.ignoreSelection}destroy(){this.widget.type.destroy(this.dom),super.destroy()}get domAtom(){return!0}get side(){return this.widget.type.side}}class mn extends fn{constructor(e,t,n,r){super(e,[],t,null),this.textDOM=n,this.text=r}get size(){return this.text.length}localPosFromDOM(e,t){return e!=this.textDOM?this.posAtStart+(t?this.size:0):this.posAtStart+t}domFromPos(e){return{node:this.textDOM,offset:e}}ignoreMutation(e){return"characterData"===e.type&&e.target.nodeValue==e.oldValue}}class gn extends fn{constructor(e,t,n,r){super(e,[],n,r),this.mark=t}static create(e,t,n,r){let i=r.nodeViews[t.type.name],o=i&&i(t,r,n);return o&&o.dom||(o=se.renderSpec(document,t.type.spec.toDOM(t,n))),new gn(e,t,o.dom,o.contentDOM||o.dom)}parseRule(){return 3&this.dirty||this.mark.type.spec.reparseInView?null:{mark:this.mark.type.name,attrs:this.mark.attrs,contentElement:this.contentDOM||void 0}}matchesMark(e){return 3!=this.dirty&&this.mark.eq(e)}markDirty(e,t){if(super.markDirty(e,t),0!=this.dirty){let e=this.parent;for(;!e.node;)e=e.parent;e.dirty0&&(i=Pn(i,0,e,n));for(let s=0;ss?s.parent?s.parent.posBeforeChild(s):void 0:o),n,r),c=a&&a.dom,h=a&&a.contentDOM;if(t.isText)if(c){if(3!=c.nodeType)throw new RangeError("Text must be rendered as a DOM text node")}else c=document.createTextNode(t.text);else c||({dom:c,contentDOM:h}=se.renderSpec(document,t.type.spec.toDOM(t)));h||t.isText||"BR"==c.nodeName||(c.hasAttribute("contenteditable")||(c.contentEditable="false"),t.type.spec.draggable&&(c.draggable=!0));let d=c;return c=Dn(c,n,t),a?s=new xn(e,t,n,r,c,h||null,d,a,i,o+1):t.isText?new wn(e,t,n,r,c,d,i):new yn(e,t,n,r,c,h||null,d,i,o+1)}parseRule(){if(this.node.type.spec.reparseInView)return null;let e={node:this.node.type.name,attrs:this.node.attrs};if("pre"==this.node.type.whitespace&&(e.preserveWhitespace="full"),this.contentDOM)if(this.contentLost){for(let t=this.children.length-1;t>=0;t--){let n=this.children[t];if(this.dom.contains(n.dom.parentNode)){e.contentElement=n.dom.parentNode;break}}e.contentElement||(e.getContent=()=>r.empty)}else e.contentElement=this.contentDOM;else e.getContent=()=>this.node.content;return e}matchesNode(e,t,n){return 0==this.dirty&&e.eq(this.node)&&Tn(t,this.outerDeco)&&n.eq(this.innerDeco)}get size(){return this.node.nodeSize}get border(){return this.node.isLeaf?0:1}updateChildren(e,t){let n=this.node.inlineContent,r=t,i=e.composing?this.localCompositionInfo(e,t):null,o=i&&i.pos>-1?i:null,s=i&&i.pos<0,a=new An(this,o&&o.node,e);!function(e,t,n,r){let i=t.locals(e),o=0;if(0==i.length){for(let n=0;no;)l.push(i[s++]);let u=o+h.nodeSize;if(h.isText){let e=u;s!e.inline)):l.slice(),t.forChild(o,h),d),o=u}}(this.node,this.innerDeco,((t,i,o)=>{t.spec.marks?a.syncToMarks(t.spec.marks,n,e):t.type.side>=0&&!o&&a.syncToMarks(i==this.node.childCount?l.none:this.node.child(i).marks,n,e),a.placeWidget(t,e,r)}),((t,o,l,c)=>{let h;a.syncToMarks(t.marks,n,e),a.findNodeMatch(t,o,l,c)||s&&e.state.selection.from>r&&e.state.selection.to-1&&a.updateNodeAt(t,o,l,h,e)||a.updateNextNode(t,o,l,e,c)||a.addNode(t,o,l,e,r),r+=t.nodeSize})),a.syncToMarks([],n,e),this.node.isTextblock&&a.addTextblockHacks(),a.destroyRest(),(a.changed||2==this.dirty)&&(o&&this.protectLocalComposition(e,o),Sn(this.contentDOM,this.children,e),Vt&&function(e){if("UL"==e.nodeName||"OL"==e.nodeName){let t=e.style.cssText;e.style.cssText=t+"; list-style: square !important",window.getComputedStyle(e).listStyle,e.style.cssText=t}}(this.dom))}localCompositionInfo(e,t){let{from:n,to:r}=e.state.selection;if(!(e.state.selection instanceof Ge)||nt+this.node.content.size)return null;let i=e.domSelectionRange(),o=function(e,t){for(;;){if(3==e.nodeType)return e;if(1==e.nodeType&&t>0){if(e.childNodes.length>t&&3==e.childNodes[t].nodeType)return e.childNodes[t];t=St(e=e.childNodes[t-1])}else{if(!(1==e.nodeType&&t=n){let e=l=0&&e+t.length+l>=n)return l+e;if(n==r&&a.length>=r+t.length-l&&a.slice(r-l,r-l+t.length)==t)return r}}return-1}(this.node.content,e,n-t,r-t);return i<0?null:{node:o,pos:i,text:e}}return{node:o,pos:-1,text:""}}protectLocalComposition(e,{node:t,pos:n,text:r}){if(this.getDesc(t))return;let i=t;for(;i.parentNode!=this.contentDOM;i=i.parentNode){for(;i.previousSibling;)i.parentNode.removeChild(i.previousSibling);for(;i.nextSibling;)i.parentNode.removeChild(i.nextSibling);i.pmViewDesc&&(i.pmViewDesc=void 0)}let o=new mn(this,i,t,r);e.input.compositionNodes.push(o),this.children=Pn(this.children,n,n+r.length,e,o)}update(e,t,n,r){return!(3==this.dirty||!e.sameMarkup(this.node))&&(this.updateInner(e,t,n,r),!0)}updateInner(e,t,n,r){this.updateOuterDeco(t),this.node=e,this.innerDeco=n,this.contentDOM&&this.updateChildren(r,this.posAtStart),this.dirty=0}updateOuterDeco(e){if(Tn(e,this.outerDeco))return;let t=1!=this.nodeDOM.nodeType,n=this.dom;this.dom=Cn(this.dom,this.nodeDOM,On(this.outerDeco,this.node,t),On(e,this.node,t)),this.dom!=n&&(n.pmViewDesc=void 0,this.dom.pmViewDesc=this),this.outerDeco=e}selectNode(){1==this.nodeDOM.nodeType&&this.nodeDOM.classList.add("ProseMirror-selectednode"),!this.contentDOM&&this.node.type.spec.draggable||(this.dom.draggable=!0)}deselectNode(){1==this.nodeDOM.nodeType&&this.nodeDOM.classList.remove("ProseMirror-selectednode"),!this.contentDOM&&this.node.type.spec.draggable||this.dom.removeAttribute("draggable")}get domAtom(){return this.node.isAtom}}function vn(e,t,n,r,i){return Dn(r,t,e),new yn(void 0,e,t,n,r,r,r,i,0)}class wn extends yn{constructor(e,t,n,r,i,o,s){super(e,t,n,r,i,null,o,s,0)}parseRule(){let e=this.nodeDOM.parentNode;for(;e&&e!=this.dom&&!e.pmIsDeco;)e=e.parentNode;return{skip:e||!0}}update(e,t,n,r){return!(3==this.dirty||0!=this.dirty&&!this.inParent()||!e.sameMarkup(this.node))&&(this.updateOuterDeco(t),0==this.dirty&&e.text==this.node.text||e.text==this.nodeDOM.nodeValue||(this.nodeDOM.nodeValue=e.text,r.trackWrites==this.nodeDOM&&(r.trackWrites=null)),this.node=e,this.dirty=0,!0)}inParent(){let e=this.parent.contentDOM;for(let t=this.nodeDOM;t;t=t.parentNode)if(t==e)return!0;return!1}domFromPos(e){return{node:this.nodeDOM,offset:e}}localPosFromDOM(e,t,n){return e==this.nodeDOM?this.posAtStart+Math.min(t,this.node.text.length):super.localPosFromDOM(e,t,n)}ignoreMutation(e){return"characterData"!=e.type&&"selection"!=e.type}slice(e,t,n){let r=this.node.cut(e,t),i=document.createTextNode(r.text);return new wn(this.parent,r,this.outerDeco,this.innerDeco,i,i,n)}markDirty(e,t){super.markDirty(e,t),this.dom==this.nodeDOM||0!=e&&t!=this.nodeDOM.nodeValue.length||(this.dirty=3)}get domAtom(){return!1}}class bn extends fn{parseRule(){return{ignore:!0}}matchesHack(e){return 0==this.dirty&&this.dom.nodeName==e}get domAtom(){return!0}get ignoreForCoords(){return"IMG"==this.dom.nodeName}}class xn extends yn{constructor(e,t,n,r,i,o,s,l,a,c){super(e,t,n,r,i,o,s,a,c),this.spec=l}update(e,t,n,r){if(3==this.dirty)return!1;if(this.spec.update){let i=this.spec.update(e,t,n);return i&&this.updateInner(e,t,n,r),i}return!(!this.contentDOM&&!e.isLeaf)&&super.update(e,t,n,r)}selectNode(){this.spec.selectNode?this.spec.selectNode():super.selectNode()}deselectNode(){this.spec.deselectNode?this.spec.deselectNode():super.deselectNode()}setSelection(e,t,n,r){this.spec.setSelection?this.spec.setSelection(e,t,n):super.setSelection(e,t,n,r)}destroy(){this.spec.destroy&&this.spec.destroy(),super.destroy()}stopEvent(e){return!!this.spec.stopEvent&&this.spec.stopEvent(e)}ignoreMutation(e){return this.spec.ignoreMutation?this.spec.ignoreMutation(e):super.ignoreMutation(e)}}function Sn(e,t,n){let r=e.firstChild,i=!1;for(let o=0;o0;){let l;for(;;)if(r){let e=n.children[r-1];if(!(e instanceof gn)){l=e,r--;break}n=e,r=e.children.length}else{if(n==t)break e;r=n.parent.children.indexOf(n),n=n.parent}let a=l.node;if(a){if(a!=e.child(i-1))break;--i,o.set(l,i),s.push(l)}}return{index:i,matched:o,matches:s.reverse()}}(e.node.content,e)}destroyBetween(e,t){if(e!=t){for(let n=e;n>1,o=Math.min(i,e.length);for(;r-1)r>this.index&&(this.changed=!0,this.destroyBetween(this.index,r)),this.top=this.top.children[this.index];else{let r=gn.create(this.top,e[i],t,n);this.top.children.splice(this.index,0,r),this.top=r,this.changed=!0}this.index=0,i++}}findNodeMatch(e,t,n,r){let i,o=-1;if(r>=this.preMatch.index&&(i=this.preMatch.matches[r-this.preMatch.index]).parent==this.top&&i.matchesNode(e,t,n))o=this.top.children.indexOf(i,this.index);else for(let s=this.index,l=Math.min(this.top.children.length,s+5);s=n||h<=t?o.push(a):(cn&&o.push(a.slice(n-c,a.size,r)))}return o}function In(e,t=null){let n=e.domSelectionRange(),r=e.state.doc;if(!n.focusNode)return null;let i=e.docView.nearestDesc(n.focusNode),o=i&&0==i.size,s=e.docView.posFromDOM(n.focusNode,n.focusOffset,1);if(s<0)return null;let l,a,c=r.resolve(s);if(Mt(n)){for(l=c;i&&!i.node;)i=i.parent;let e=i.node;if(i&&e.isAtom&&Xe.isSelectable(e)&&i.parent&&(!e.isInline||!function(e,t,n){for(let r=0==t,i=t==St(e);r||i;){if(e==n)return!0;let t=mt(e);if(!(e=e.parentNode))return!1;r=r&&0==t,i=i&&t==St(e)}}(n.focusNode,n.focusOffset,i.dom))){let e=i.posBefore;a=new Xe(s==e?c:r.resolve(e))}}else{let t=e.docView.posFromDOM(n.anchorNode,n.anchorOffset,1);if(t<0)return null;l=r.resolve(t)}if(!a){a=qn(e,l,c,"pointer"==t||e.state.selection.head{n.anchorNode==r&&n.anchorOffset==i||(t.removeEventListener("selectionchange",e.input.hideSelectionGuard),setTimeout((()=>{zn(e)&&!e.state.selection.visible||e.dom.classList.remove("ProseMirror-hideselection")}),20))})}(e))}e.domObserver.setCurSelection(),e.domObserver.connectSelection()}}const _n=Bt||Rt&&_t<63;function Bn(e,t){let{node:n,offset:r}=e.docView.domFromPos(t,0),i=rr(e,t,n)))||Ge.between(t,n,r)}function Wn(e){return!(e.editable&&!e.hasFocus())&&Jn(e)}function Jn(e){let t=e.domSelectionRange();if(!t.anchorNode)return!1;try{return e.dom.contains(3==t.anchorNode.nodeType?t.anchorNode.parentNode:t.anchorNode)&&(e.editable||e.dom.contains(3==t.focusNode.nodeType?t.focusNode.parentNode:t.focusNode))}catch(n){return!1}}function Kn(e,t){let{$anchor:n,$head:r}=e.selection,i=t>0?n.max(r):n.min(r),o=i.parent.inlineContent?i.depth?e.doc.resolve(t>0?i.after():i.before()):null:i;return o&&Ke.findFrom(o,t)}function Hn(e,t){return e.dispatch(e.state.tr.setSelection(t).scrollIntoView()),!0}function Yn(e,t,n){let r=e.state.selection;if(!(r instanceof Ge)){if(r instanceof Xe&&r.node.isInline)return Hn(e,new Ge(t>0?r.$to:r.$from));{let n=Kn(e.state,t);return!!n&&Hn(e,n)}}if(!r.empty||n.indexOf("s")>-1)return!1;if(e.endOfTextblock(t>0?"right":"left")){let n=Kn(e.state,t);return!!(n&&n instanceof Xe)&&Hn(e,n)}if(!(jt&&n.indexOf("m")>-1)){let n,i=r.$head,o=i.textOffset?null:t<0?i.nodeBefore:i.nodeAfter;if(!o||o.isText)return!1;let s=t<0?i.pos-o.nodeSize:i.pos;return!!(o.isAtom||(n=e.docView.descAt(s))&&!n.contentDOM)&&(Xe.isSelectable(o)?Hn(e,new Xe(t<0?e.state.doc.resolve(i.pos-o.nodeSize):i)):!!Lt&&Hn(e,new Ge(e.state.doc.resolve(t<0?s:s+o.nodeSize))))}}function Un(e){return 3==e.nodeType?e.nodeValue.length:e.childNodes.length}function Gn(e){let t=e.pmViewDesc;return t&&0==t.size&&(e.nextSibling||"BR"!=e.nodeName)}function Zn(e){let t=e.domSelectionRange(),n=t.focusNode,r=t.focusOffset;if(!n)return;let i,o,s=!1;for(It&&1==n.nodeType&&r0){if(1!=n.nodeType)break;{let e=n.childNodes[r-1];if(Gn(e))i=n,o=--r;else{if(3!=e.nodeType)break;n=e,r=n.nodeValue.length}}}else{if(Qn(n))break;{let t=n.previousSibling;for(;t&&Gn(t);)i=n.parentNode,o=mt(t),t=t.previousSibling;if(t)n=t,r=Un(n);else{if(n=n.parentNode,n==e.dom)break;r=0}}}s?er(e,n,r):i&&er(e,i,o)}function Xn(e){let t=e.domSelectionRange(),n=t.focusNode,r=t.focusOffset;if(!n)return;let i,o,s=Un(n);for(;;)if(r{e.state==i&&Rn(e)}),50)}function tr(e,t,n){let r=e.state.selection;if(r instanceof Ge&&!r.empty||n.indexOf("s")>-1)return!1;if(jt&&n.indexOf("m")>-1)return!1;let{$from:i,$to:o}=r;if(!i.parent.inlineContent||e.endOfTextblock(t<0?"up":"down")){let n=Kn(e.state,t);if(n&&n instanceof Xe)return Hn(e,n)}if(!i.parent.inlineContent){let n=t<0?i:o,s=r instanceof et?Ke.near(n,t):Ke.findFrom(n,t);return!!s&&Hn(e,s)}return!1}function nr(e,t){if(!(e.state.selection instanceof Ge))return!0;let{$head:n,$anchor:r,empty:i}=e.state.selection;if(!n.sameParent(r))return!0;if(!i)return!1;if(e.endOfTextblock(t>0?"forward":"backward"))return!0;let o=!n.textOffset&&(t<0?n.nodeBefore:n.nodeAfter);if(o&&!o.isText){let r=e.state.tr;return t<0?r.delete(n.pos-o.nodeSize,n.pos):r.delete(n.pos,n.pos+o.nodeSize),e.dispatch(r),!0}return!1}function rr(e,t,n){e.domObserver.stop(),t.contentEditable=n,e.domObserver.start()}function ir(e,t){let n=t.keyCode,r=function(e){let t="";return e.ctrlKey&&(t+="c"),e.metaKey&&(t+="m"),e.altKey&&(t+="a"),e.shiftKey&&(t+="s"),t}(t);return 8==n||jt&&72==n&&"c"==r?nr(e,-1)||Zn(e):46==n||jt&&68==n&&"c"==r?nr(e,1)||Xn(e):13==n||27==n||(37==n||jt&&66==n&&"c"==r?Yn(e,-1,r)||Zn(e):39==n||jt&&70==n&&"c"==r?Yn(e,1,r)||Xn(e):38==n||jt&&80==n&&"c"==r?tr(e,-1,r)||Zn(e):40==n||jt&&78==n&&"c"==r?function(e){if(!Bt||e.state.selection.$head.parentOffset>0)return!1;let{focusNode:t,focusOffset:n}=e.domSelectionRange();if(t&&1==t.nodeType&&0==n&&t.firstChild&&"false"==t.firstChild.contentEditable){let n=t.firstChild;rr(e,n,"true"),setTimeout((()=>rr(e,n,"false")),20)}return!1}(e)||tr(e,1,r)||Xn(e):r==(jt?"m":"c")&&(66==n||73==n||89==n||90==n))}function or(e,t){e.someProp("transformCopied",(n=>{t=n(t,e)}));let n=[],{content:r,openStart:i,openEnd:o}=t;for(;i>1&&o>1&&1==r.childCount&&1==r.firstChild.childCount;){i--,o--;let e=r.firstChild;n.push(e.type.name,e.attrs!=e.type.defaultAttrs?e.attrs:null),r=e.content}let s=e.someProp("clipboardSerializer")||se.fromSchema(e.state.schema),l=mr(),a=l.createElement("div");a.appendChild(s.serializeFragment(r,{document:l}));let c,h=a.firstChild,d=0;for(;h&&1==h.nodeType&&(c=fr[h.nodeName.toLowerCase()]);){for(let e=c.length-1;e>=0;e--){let t=l.createElement(c[e]);for(;a.firstChild;)t.appendChild(a.firstChild);a.appendChild(t),d++}h=a.firstChild}return h&&1==h.nodeType&&h.setAttribute("data-pm-slice",`${i} ${o}${d?` -${d}`:""} ${JSON.stringify(n)}`),{dom:a,text:e.someProp("clipboardTextSerializer",(n=>n(t,e)))||t.content.textBetween(0,t.content.size,"\n\n")}}function sr(e,t,n,i,o){let s,l,a=o.parent.type.spec.code;if(!n&&!t)return null;let h=t&&(i||a||!n);if(h){if(e.someProp("transformPastedText",(n=>{t=n(t,a||i,e)})),a)return t?new c(r.from(e.state.schema.text(t.replace(/\r\n?/g,"\n"))),0,0):c.empty;let n=e.someProp("clipboardTextParser",(n=>n(t,o,i,e)));if(n)l=n;else{let n=o.marks(),{schema:r}=e.state,i=se.fromSchema(r);s=document.createElement("div"),t.split(/(?:\r\n?|\n)+/).forEach((e=>{let t=s.appendChild(document.createElement("p"));e&&t.appendChild(i.serializeNode(r.text(e,n)))}))}}else e.someProp("transformPastedHTML",(t=>{n=t(n,e)})),s=function(e){let t=/^(\s*]*>)*/.exec(e);t&&(e=e.slice(t[0].length));let n,r=mr().createElement("div"),i=/<([a-z][^>\s]+)/i.exec(e);(n=i&&fr[i[1].toLowerCase()])&&(e=n.map((e=>"<"+e+">")).join("")+e+n.map((e=>"")).reverse().join(""));if(r.innerHTML=e,n)for(let o=0;o0;r--){let e=s.firstChild;for(;e&&1!=e.nodeType;)e=e.nextSibling;if(!e)break;s=e}if(!l){let t=e.someProp("clipboardParser")||e.someProp("domParser")||H.fromSchema(e.state.schema);l=t.parseSlice(s,{preserveWhitespace:!(!h&&!u),context:o,ruleFromNode:e=>"BR"!=e.nodeName||e.nextSibling||!e.parentNode||lr.test(e.parentNode.nodeName)?null:{ignore:!0}})}if(u)l=function(e,t){if(!e.size)return e;let n,i=e.content.firstChild.type.schema;try{n=JSON.parse(t)}catch(a){return e}let{content:o,openStart:s,openEnd:l}=e;for(let c=n.length-2;c>=0;c-=2){let e=i.nodes[n[c]];if(!e||e.hasRequiredAttrs())break;o=r.from(e.create(n[c+1],o)),s++,l++}return new c(o,s,l)}(ur(l,+u[1],+u[2]),u[4]);else if(l=c.maxOpen(function(e,t){if(e.childCount<2)return e;for(let n=t.depth;n>=0;n--){let i,o=t.node(n).contentMatchAt(t.index(n)),s=[];if(e.forEach((e=>{if(!s)return;let t,n=o.findWrapping(e.type);if(!n)return s=null;if(t=s.length&&i.length&&cr(n,i,e,s[s.length-1],0))s[s.length-1]=t;else{s.length&&(s[s.length-1]=hr(s[s.length-1],i.length));let t=ar(e,n);s.push(t),o=o.matchType(t.type),i=n}})),s)return r.from(s)}return e}(l.content,o),!0),l.openStart||l.openEnd){let e=0,t=0;for(let n=l.content.firstChild;e{l=t(l,e)})),l}const lr=/^(a|abbr|acronym|b|cite|code|del|em|i|ins|kbd|label|output|q|ruby|s|samp|span|strong|sub|sup|time|u|tt|var)$/i;function ar(e,t,n=0){for(let i=t.length-1;i>=n;i--)e=t[i].create(null,r.from(e));return e}function cr(e,t,n,i,o){if(o=n&&(a=t<0?l.contentMatchAt(0).fillBefore(a,e.childCount>1||s<=o).append(a):a.append(l.contentMatchAt(l.childCount).fillBefore(r.empty,!0))),e.replaceChild(t<0?0:e.childCount-1,l.copy(a))}function ur(e,t,n){return t{for(let n in t)e.input.eventHandlers[n]||e.dom.addEventListener(n,e.input.eventHandlers[n]=t=>Sr(e,t))}))}function Sr(e,t){return e.someProp("handleDOMEvents",(n=>{let r=n[t.type];return!!r&&(r(e,t)||t.defaultPrevented)}))}function kr(e,t){if(!t.bubbles)return!0;if(t.defaultPrevented)return!1;for(let n=t.target;n!=e.dom;n=n.parentNode)if(!n||11==n.nodeType||n.pmViewDesc&&n.pmViewDesc.stopEvent(t))return!1;return!0}function Mr(e){return{left:e.clientX,top:e.clientY}}function Or(e,t,n,r,i){if(-1==r)return!1;let o=e.state.doc.resolve(r);for(let s=o.depth+1;s>0;s--)if(e.someProp(t,(t=>s>o.depth?t(e,n,o.nodeAfter,o.before(s),i,!0):t(e,n,o.node(s),o.before(s),i,!1))))return!0;return!1}function Cr(e,t,n){e.focused||e.focus();let r=e.state.tr.setSelection(t);"pointer"==n&&r.setMeta("pointer",!0),e.dispatch(r)}function Nr(e,t,n,r,i){return Or(e,"handleClickOn",t,n,r)||e.someProp("handleClick",(n=>n(e,t,r)))||(i?function(e,t){if(-1==t)return!1;let n,r,i=e.state.selection;i instanceof Xe&&(n=i.node);let o=e.state.doc.resolve(t);for(let s=o.depth+1;s>0;s--){let e=s>o.depth?o.nodeAfter:o.node(s);if(Xe.isSelectable(e)){r=n&&i.$from.depth>0&&s>=i.$from.depth&&o.before(i.$from.depth+1)==i.$from.pos?o.before(i.$from.depth):o.before(s);break}}return null!=r&&(Cr(e,Xe.create(e.state.doc,r),"pointer"),!0)}(e,n):function(e,t){if(-1==t)return!1;let n=e.state.doc.resolve(t),r=n.nodeAfter;return!!(r&&r.isAtom&&Xe.isSelectable(r))&&(Cr(e,new Xe(n),"pointer"),!0)}(e,n))}function Dr(e,t,n,r){return Or(e,"handleDoubleClickOn",t,n,r)||e.someProp("handleDoubleClick",(n=>n(e,t,r)))}function Tr(e,t,n,r){return Or(e,"handleTripleClickOn",t,n,r)||e.someProp("handleTripleClick",(n=>n(e,t,r)))||function(e,t,n){if(0!=n.button)return!1;let r=e.state.doc;if(-1==t)return!!r.inlineContent&&(Cr(e,Ge.create(r,0,r.content.size),"pointer"),!0);let i=r.resolve(t);for(let o=i.depth+1;o>0;o--){let t=o>i.depth?i.nodeAfter:i.node(o),n=i.before(o);if(t.inlineContent)Cr(e,Ge.create(r,n+1,n+1+t.content.size),"pointer");else{if(!Xe.isSelectable(t))continue;Cr(e,Xe.create(r,n),"pointer")}return!0}}(e,n,r)}function Er(e){return _r(e)}yr.keydown=(e,t)=>{let n=t;if(e.input.shiftKey=16==n.keyCode||n.shiftKey,!Pr(e,n)&&(e.input.lastKeyCode=n.keyCode,e.input.lastKeyCodeTime=Date.now(),!Ft||!Rt||13!=n.keyCode))if(229!=n.keyCode&&e.domObserver.forceFlush(),!Vt||13!=n.keyCode||n.ctrlKey||n.altKey||n.metaKey)e.someProp("handleKeyDown",(t=>t(e,n)))||ir(e,n)?n.preventDefault():br(e,"key");else{let t=Date.now();e.input.lastIOSEnter=t,e.input.lastIOSEnterFallbackTimeout=setTimeout((()=>{e.input.lastIOSEnter==t&&(e.someProp("handleKeyDown",(t=>t(e,Ot(13,"Enter")))),e.input.lastIOSEnter=0)}),200)}},yr.keyup=(e,t)=>{16==t.keyCode&&(e.input.shiftKey=!1)},yr.keypress=(e,t)=>{let n=t;if(Pr(e,n)||!n.charCode||n.ctrlKey&&!n.altKey||jt&&n.metaKey)return;if(e.someProp("handleKeyPress",(t=>t(e,n))))return void n.preventDefault();let r=e.state.selection;if(!(r instanceof Ge&&r.$from.sameParent(r.$to))){let t=String.fromCharCode(n.charCode);/[\r\n]/.test(t)||e.someProp("handleTextInput",(n=>n(e,r.$from.pos,r.$to.pos,t)))||e.dispatch(e.state.tr.insertText(t).scrollIntoView()),n.preventDefault()}};const Ar=jt?"metaKey":"ctrlKey";gr.mousedown=(e,t)=>{let n=t;e.input.shiftKey=n.shiftKey;let r=Er(e),i=Date.now(),o="singleClick";i-e.input.lastClick.time<500&&function(e,t){let n=t.x-e.clientX,r=t.y-e.clientY;return n*n+r*r<100}(n,e.input.lastClick)&&!n[Ar]&&("singleClick"==e.input.lastClick.type?o="doubleClick":"doubleClick"==e.input.lastClick.type&&(o="tripleClick")),e.input.lastClick={time:i,x:n.clientX,y:n.clientY,type:o};let s=e.posAtCoords(Mr(n));s&&("singleClick"==o?(e.input.mouseDown&&e.input.mouseDown.done(),e.input.mouseDown=new $r(e,s,n,!!r)):("doubleClick"==o?Dr:Tr)(e,s.pos,s.inside,n)?n.preventDefault():br(e,"pointer"))};class $r{constructor(e,t,n,r){let i,o;if(this.view=e,this.pos=t,this.event=n,this.flushed=r,this.delayedSelectionSync=!1,this.mightDrag=null,this.startDoc=e.state.doc,this.selectNode=!!n[Ar],this.allowDefault=n.shiftKey,t.inside>-1)i=e.state.doc.nodeAt(t.inside),o=t.inside;else{let n=e.state.doc.resolve(t.pos);i=n.parent,o=n.depth?n.before():0}const s=r?null:n.target,l=s?e.docView.nearestDesc(s,!0):null;this.target=l?l.dom:null;let{selection:a}=e.state;(0==n.button&&i.type.spec.draggable&&!1!==i.type.spec.selectable||a instanceof Xe&&a.from<=o&&a.to>o)&&(this.mightDrag={node:i,pos:o,addAttr:!(!this.target||this.target.draggable),setUneditable:!(!this.target||!It||this.target.hasAttribute("contentEditable"))}),this.target&&this.mightDrag&&(this.mightDrag.addAttr||this.mightDrag.setUneditable)&&(this.view.domObserver.stop(),this.mightDrag.addAttr&&(this.target.draggable=!0),this.mightDrag.setUneditable&&setTimeout((()=>{this.view.input.mouseDown==this&&this.target.setAttribute("contentEditable","false")}),20),this.view.domObserver.start()),e.root.addEventListener("mouseup",this.up=this.up.bind(this)),e.root.addEventListener("mousemove",this.move=this.move.bind(this)),br(e,"pointer")}done(){this.view.root.removeEventListener("mouseup",this.up),this.view.root.removeEventListener("mousemove",this.move),this.mightDrag&&this.target&&(this.view.domObserver.stop(),this.mightDrag.addAttr&&this.target.removeAttribute("draggable"),this.mightDrag.setUneditable&&this.target.removeAttribute("contentEditable"),this.view.domObserver.start()),this.delayedSelectionSync&&setTimeout((()=>Rn(this.view))),this.view.input.mouseDown=null}up(e){if(this.done(),!this.view.dom.contains(e.target))return;let t=this.pos;this.view.state.doc!=this.startDoc&&(t=this.view.posAtCoords(Mr(e))),this.updateAllowDefault(e),this.allowDefault||!t?br(this.view,"pointer"):Nr(this.view,t.pos,t.inside,e,this.selectNode)?e.preventDefault():0==e.button&&(this.flushed||Bt&&this.mightDrag&&!this.mightDrag.node.isAtom||Rt&&!this.view.state.selection.visible&&Math.min(Math.abs(t.pos-this.view.state.selection.from),Math.abs(t.pos-this.view.state.selection.to))<=2)?(Cr(this.view,Ke.near(this.view.state.doc.resolve(t.pos)),"pointer"),e.preventDefault()):br(this.view,"pointer")}move(e){this.updateAllowDefault(e),br(this.view,"pointer"),0==e.buttons&&this.done()}updateAllowDefault(e){!this.allowDefault&&(Math.abs(this.event.x-e.clientX)>4||Math.abs(this.event.y-e.clientY)>4)&&(this.allowDefault=!0)}}function Pr(e,t){return!!e.composing||!!(Bt&&Math.abs(t.timeStamp-e.input.compositionEndedAt)<500)&&(e.input.compositionEndedAt=-2e8,!0)}gr.touchstart=e=>{e.input.lastTouch=Date.now(),Er(e),br(e,"pointer")},gr.touchmove=e=>{e.input.lastTouch=Date.now(),br(e,"pointer")},gr.contextmenu=e=>Er(e);const Ir=Ft?5e3:-1;function zr(e,t){clearTimeout(e.input.composingTimeout),t>-1&&(e.input.composingTimeout=setTimeout((()=>_r(e)),t))}function Rr(e){for(e.composing&&(e.input.composing=!1,e.input.compositionEndedAt=function(){let e=document.createEvent("Event");return e.initEvent("event",!0,!0),e.timeStamp}());e.input.compositionNodes.length>0;)e.input.compositionNodes.pop().markParentsDirty()}function _r(e,t=!1){if(!(Ft&&e.domObserver.flushingSoon>=0)){if(e.domObserver.forceFlush(),Rr(e),t||e.docView&&e.docView.dirty){let t=In(e);return t&&!t.eq(e.state.selection)?e.dispatch(e.state.tr.setSelection(t)):e.updateState(e.state),!0}return!1}}yr.compositionstart=yr.compositionupdate=e=>{if(!e.composing){e.domObserver.flush();let{state:t}=e,n=t.selection.$from;if(t.selection.empty&&(t.storedMarks||!n.textOffset&&n.parentOffset&&n.nodeBefore.marks.some((e=>!1===e.type.spec.inclusive))))e.markCursor=e.state.storedMarks||n.marks(),_r(e,!0),e.markCursor=null;else if(_r(e),It&&t.selection.empty&&n.parentOffset&&!n.textOffset&&n.nodeBefore.marks.length){let t=e.domSelectionRange();for(let n=t.focusNode,r=t.focusOffset;n&&1==n.nodeType&&0!=r;){let t=r<0?n.lastChild:n.childNodes[r-1];if(!t)break;if(3==t.nodeType){e.domSelection().collapse(t,t.nodeValue.length);break}n=t,r=-1}}e.input.composing=!0}zr(e,Ir)},yr.compositionend=(e,t)=>{e.composing&&(e.input.composing=!1,e.input.compositionEndedAt=t.timeStamp,zr(e,20))};const Br=$t&&Pt<15||Vt&&qt<604;function Vr(e,t,n,r,i){let o=sr(e,t,n,r,e.state.selection.$from);if(e.someProp("handlePaste",(t=>t(e,i,o||c.empty))))return!0;if(!o)return!1;let s=function(e){return 0==e.openStart&&0==e.openEnd&&1==e.content.childCount?e.content.firstChild:null}(o),l=s?e.state.tr.replaceSelectionWith(s,e.input.shiftKey):e.state.tr.replaceSelection(o);return e.dispatch(l.scrollIntoView().setMeta("paste",!0).setMeta("uiEvent","paste")),!0}gr.copy=yr.cut=(e,t)=>{let n=t,r=e.state.selection,i="cut"==n.type;if(r.empty)return;let o=Br?null:n.clipboardData,s=r.content(),{dom:l,text:a}=or(e,s);o?(n.preventDefault(),o.clearData(),o.setData("text/html",l.innerHTML),o.setData("text/plain",a)):function(e,t){if(!e.dom.parentNode)return;let n=e.dom.parentNode.appendChild(document.createElement("div"));n.appendChild(t),n.style.cssText="position: fixed; left: -10000px; top: 10px";let r=getSelection(),i=document.createRange();i.selectNodeContents(t),e.dom.blur(),r.removeAllRanges(),r.addRange(i),setTimeout((()=>{n.parentNode&&n.parentNode.removeChild(n),e.focus()}),50)}(e,l),i&&e.dispatch(e.state.tr.deleteSelection().scrollIntoView().setMeta("uiEvent","cut"))},yr.paste=(e,t)=>{let n=t;if(e.composing&&!Ft)return;let r=Br?null:n.clipboardData;r&&Vr(e,r.getData("text/plain"),r.getData("text/html"),e.input.shiftKey,n)?n.preventDefault():function(e,t){if(!e.dom.parentNode)return;let n=e.input.shiftKey||e.state.selection.$from.parent.type.spec.code,r=e.dom.parentNode.appendChild(document.createElement(n?"textarea":"div"));n||(r.contentEditable="true"),r.style.cssText="position: fixed; left: -10000px; top: 10px",r.focus(),setTimeout((()=>{e.focus(),r.parentNode&&r.parentNode.removeChild(r),n?Vr(e,r.value,null,e.input.shiftKey,t):Vr(e,r.textContent,r.innerHTML,e.input.shiftKey,t)}),50)}(e,n)};class jr{constructor(e,t){this.slice=e,this.move=t}}const Fr=jt?"altKey":"ctrlKey";gr.dragstart=(e,t)=>{let n=t,r=e.input.mouseDown;if(r&&r.done(),!n.dataTransfer)return;let i=e.state.selection,o=i.empty?null:e.posAtCoords(Mr(n));if(o&&o.pos>=i.from&&o.pos<=(i instanceof Xe?i.to-1:i.to));else if(r&&r.mightDrag)e.dispatch(e.state.tr.setSelection(Xe.create(e.state.doc,r.mightDrag.pos)));else if(n.target&&1==n.target.nodeType){let t=e.docView.nearestDesc(n.target,!0);t&&t.node.type.spec.draggable&&t!=e.docView&&e.dispatch(e.state.tr.setSelection(Xe.create(e.state.doc,t.posBefore)))}let s=e.state.selection.content(),{dom:l,text:a}=or(e,s);n.dataTransfer.clearData(),n.dataTransfer.setData(Br?"Text":"text/html",l.innerHTML),n.dataTransfer.effectAllowed="copyMove",Br||n.dataTransfer.setData("text/plain",a),e.dragging=new jr(s,!n[Fr])},gr.dragend=e=>{let t=e.dragging;window.setTimeout((()=>{e.dragging==t&&(e.dragging=null)}),50)},yr.dragover=yr.dragenter=(e,t)=>t.preventDefault(),yr.drop=(e,t)=>{let n=t,r=e.dragging;if(e.dragging=null,!n.dataTransfer)return;let i=e.posAtCoords(Mr(n));if(!i)return;let o=e.state.doc.resolve(i.pos),s=r&&r.slice;s?e.someProp("transformPasted",(t=>{s=t(s,e)})):s=sr(e,n.dataTransfer.getData(Br?"Text":"text/plain"),Br?null:n.dataTransfer.getData("text/html"),!1,o);let l=!(!r||n[Fr]);if(e.someProp("handleDrop",(t=>t(e,n,s||c.empty,l))))return void n.preventDefault();if(!s)return;n.preventDefault();let a=s?function(e,t,n){let r=e.resolve(t);if(!n.content.size)return t;let i=n.content;for(let o=0;o=0;e--){let t=e==r.depth?0:r.pos<=(r.start(e+1)+r.end(e+1))/2?-1:1,n=r.index(e)+(t>0?1:0),s=r.node(e),l=!1;if(1==o)l=s.canReplace(n,n,i);else{let e=s.contentMatchAt(n).findWrapping(i.firstChild.type);l=e&&s.canReplaceWith(n,n,e[0])}if(l)return 0==t?r.pos:t<0?r.before(e+1):r.after(e+1)}return null}(e.state.doc,o.pos,s):o.pos;null==a&&(a=o.pos);let h=e.state.tr;l&&h.deleteSelection();let d=h.mapping.map(a),u=0==s.openStart&&0==s.openEnd&&1==s.content.childCount,f=h.doc;if(u?h.replaceRangeWith(d,d,s.content.firstChild):h.replaceRange(d,d,s),h.doc.eq(f))return;let p=h.doc.resolve(d);if(u&&Xe.isSelectable(s.content.firstChild)&&p.nodeAfter&&p.nodeAfter.sameMarkup(s.content.firstChild))h.setSelection(new Xe(p));else{let t=h.mapping.map(a);h.mapping.maps[h.mapping.maps.length-1].forEach(((e,n,r,i)=>t=i)),h.setSelection(qn(e,p,h.doc.resolve(t)))}e.focus(),e.dispatch(h.setMeta("uiEvent","drop"))},gr.focus=e=>{e.input.lastFocus=Date.now(),e.focused||(e.domObserver.stop(),e.dom.classList.add("ProseMirror-focused"),e.domObserver.start(),e.focused=!0,setTimeout((()=>{e.docView&&e.hasFocus()&&!e.domObserver.currentSelection.eq(e.domSelectionRange())&&Rn(e)}),20))},gr.blur=(e,t)=>{let n=t;e.focused&&(e.domObserver.stop(),e.dom.classList.remove("ProseMirror-focused"),e.domObserver.start(),n.relatedTarget&&e.dom.contains(n.relatedTarget)&&e.domObserver.currentSelection.clear(),e.focused=!1)},gr.beforeinput=(e,t)=>{if(Rt&&Ft&&"deleteContentBackward"==t.inputType){e.domObserver.flushSoon();let{domChangeCount:t}=e.input;setTimeout((()=>{if(e.input.domChangeCount!=t)return;if(e.dom.blur(),e.focus(),e.someProp("handleKeyDown",(t=>t(e,Ot(8,"Backspace")))))return;let{$cursor:n}=e.state.selection;n&&n.pos>0&&e.dispatch(e.state.tr.delete(n.pos-1,n.pos).scrollIntoView())}),50)}};for(let os in yr)gr[os]=yr[os];function Lr(e,t){if(e==t)return!0;for(let n in e)if(e[n]!==t[n])return!1;for(let n in t)if(!(n in e))return!1;return!0}class qr{constructor(e,t){this.toDOM=e,this.spec=t||Yr,this.side=this.spec.side||0}map(e,t,n,r){let{pos:i,deleted:o}=e.mapResult(t.from+r,this.side<0?-1:1);return o?null:new Kr(i-n,i-n,this)}valid(){return!0}eq(e){return this==e||e instanceof qr&&(this.spec.key&&this.spec.key==e.spec.key||this.toDOM==e.toDOM&&Lr(this.spec,e.spec))}destroy(e){this.spec.destroy&&this.spec.destroy(e)}}class Wr{constructor(e,t){this.attrs=e,this.spec=t||Yr}map(e,t,n,r){let i=e.map(t.from+r,this.spec.inclusiveStart?-1:1)-n,o=e.map(t.to+r,this.spec.inclusiveEnd?1:-1)-n;return i>=o?null:new Kr(i,o,this)}valid(e,t){return t.from=e&&(!i||i(s.spec))&&n.push(s.copy(s.from+r,s.to+r))}for(let o=0;oe){let s=this.children[o]+1;this.children[o+2].findInner(e-s,t-s,n,r+s,i)}}map(e,t,n){return this==Gr||0==e.maps.length?this:this.mapInner(e,t,0,0,n||Yr)}mapInner(e,t,n,r,i){let o;for(let s=0;s{let s=o-r-(n-t);for(let a=0;ao+h-e)continue;let c=l[a]+h-e;n>=c?l[a+1]=t<=c?-2:-1:r>=i&&s&&(l[a]+=s,l[a+1]+=s)}e+=s})),h=n.maps[c].map(h,-1)}let a=!1;for(let c=0;c=r.content.size){a=!0;continue}let d=n.map(e[c+1]+o,-1)-i,{index:u,offset:f}=r.content.findIndex(h),p=r.maybeChild(u);if(p&&f==h&&f+p.nodeSize==d){let r=l[c+2].mapInner(n,p,t+1,e[c]+o+1,s);r!=Gr?(l[c]=h,l[c+1]=d,l[c+2]=r):(l[c+1]=-2,a=!0)}else a=!0}if(a){let a=function(e,t,n,r,i,o,s){function l(e,t){for(let o=0;o{let s,l=o+n;if(s=Qr(t,e,l)){for(r||(r=this.children.slice());io&&t.to=e){this.children[s]==e&&(n=this.children[s+2]);break}let i=e+1,o=i+t.content.size;for(let s=0;si&&e.type instanceof Wr){let t=Math.max(i,e.from)-i,n=Math.min(o,e.to)-i;tn.map(e,t,Yr)));return Zr.from(n)}forChild(e,t){if(t.isLeaf)return Ur.empty;let n=[];for(let r=0;re instanceof Ur))?e:e.reduce(((e,t)=>e.concat(t instanceof Ur?t:t.members)),[]))}}}function Xr(e,t){if(!t||!e.length)return e;let n=[];for(let r=0;rn&&o.to{let l=Qr(e,t,s+n);if(l){o=!0;let e=ti(l,t,n+s+1,r);e!=Gr&&i.push(s,s+t.nodeSize,e)}}));let s=Xr(o?ei(e):e,-n).sort(ni);for(let l=0;l0;)t++;e.splice(t,0,n)}function oi(e){let t=[];return e.someProp("decorations",(n=>{let r=n(e.state);r&&r!=Gr&&t.push(r)})),e.cursorWrapper&&t.push(Ur.create(e.state.doc,[e.cursorWrapper.deco])),Zr.from(t)}const si={childList:!0,characterData:!0,characterDataOldValue:!0,attributes:!0,attributeOldValue:!0,subtree:!0},li=$t&&Pt<=11;class ai{constructor(){this.anchorNode=null,this.anchorOffset=0,this.focusNode=null,this.focusOffset=0}set(e){this.anchorNode=e.anchorNode,this.anchorOffset=e.anchorOffset,this.focusNode=e.focusNode,this.focusOffset=e.focusOffset}clear(){this.anchorNode=this.focusNode=null}eq(e){return e.anchorNode==this.anchorNode&&e.anchorOffset==this.anchorOffset&&e.focusNode==this.focusNode&&e.focusOffset==this.focusOffset}}class ci{constructor(e,t){this.view=e,this.handleDOMChange=t,this.queue=[],this.flushingSoon=-1,this.observer=null,this.currentSelection=new ai,this.onCharData=null,this.suppressingSelectionUpdates=!1,this.observer=window.MutationObserver&&new window.MutationObserver((e=>{for(let t=0;t"childList"==e.type&&e.removedNodes.length||"characterData"==e.type&&e.oldValue.length>e.target.nodeValue.length))?this.flushSoon():this.flush()})),li&&(this.onCharData=e=>{this.queue.push({target:e.target,type:"characterData",oldValue:e.prevValue}),this.flushSoon()}),this.onSelectionChange=this.onSelectionChange.bind(this)}flushSoon(){this.flushingSoon<0&&(this.flushingSoon=window.setTimeout((()=>{this.flushingSoon=-1,this.flush()}),20))}forceFlush(){this.flushingSoon>-1&&(window.clearTimeout(this.flushingSoon),this.flushingSoon=-1,this.flush())}start(){this.observer&&(this.observer.takeRecords(),this.observer.observe(this.view.dom,si)),this.onCharData&&this.view.dom.addEventListener("DOMCharacterDataModified",this.onCharData),this.connectSelection()}stop(){if(this.observer){let e=this.observer.takeRecords();if(e.length){for(let t=0;tthis.flush()),20)}this.observer.disconnect()}this.onCharData&&this.view.dom.removeEventListener("DOMCharacterDataModified",this.onCharData),this.disconnectSelection()}connectSelection(){this.view.dom.ownerDocument.addEventListener("selectionchange",this.onSelectionChange)}disconnectSelection(){this.view.dom.ownerDocument.removeEventListener("selectionchange",this.onSelectionChange)}suppressSelectionUpdates(){this.suppressingSelectionUpdates=!0,setTimeout((()=>this.suppressingSelectionUpdates=!1),50)}onSelectionChange(){if(Wn(this.view)){if(this.suppressingSelectionUpdates)return Rn(this.view);if($t&&Pt<=11&&!this.view.state.selection.empty){let e=this.view.domSelectionRange();if(e.focusNode&&wt(e.focusNode,e.focusOffset,e.anchorNode,e.anchorOffset))return this.flushSoon()}this.flush()}}setCurSelection(){this.currentSelection.set(this.view.domSelectionRange())}ignoreSelectionChange(e){if(!e.focusNode)return!0;let t,n=new Set;for(let i=e.focusNode;i;i=gt(i))n.add(i);for(let i=e.anchorNode;i;i=gt(i))if(n.has(i)){t=i;break}let r=t&&this.view.docView.nearestDesc(t);return r&&r.ignoreMutation({type:"selection",target:3==t.nodeType?t.parentNode:t})?(this.setCurSelection(),!0):void 0}flush(){let{view:e}=this;if(!e.docView||this.flushingSoon>-1)return;let t=this.observer?this.observer.takeRecords():[];this.queue.length&&(t=this.queue.concat(t),this.queue.length=0);let n=e.domSelectionRange(),r=!this.suppressingSelectionUpdates&&!this.currentSelection.eq(n)&&Wn(e)&&!this.ignoreSelectionChange(n),i=-1,o=-1,s=!1,l=[];if(e.editable)for(let c=0;c1){let e=l.filter((e=>"BR"==e.nodeName));if(2==e.length){let t=e[0],n=e[1];t.parentNode&&t.parentNode.parentNode==n.parentNode?n.remove():t.remove()}}let a=null;i<0&&r&&e.input.lastFocus>Date.now()-200&&Math.max(e.input.lastTouch,e.input.lastClick.time)-1||r)&&(i>-1&&(e.docView.markDirty(i,o),function(e){if(hi.has(e))return;if(hi.set(e,null),-1!==["normal","nowrap","pre-line"].indexOf(getComputedStyle(e.dom).whiteSpace)){if(e.requiresGeckoHackNode=It,di)return;console.warn("ProseMirror expects the CSS white-space property to be set, preferably to 'pre-wrap'. It is recommended to load style/prosemirror.css from the prosemirror-view package."),di=!0}}(e)),this.handleDOMChange(i,o,s,l),e.docView&&e.docView.dirty?e.updateState(e.state):this.currentSelection.eq(n)||Rn(e),this.currentSelection.set(n))}registerMutation(e,t){if(t.indexOf(e.target)>-1)return null;let n=this.view.docView.nearestDesc(e.target);if("attributes"==e.type&&(n==this.view.docView||"contenteditable"==e.attributeName||"style"==e.attributeName&&!e.oldValue&&!e.target.getAttribute("style")))return null;if(!n||n.ignoreMutation(e))return null;if("childList"==e.type){for(let n=0;nDate.now()-50?e.input.lastSelectionOrigin:null,n=In(e,t);if(n&&!e.state.selection.eq(n)){if(Rt&&Ft&&13===e.input.lastKeyCode&&Date.now()-100t(e,Ot(13,"Enter")))))return;let r=e.state.tr.setSelection(n);"pointer"==t?r.setMeta("pointer",!0):"key"==t&&r.scrollIntoView(),e.dispatch(r)}return}let s=e.state.doc.resolve(t),l=s.sharedDepth(n);t=s.before(l+1),n=e.state.doc.resolve(n).after(l+1);let a,c,h=e.state.selection,d=function(e,t,n){let r,{node:i,fromOffset:o,toOffset:s,from:l,to:a}=e.docView.parseRange(t,n),c=e.domSelectionRange(),h=c.anchorNode;if(h&&e.dom.contains(1==h.nodeType?h:h.parentNode)&&(r=[{node:h,offset:c.anchorOffset}],Mt(c)||r.push({node:c.focusNode,offset:c.focusOffset})),Rt&&8===e.input.lastKeyCode)for(let g=s;g>o;g--){let e=i.childNodes[g-1],t=e.pmViewDesc;if("BR"==e.nodeName&&!t){s=g;break}if(!t||t.size)break}let d=e.state.doc,u=e.someProp("domParser")||H.fromSchema(e.state.schema),f=d.resolve(l),p=null,m=u.parse(i,{topNode:f.parent,topMatch:f.parent.contentMatchAt(f.index()),topOpen:!0,from:o,to:s,preserveWhitespace:"pre"!=f.parent.type.whitespace||"full",findPositions:r,ruleFromNode:ui,context:f});if(r&&null!=r[0].pos){let e=r[0].pos,t=r[1]&&r[1].pos;null==t&&(t=e),p={anchor:e+l,head:t+l}}return{doc:m,sel:p,from:l,to:a}}(e,t,n),u=e.state.doc,f=u.slice(d.from,d.to);8===e.input.lastKeyCode&&Date.now()-100=s?o-r:0,l=o+(l-s),s=o}else if(l=l?o-r:0,s=o+(s-l),l=o}return{start:o,endA:s,endB:l}}(f.content,d.doc.content,d.from,a,c);if((Vt&&e.input.lastIOSEnter>Date.now()-225||Ft)&&o.some((e=>1==e.nodeType&&!fi.test(e.nodeName)))&&(!p||p.endA>=p.endB)&&e.someProp("handleKeyDown",(t=>t(e,Ot(13,"Enter")))))return void(e.input.lastIOSEnter=0);if(!p){if(!(i&&h instanceof Ge&&!h.empty&&h.$head.sameParent(h.$anchor))||e.composing||d.sel&&d.sel.anchor!=d.sel.head){if(d.sel){let t=mi(e,e.state.doc,d.sel);t&&!t.eq(e.state.selection)&&e.dispatch(e.state.tr.setSelection(t))}return}p={start:h.from,endA:h.to,endB:h.to}}if(Rt&&e.cursorWrapper&&d.sel&&d.sel.anchor==e.cursorWrapper.deco.from&&d.sel.head==d.sel.anchor){let e=p.endB-p.start;d.sel={anchor:d.sel.anchor+e,head:d.sel.anchor+e}}e.input.domChangeCount++,e.state.selection.frome.state.selection.from&&p.start<=e.state.selection.from+2&&e.state.selection.from>=d.from?p.start=e.state.selection.from:p.endA=e.state.selection.to-2&&e.state.selection.to<=d.to&&(p.endB+=e.state.selection.to-p.endA,p.endA=e.state.selection.to)),$t&&Pt<=11&&p.endB==p.start+1&&p.endA==p.start&&p.start>d.from&&"  "==d.doc.textBetween(p.start-d.from-1,p.start-d.from+1)&&(p.start--,p.endA--,p.endB--);let m,g=d.doc.resolveNoCache(p.start-d.from),y=d.doc.resolveNoCache(p.endB-d.from),v=u.resolve(p.start),w=g.sameParent(y)&&g.parent.inlineContent&&v.end()>=p.endA;if((Vt&&e.input.lastIOSEnter>Date.now()-225&&(!w||o.some((e=>"DIV"==e.nodeName||"P"==e.nodeName)))||!w&&g.post(e,Ot(13,"Enter")))))return void(e.input.lastIOSEnter=0);if(e.state.selection.anchor>p.start&&function(e,t,n,r,i){if(!r.parent.isTextblock||n-t<=i.pos-r.pos||gi(r,!0,!1)n||gi(s,!0,!1)t(e,Ot(8,"Backspace")))))return void(Ft&&Rt&&e.domObserver.suppressSelectionUpdates());Rt&&Ft&&p.endB==p.start&&(e.input.lastAndroidDelete=Date.now()),Ft&&!w&&g.start()!=y.start()&&0==y.parentOffset&&g.depth==y.depth&&d.sel&&d.sel.anchor==d.sel.head&&d.sel.head==p.endA&&(p.endB-=2,y=d.doc.resolveNoCache(p.endB-d.from),setTimeout((()=>{e.someProp("handleKeyDown",(function(t){return t(e,Ot(13,"Enter"))}))}),20));let b,x,S,k=p.start,M=p.endA;if(w)if(g.pos==y.pos)$t&&Pt<=11&&0==g.parentOffset&&(e.domObserver.suppressSelectionUpdates(),setTimeout((()=>Rn(e)),20)),b=e.state.tr.delete(k,M),x=u.resolve(p.start).marksAcross(u.resolve(p.endA));else if(p.endA==p.endB&&(S=function(e,t){let n,i,o,s=e.firstChild.marks,l=t.firstChild.marks,a=s,c=l;for(let r=0;re.mark(i.addToSet(e.marks));else{if(0!=a.length||1!=c.length)return null;i=c[0],n="remove",o=e=>e.mark(i.removeFromSet(e.marks))}let h=[];for(let r=0;rn(e,k,M,t))))return;b=e.state.tr.insertText(t,k,M)}if(b||(b=e.state.tr.replace(k,M,d.doc.slice(p.start-d.from,p.endB-d.from))),d.sel){let t=mi(e,b.doc,d.sel);t&&!(Rt&&Ft&&e.composing&&t.empty&&(p.start!=p.endB||e.input.lastAndroidDeletet.content.size?null:qn(e,t.resolve(n.anchor),t.resolve(n.head))}function gi(e,t,n){let r=e.depth,i=t?e.end():e.pos;for(;r>0&&(t||e.indexAfter(r)==e.node(r).childCount);)r--,i++,t=!1;if(n){let t=e.node(r).maybeChild(e.indexAfter(r));for(;t&&!t.isLeaf;)t=t.firstChild,i++}return i}class yi{constructor(e,t){this._root=null,this.focused=!1,this.trackWrites=null,this.mounted=!1,this.markCursor=null,this.cursorWrapper=null,this.lastSelectedViewDesc=void 0,this.input=new wr,this.prevDirectPlugins=[],this.pluginViews=[],this.requiresGeckoHackNode=!1,this.dragging=null,this._props=t,this.state=t.state,this.directPlugins=t.plugins||[],this.directPlugins.forEach(Si),this.dispatch=this.dispatch.bind(this),this.dom=e&&e.mount||document.createElement("div"),e&&(e.appendChild?e.appendChild(this.dom):"function"==typeof e?e(this.dom):e.mount&&(this.mounted=!0)),this.editable=bi(this),wi(this),this.nodeViews=xi(this),this.docView=vn(this.state.doc,vi(this),oi(this),this.dom,this),this.domObserver=new ci(this,((e,t,n,r)=>pi(this,e,t,n,r))),this.domObserver.start(),function(e){for(let t in gr){let n=gr[t];e.dom.addEventListener(t,e.input.eventHandlers[t]=t=>{!kr(e,t)||Sr(e,t)||!e.editable&&t.type in yr||n(e,t)},vr[t]?{passive:!0}:void 0)}Bt&&e.dom.addEventListener("input",(()=>null)),xr(e)}(this),this.updatePluginViews()}get composing(){return this.input.composing}get props(){if(this._props.state!=this.state){let e=this._props;this._props={};for(let t in e)this._props[t]=e[t];this._props.state=this.state}return this._props}update(e){e.handleDOMEvents!=this._props.handleDOMEvents&&xr(this);let t=this._props;this._props=e,e.plugins&&(e.plugins.forEach(Si),this.directPlugins=e.plugins),this.updateStateInner(e.state,t)}setProps(e){let t={};for(let n in this._props)t[n]=this._props[n];t.state=this.state;for(let n in e)t[n]=e[n];this.update(t)}updateState(e){this.updateStateInner(e,this._props)}updateStateInner(e,t){let n=this.state,r=!1,i=!1;e.storedMarks&&this.composing&&(Rr(this),i=!0),this.state=e;let o=n.plugins!=e.plugins||this._props.plugins!=t.plugins;if(o||this._props.plugins!=t.plugins||this._props.nodeViews!=t.nodeViews){let e=xi(this);(function(e,t){let n=0,r=0;for(let i in e){if(e[i]!=t[i])return!0;n++}for(let i in t)r++;return n!=r})(e,this.nodeViews)&&(this.nodeViews=e,r=!0)}(o||t.handleDOMEvents!=this._props.handleDOMEvents)&&xr(this),this.editable=bi(this),wi(this);let s=oi(this),l=vi(this),a=n.plugins==e.plugins||n.doc.eq(e.doc)?e.scrollToSelection>n.scrollToSelection?"to selection":"preserve":"reset",c=r||!this.docView.matchesNode(e.doc,l,s);!c&&e.selection.eq(n.selection)||(i=!0);let h="preserve"==a&&i&&null==this.dom.style.overflowAnchor&&function(e){let t,n,r=e.dom.getBoundingClientRect(),i=Math.max(0,r.top);for(let o=(r.left+r.right)/2,s=i+1;s=i-20){t=r,n=l.top;break}}return{refDOM:t,refTop:n,stack:Yt(e.dom)}}(this);if(i){this.domObserver.stop();let t=c&&($t||Rt)&&!this.composing&&!n.selection.empty&&!e.selection.empty&&function(e,t){let n=Math.min(e.$anchor.sharedDepth(e.head),t.$anchor.sharedDepth(t.head));return e.$anchor.start(n)!=t.$anchor.start(n)}(n.selection,e.selection);if(c){let n=Rt?this.trackWrites=this.domSelectionRange().focusNode:null;!r&&this.docView.update(e.doc,l,s,this)||(this.docView.updateOuterDeco([]),this.docView.destroy(),this.docView=vn(e.doc,l,s,this.dom,this)),n&&!this.trackWrites&&(t=!0)}t||!(this.input.mouseDown&&this.domObserver.currentSelection.eq(this.domSelectionRange())&&function(e){let t=e.docView.domFromPos(e.state.selection.anchor,0),n=e.domSelectionRange();return wt(t.node,t.offset,n.anchorNode,n.anchorOffset)}(this))?Rn(this,t):(Fn(this,e.selection),this.domObserver.setCurSelection()),this.domObserver.start()}this.updatePluginViews(n),"reset"==a?this.dom.scrollTop=0:"to selection"==a?this.scrollToSelection():h&&function({refDOM:e,refTop:t,stack:n}){let r=e?e.getBoundingClientRect().top:0;Ut(n,0==r?0:r-t)}(h)}scrollToSelection(){let e=this.domSelectionRange().focusNode;if(this.someProp("handleScrollToSelection",(e=>e(this))));else if(this.state.selection instanceof Xe){let t=this.docView.domAfterPos(this.state.selection.from);1==t.nodeType&&Ht(this,t.getBoundingClientRect(),e)}else Ht(this,this.coordsAtPos(this.state.selection.head,1),e)}destroyPluginViews(){let e;for(;e=this.pluginViews.pop();)e.destroy&&e.destroy()}updatePluginViews(e){if(e&&e.plugins==this.state.plugins&&this.directPlugins==this.prevDirectPlugins)for(let t=0;tt.ownerDocument.getSelection()),this._root=t;return e||document}posAtCoords(e){return en(this,e)}coordsAtPos(e,t=1){return rn(this,e,t)}domAtPos(e,t=0){return this.docView.domFromPos(e,t)}nodeDOM(e){let t=this.docView.descAt(e);return t?t.nodeDOM:null}posAtDOM(e,t,n=-1){let r=this.docView.posFromDOM(e,t,n);if(null==r)throw new RangeError("DOM position not inside the editor");return r}endOfTextblock(e,t){return un(this,t||this.state,e)}pasteHTML(e,t){return Vr(this,"",e,!1,t||new ClipboardEvent("paste"))}pasteText(e,t){return Vr(this,e,null,!0,t||new ClipboardEvent("paste"))}destroy(){this.docView&&(!function(e){e.domObserver.stop();for(let t in e.input.eventHandlers)e.dom.removeEventListener(t,e.input.eventHandlers[t]);clearTimeout(e.input.composingTimeout),clearTimeout(e.input.lastIOSEnterFallbackTimeout)}(this),this.destroyPluginViews(),this.mounted?(this.docView.update(this.state.doc,[],oi(this),this),this.dom.textContent=""):this.dom.parentNode&&this.dom.parentNode.removeChild(this.dom),this.docView.destroy(),this.docView=null)}get isDestroyed(){return null==this.docView}dispatchEvent(e){return function(e,t){Sr(e,t)||!gr[t.type]||!e.editable&&t.type in yr||gr[t.type](e,t)}(this,e)}dispatch(e){let t=this._props.dispatchTransaction;t?t.call(this,e):this.updateState(this.state.apply(e))}domSelectionRange(){return Bt&&11===this.root.nodeType&&function(e){let t=e.activeElement;for(;t&&t.shadowRoot;)t=t.shadowRoot.activeElement;return t}(this.dom.ownerDocument)==this.dom?function(e){let t;function n(e){e.preventDefault(),e.stopImmediatePropagation(),t=e.getTargetRanges()[0]}e.dom.addEventListener("beforeinput",n,!0),document.execCommand("indent"),e.dom.removeEventListener("beforeinput",n,!0);let r=t.startContainer,i=t.startOffset,o=t.endContainer,s=t.endOffset,l=e.domAtPos(e.state.selection.anchor);return wt(l.node,l.offset,o,s)&&([r,i,o,s]=[o,s,r,i]),{anchorNode:r,anchorOffset:i,focusNode:o,focusOffset:s}}(this):this.domSelection()}domSelection(){return this.root.getSelection()}}function vi(e){let t=Object.create(null);return t.class="ProseMirror",t.contenteditable=String(e.editable),t.translate="no",e.someProp("attributes",(n=>{if("function"==typeof n&&(n=n(e.state)),n)for(let e in n)"class"==e&&(t.class+=" "+n[e]),"style"==e?t.style=(t.style?t.style+";":"")+n[e]:t[e]||"contenteditable"==e||"nodeName"==e||(t[e]=String(n[e]))})),[Kr.node(0,e.state.doc.content.size,t)]}function wi(e){if(e.markCursor){let t=document.createElement("img");t.className="ProseMirror-separator",t.setAttribute("mark-placeholder","true"),t.setAttribute("alt",""),e.cursorWrapper={dom:t,deco:Kr.widget(e.state.selection.head,t,{raw:!0,marks:e.markCursor})}}else e.cursorWrapper=null}function bi(e){return!e.someProp("editable",(t=>!1===t(e.state)))}function xi(e){let t=Object.create(null);function n(e){for(let n in e)Object.prototype.hasOwnProperty.call(t,n)||(t[n]=e[n])}return e.someProp("nodeViews",n),e.someProp("markViews",n),t}function Si(e){if(e.spec.state||e.spec.filterTransaction||e.spec.appendTransaction)throw new RangeError("Plugins passed directly to the view must not have a state component")}for(var ki={8:"Backspace",9:"Tab",10:"Enter",12:"NumLock",13:"Enter",16:"Shift",17:"Control",18:"Alt",20:"CapsLock",27:"Escape",32:" ",33:"PageUp",34:"PageDown",35:"End",36:"Home",37:"ArrowLeft",38:"ArrowUp",39:"ArrowRight",40:"ArrowDown",44:"PrintScreen",45:"Insert",46:"Delete",59:";",61:"=",91:"Meta",92:"Meta",106:"*",107:"+",108:",",109:"-",110:".",111:"/",144:"NumLock",145:"ScrollLock",160:"Shift",161:"Shift",162:"Control",163:"Control",164:"Alt",165:"Alt",173:"-",186:";",187:"=",188:",",189:"-",190:".",191:"/",192:"`",219:"[",220:"\\",221:"]",222:"'"},Mi={48:")",49:"!",50:"@",51:"#",52:"$",53:"%",54:"^",55:"&",56:"*",57:"(",59:":",61:"+",173:"_",186:":",187:"+",188:"<",189:"_",190:">",191:"?",192:"~",219:"{",220:"|",221:"}",222:'"'},Oi="undefined"!=typeof navigator&&/Chrome\/(\d+)/.exec(navigator.userAgent),Ci="undefined"!=typeof navigator&&/Mac/.test(navigator.platform),Ni="undefined"!=typeof navigator&&/MSIE \d|Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(navigator.userAgent),Di=Ci||Oi&&+Oi[1]<57,Ti=0;Ti<10;Ti++)ki[48+Ti]=ki[96+Ti]=String(Ti);for(Ti=1;Ti<=24;Ti++)ki[Ti+111]="F"+Ti;for(Ti=65;Ti<=90;Ti++)ki[Ti]=String.fromCharCode(Ti+32),Mi[Ti]=String.fromCharCode(Ti);for(var Ei in ki)Mi.hasOwnProperty(Ei)||(Mi[Ei]=ki[Ei]);const Ai="undefined"!=typeof navigator&&/Mac|iP(hone|[oa]d)/.test(navigator.platform);function $i(e){let t,n,r,i,o=e.split(/-(?!$)/),s=o[o.length-1];"Space"==s&&(s=" ");for(let l=0;l127)&&(r=ki[n.keyCode])&&r!=i){let i=t[Pi(r,n)];if(i&&i(e.state,e.dispatch,e))return!0}}return!1}}const Ri=(e,t)=>!e.selection.empty&&(t&&t(e.tr.deleteSelection().scrollIntoView()),!0);function _i(e,t,n=!1){for(let r=e;r;r="start"==t?r.firstChild:r.lastChild){if(r.isTextblock)return!0;if(n&&1!=r.childCount)return!1}return!1}function Bi(e){if(!e.parent.type.spec.isolating)for(let t=e.depth-1;t>=0;t--){if(e.index(t)>0)return e.doc.resolve(e.before(t+1));if(e.node(t).type.spec.isolating)break}return null}function Vi(e){if(!e.parent.type.spec.isolating)for(let t=e.depth-1;t>=0;t--){let n=e.node(t);if(e.index(t)+1{let{$head:n,$anchor:r}=e.selection;if(!n.parent.type.spec.code||!n.sameParent(r))return!1;let i=n.node(-1),o=n.indexAfter(-1),s=ji(i.contentMatchAt(o));if(!s||!i.canReplaceWith(o,o,s))return!1;if(t){let r=n.after(),i=e.tr.replaceWith(r,r,s.createAndFill());i.setSelection(Ke.near(i.doc.resolve(r),1)),t(i.scrollIntoView())}return!0};const Li=(e,t)=>{let{$from:n,$to:r}=e.selection;if(e.selection instanceof Xe&&e.selection.node.isBlock)return!(!n.parentOffset||!Te(e.doc,n.pos)||(t&&t(e.tr.split(n.pos).scrollIntoView()),0));if(!n.parent.isBlock)return!1;if(t){let i=r.parentOffset==r.parent.content.size,o=e.tr;(e.selection instanceof Ge||e.selection instanceof et)&&o.deleteSelection();let s=0==n.depth?null:ji(n.node(-1).contentMatchAt(n.indexAfter(-1))),l=qi&&qi(r.parent,i),a=l?[l]:i&&s?[{type:s}]:void 0,c=Te(o.doc,o.mapping.map(n.pos),1,a);if(a||c||!Te(o.doc,o.mapping.map(n.pos),1,s?[{type:s}]:void 0)||(s&&(a=[{type:s}]),c=!0),c&&(o.split(o.mapping.map(n.pos),1,a),!i&&!n.parentOffset&&n.parent.type!=s)){let e=o.mapping.map(n.before()),t=o.doc.resolve(e);s&&n.node(-1).canReplaceWith(t.index(),t.index()+1,s)&&o.setNodeMarkup(o.mapping.map(n.before()),s)}t(o.scrollIntoView())}return!0};var qi;function Wi(e,t,n){let i,o,s=t.nodeBefore,l=t.nodeAfter;if(s.type.spec.isolating||l.type.spec.isolating)return!1;if(function(e,t,n){let r=t.nodeBefore,i=t.nodeAfter,o=t.index();return!(!(r&&i&&r.type.compatibleContent(i.type))||(!r.content.size&&t.parent.canReplace(o-1,o)?(n&&n(e.tr.delete(t.pos-r.nodeSize,t.pos).scrollIntoView()),0):!t.parent.canReplace(o,o+1)||!i.isTextblock&&!Ee(e.doc,t.pos)||(n&&n(e.tr.clearIncompatible(t.pos,r.type,r.contentMatchAt(r.childCount)).join(t.pos).scrollIntoView()),0)))}(e,t,n))return!0;let a=t.parent.canReplace(t.index(),t.index()+1);if(a&&(i=(o=s.contentMatchAt(s.childCount)).findWrapping(l.type))&&o.matchType(i[0]||l.type).validEnd){if(n){let o=t.pos+l.nodeSize,a=r.empty;for(let e=i.length-1;e>=0;e--)a=r.from(i[e].create(null,a));a=r.from(s.copy(a));let h=e.tr.step(new ke(t.pos-1,o,t.pos,o,new c(a,1,0),i.length,!0)),d=o+2*i.length;Ee(h.doc,d)&&h.join(d),n(h.scrollIntoView())}return!0}let h=Ke.findFrom(t,1),d=h&&h.$from.blockRange(h.$to),u=d&&Ce(d);if(null!=u&&u>=t.depth)return n&&n(e.tr.lift(d,u).scrollIntoView()),!0;if(a&&_i(l,"start",!0)&&_i(s,"end")){let i=s,o=[];for(;o.push(i),!i.isTextblock;)i=i.lastChild;let a=l,h=1;for(;!a.isTextblock;a=a.firstChild)h++;if(i.canReplace(i.childCount,i.childCount,a.content)){if(n){let i=r.empty;for(let e=o.length-1;e>=0;e--)i=r.from(o[e].copy(i));n(e.tr.step(new ke(t.pos-o.length,t.pos+l.nodeSize,t.pos+h,t.pos+l.nodeSize-h,new c(i,o.length,0),0,!0)).scrollIntoView())}return!0}}return!1}function Ji(e){return function(t,n){let r=t.selection,i=e<0?r.$from:r.$to,o=i.depth;for(;i.node(o).isInline;){if(!o)return!1;o--}return!!i.node(o).isTextblock&&(n&&n(t.tr.setSelection(Ge.create(t.doc,e<0?i.start(o):i.end(o)))),!0)}}const Ki=Ji(-1),Hi=Ji(1);function Yi(e,t=null){return function(n,r){let i=!1;for(let o=0;o{if(i)return!1;if(r.isTextblock&&!r.hasMarkup(e,t))if(r.type==e)i=!0;else{let t=n.doc.resolve(o),r=t.index();i=t.parent.canReplaceWith(r,r+1,e)}}))}if(!i)return!1;if(r){let i=n.tr;for(let r=0;r{if(s)return!1;s=e.inlineContent&&e.type.allowsMarkType(n)})),s)return!0}return!1}(n.doc,s,e))return!1;if(r)if(o)e.isInSet(n.storedMarks||o.marks())?r(n.tr.removeStoredMark(e)):r(n.tr.addStoredMark(e.create(t)));else{let i=!1,o=n.tr;for(let t=0;!i&&t{let r=function(e,t){let{$cursor:n}=e.selection;return!n||(t?!t.endOfTextblock("backward",e):n.parentOffset>0)?null:n}(e,n);if(!r)return!1;let i=Bi(r);if(!i){let n=r.blockRange(),i=n&&Ce(n);return null!=i&&(t&&t(e.tr.lift(n,i).scrollIntoView()),!0)}let o=i.nodeBefore;if(!o.type.spec.isolating&&Wi(e,i,t))return!0;if(0==r.parent.content.size&&(_i(o,"end")||Xe.isSelectable(o))){let n=Ae(e.doc,r.before(),r.after(),c.empty);if(n&&n.slice.size{let{$head:r,empty:i}=e.selection,o=r;if(!i)return!1;if(r.parent.isTextblock){if(n?!n.endOfTextblock("backward",e):r.parentOffset>0)return!1;o=Bi(r)}let s=o&&o.nodeBefore;return!(!s||!Xe.isSelectable(s))&&(t&&t(e.tr.setSelection(Xe.create(e.doc,o.pos-s.nodeSize)).scrollIntoView()),!0)})),Xi=Gi(Ri,((e,t,n)=>{let r=function(e,t){let{$cursor:n}=e.selection;return!n||(t?!t.endOfTextblock("forward",e):n.parentOffset{let{$head:r,empty:i}=e.selection,o=r;if(!i)return!1;if(r.parent.isTextblock){if(n?!n.endOfTextblock("forward",e):r.parentOffset{let{$head:n,$anchor:r}=e.selection;return!(!n.parent.type.spec.code||!n.sameParent(r))&&(t&&t(e.tr.insertText("\n").scrollIntoView()),!0)}),((e,t)=>{let n=e.selection,{$from:r,$to:i}=n;if(n instanceof et||r.parent.inlineContent||i.parent.inlineContent)return!1;let o=ji(i.parent.contentMatchAt(i.indexAfter()));if(!o||!o.isTextblock)return!1;if(t){let n=(!r.parentOffset&&i.index(){let{$cursor:n}=e.selection;if(!n||n.parent.content.size)return!1;if(n.depth>1&&n.after()!=n.end(-1)){let r=n.before();if(Te(e.doc,r))return t&&t(e.tr.split(r).scrollIntoView()),!0}let r=n.blockRange(),i=r&&Ce(r);return null!=i&&(t&&t(e.tr.lift(r,i).scrollIntoView()),!0)}),Li),"Mod-Enter":Fi,Backspace:Zi,"Mod-Backspace":Zi,"Shift-Backspace":Zi,Delete:Xi,"Mod-Delete":Xi,"Mod-a":(e,t)=>(t&&t(e.tr.setSelection(new et(e.doc))),!0)},eo={"Ctrl-h":Qi.Backspace,"Alt-Backspace":Qi["Mod-Backspace"],"Ctrl-d":Qi.Delete,"Ctrl-Alt-Backspace":Qi["Mod-Delete"],"Alt-Delete":Qi["Mod-Delete"],"Alt-d":Qi["Mod-Delete"],"Ctrl-a":Ki,"Ctrl-e":Hi};for(let os in Qi)eo[os]=Qi[os];const to=("undefined"!=typeof navigator?/Mac|iP(hone|[oa]d)/.test(navigator.platform):!("undefined"==typeof os||!os.platform)&&"darwin"==os.platform())?eo:Qi;class no{constructor(e,t){var n;this.match=e,this.match=e,this.handler="string"==typeof t?(n=t,function(e,t,r,i){let o=n;if(t[1]){let e=t[0].lastIndexOf(t[1]);o+=t[0].slice(e+t[1].length);let n=(r+=e)-i;n>0&&(o=t[0].slice(e-n,e)+o,r=i)}return e.tr.insertText(o,r,i)}):t}}const ro=500;function io({rules:e}){let t=new dt({state:{init:()=>null,apply(e,t){let n=e.getMeta(this);return n||(e.selectionSet||e.docChanged?null:t)}},props:{handleTextInput:(n,r,i,o)=>oo(n,r,i,o,e,t),handleDOMEvents:{compositionend:n=>{setTimeout((()=>{let{$cursor:r}=n.state.selection;r&&oo(n,r.pos,r.pos,"",e,t)}))}}},isInputRules:!0});return t}function oo(e,t,n,r,i,o){if(e.composing)return!1;let s=e.state,l=s.doc.resolve(t);if(l.parent.type.spec.code)return!1;let a=l.parent.textBetween(Math.max(0,l.parentOffset-ro),l.parentOffset,null,"")+r;for(let c=0;c{let n=e.plugins;for(let r=0;r=0;e--)n.step(r.steps[e].invert(r.docs[e]));if(i.text){let t=n.doc.resolve(i.from).marks();n.replaceWith(i.from,i.to,e.schema.text(i.text,t))}else n.delete(i.from,i.to);t(n)}return!0}}return!1};function lo(e,t,n=null,r){return new no(e,((e,i,o,s)=>{let l=n instanceof Function?n(i):n,a=e.tr.delete(o,s),c=a.doc.resolve(o).blockRange(),h=c&&Ne(c,t,l);if(!h)return null;a.wrap(c,h);let d=a.doc.resolve(o-1).nodeBefore;return d&&d.type==t&&Ee(a.doc,o-1)&&(!r||r(i,d))&&a.join(o-1),a}))}function ao(e,t,n=null){return new no(e,((e,r,i,o)=>{let s=e.doc.resolve(i),l=n instanceof Function?n(r):n;return s.node(-1).canReplaceWith(s.index(-1),s.indexAfter(-1),t)?e.tr.delete(i,o).setBlockType(i,i,t,l):null}))}const co=["ol",0],ho=["ul",0],uo=["li",0],fo={attrs:{order:{default:1}},parseDOM:[{tag:"ol",getAttrs:e=>({order:e.hasAttribute("start")?+e.getAttribute("start"):1})}],toDOM:e=>1==e.attrs.order?co:["ol",{start:e.attrs.order},0]},po={parseDOM:[{tag:"ul"}],toDOM:()=>ho},mo={parseDOM:[{tag:"li"}],toDOM:()=>uo,defining:!0};function go(e,t){let n={};for(let r in e)n[r]=e[r];for(let r in t)n[r]=t[r];return n}function yo(e,t,n){return e.append({ordered_list:go(fo,{content:"list_item+",group:n}),bullet_list:go(po,{content:"list_item+",group:n}),list_item:go(mo,{content:t})})}function vo(e,t=null){return function(n,i){let{$from:o,$to:s}=n.selection,l=o.blockRange(s),a=!1,h=l;if(!l)return!1;if(l.depth>=2&&o.node(l.depth-1).type.compatibleContent(e)&&0==l.startIndex){if(0==o.index(l.depth-1))return!1;let e=n.doc.resolve(l.start-2);h=new O(e,e,l.depth),l.endIndex=0;c--)s=r.from(n[c].type.create(n[c].attrs,s));e.step(new ke(t.start-(i?2:0),t.end,t.start,t.end,new c(s,0,0),n.length,!0));let l=0;for(let r=0;r=i.depth-3;e--)o=r.from(i.node(e).copy(o));let l=i.indexAfter(-1){if(d>-1)return!1;e.isTextblock&&0==e.content.size&&(d=t+1)})),d>-1&&h.setSelection(Ke.near(h.doc.resolve(d))),n(h.scrollIntoView())}return!0}let a=o.pos==i.end()?l.contentMatchAt(0).defaultType:null,h=t.tr.delete(i.pos,o.pos),d=a?[null,{type:a}]:void 0;return!!Te(h.doc,i.pos,2,d)&&(n&&n(h.split(i.pos,2,d).scrollIntoView()),!0)}}function bo(e){return function(t,n){let{$from:i,$to:o}=t.selection,s=i.blockRange(o,(t=>t.childCount>0&&t.firstChild.type==e));return!!s&&(!n||(i.node(s.depth-1).type==e?function(e,t,n,i){let o=e.tr,s=i.end,l=i.$to.end(i.depth);sm;c--)r-=o.child(c).nodeSize,i.delete(r-1,r+1);let s=i.doc.resolve(n.start),l=s.nodeAfter;if(i.mapping.map(n.end)!=n.start+s.nodeAfter.nodeSize)return!1;let a=0==n.startIndex,h=n.endIndex==o.childCount,d=s.node(-1),u=s.index(-1);if(!d.canReplace(u+(a?0:1),u+1,l.content.append(h?r.empty:r.from(o))))return!1;let f=s.pos,p=f+l.nodeSize;return i.step(new ke(f-(a?1:0),p+(h?1:0),f+1,p-1,new c((a?r.empty:r.from(o.copy(r.empty))).append(h?r.empty:r.from(o.copy(r.empty))),a?0:1,h?0:1),a?0:1)),t(i.scrollIntoView()),!0}(t,n,s)))}}function xo(e){return function(t,n){let{$from:i,$to:o}=t.selection,s=i.blockRange(o,(t=>t.childCount>0&&t.firstChild.type==e));if(!s)return!1;let l=s.startIndex;if(0==l)return!1;let a=s.parent,h=a.child(l-1);if(h.type!=e)return!1;if(n){let i=h.lastChild&&h.lastChild.type==a.type,o=r.from(i?e.create():null),l=new c(r.from(e.create(null,r.from(a.type.create(null,o)))),i?3:1,0),d=s.start,u=s.end;n(t.tr.step(new ke(d-(i?3:1),u,d,u,l,1,!0)).scrollIntoView())}return!0}}var So=200,ko=function(){};ko.prototype.append=function(e){return e.length?(e=ko.from(e),!this.length&&e||e.length=t?ko.empty:this.sliceInner(Math.max(0,e),Math.min(this.length,t))},ko.prototype.get=function(e){if(!(e<0||e>=this.length))return this.getInner(e)},ko.prototype.forEach=function(e,t,n){void 0===t&&(t=0),void 0===n&&(n=this.length),t<=n?this.forEachInner(e,t,n,0):this.forEachInvertedInner(e,t,n,0)},ko.prototype.map=function(e,t,n){void 0===t&&(t=0),void 0===n&&(n=this.length);var r=[];return this.forEach((function(t,n){return r.push(e(t,n))}),t,n),r},ko.from=function(e){return e instanceof ko?e:e&&e.length?new Mo(e):ko.empty};var Mo=function(e){function t(t){e.call(this),this.values=t}e&&(t.__proto__=e),t.prototype=Object.create(e&&e.prototype),t.prototype.constructor=t;var n={length:{configurable:!0},depth:{configurable:!0}};return t.prototype.flatten=function(){return this.values},t.prototype.sliceInner=function(e,n){return 0==e&&n==this.length?this:new t(this.values.slice(e,n))},t.prototype.getInner=function(e){return this.values[e]},t.prototype.forEachInner=function(e,t,n,r){for(var i=t;i=n;i--)if(!1===e(this.values[i],r+i))return!1},t.prototype.leafAppend=function(e){if(this.length+e.length<=So)return new t(this.values.concat(e.flatten()))},t.prototype.leafPrepend=function(e){if(this.length+e.length<=So)return new t(e.flatten().concat(this.values))},n.length.get=function(){return this.values.length},n.depth.get=function(){return 0},Object.defineProperties(t.prototype,n),t}(ko);ko.empty=new Mo([]);var Oo=function(e){function t(t,n){e.call(this),this.left=t,this.right=n,this.length=t.length+n.length,this.depth=Math.max(t.depth,n.depth)+1}return e&&(t.__proto__=e),t.prototype=Object.create(e&&e.prototype),t.prototype.constructor=t,t.prototype.flatten=function(){return this.left.flatten().concat(this.right.flatten())},t.prototype.getInner=function(e){return ei&&!1===this.right.forEachInner(e,Math.max(t-i,0),Math.min(this.length,n)-i,r+i))&&void 0)},t.prototype.forEachInvertedInner=function(e,t,n,r){var i=this.left.length;return!(t>i&&!1===this.right.forEachInvertedInner(e,t-i,Math.max(n,i)-i,r+i))&&(!(n=n?this.right.slice(e-n,t-n):this.left.slice(e,n).append(this.right.slice(0,t-n))},t.prototype.leafAppend=function(e){var n=this.right.leafAppend(e);if(n)return new t(this.left,n)},t.prototype.leafPrepend=function(e){var n=this.left.leafPrepend(e);if(n)return new t(n,this.right)},t.prototype.appendInner=function(e){return this.left.depth>=Math.max(this.right.depth,e.depth)+1?new t(this.left,new t(this.right,e)):new t(this,e)},t}(ko),Co=ko;class No{constructor(e,t){this.items=e,this.eventCount=t}popEvent(e,t){if(0==this.eventCount)return null;let n,r,i=this.items.length;for(;;i--){if(this.items.get(i-1).selection){--i;break}}t&&(n=this.remapping(i,this.items.length),r=n.maps.length);let o,s,l=e.tr,a=[],c=[];return this.items.forEach(((e,t)=>{if(!e.step)return n||(n=this.remapping(i,t+1),r=n.maps.length),r--,void c.push(e);if(n){c.push(new Do(e.map));let t,i=e.step.map(n.slice(r));i&&l.maybeStep(i).doc&&(t=l.mapping.maps[l.mapping.maps.length-1],a.push(new Do(t,void 0,void 0,a.length+c.length))),r--,t&&n.appendMap(t,r)}else l.maybeStep(e.step);return e.selection?(o=n?e.selection.map(n.slice(r)):e.selection,s=new No(this.items.slice(0,i).append(c.reverse().concat(a)),this.eventCount-1),!1):void 0}),this.items.length,0),{remaining:s,transform:l,selection:o}}addTransform(e,t,n,r){let i=[],o=this.eventCount,s=this.items,l=!r&&s.length?s.get(s.length-1):null;for(let c=0;cEo&&(s=function(e,t){let n;return e.forEach(((e,r)=>{if(e.selection&&0==t--)return n=r,!1})),e.slice(n)}(s,a),o-=a),new No(s.append(i),o)}remapping(e,t){let n=new fe;return this.items.forEach(((t,r)=>{let i=null!=t.mirrorOffset&&r-t.mirrorOffset>=e?n.maps.length-t.mirrorOffset:void 0;n.appendMap(t.map,i)}),e,t),n}addMaps(e){return 0==this.eventCount?this:new No(this.items.append(e.map((e=>new Do(e)))),this.eventCount)}rebased(e,t){if(!this.eventCount)return this;let n=[],r=Math.max(0,this.items.length-t),i=e.mapping,o=e.steps.length,s=this.eventCount;this.items.forEach((e=>{e.selection&&s--}),r);let l=t;this.items.forEach((t=>{let r=i.getMirror(--l);if(null==r)return;o=Math.min(o,r);let a=i.maps[r];if(t.step){let o=e.steps[r].invert(e.docs[r]),c=t.selection&&t.selection.map(i.slice(l+1,r));c&&s++,n.push(new Do(a,o,c))}else n.push(new Do(a))}),r);let a=[];for(let d=t;d500&&(h=h.compress(this.items.length-n.length)),h}emptyItemCount(){let e=0;return this.items.forEach((t=>{t.step||e++})),e}compress(e=this.items.length){let t=this.remapping(0,e),n=t.maps.length,r=[],i=0;return this.items.forEach(((o,s)=>{if(s>=e)r.push(o),o.selection&&i++;else if(o.step){let e=o.step.map(t.slice(n)),s=e&&e.getMap();if(n--,s&&t.appendMap(s,n),e){let l=o.selection&&o.selection.map(t.slice(n));l&&i++;let a,c=new Do(s.invert(),e,l),h=r.length-1;(a=r.length&&r[h].merge(c))?r[h]=a:r.push(c)}}else o.map&&n--}),this.items.length,0),new No(Co.from(r.reverse()),i)}}No.empty=new No(Co.empty,0);class Do{constructor(e,t,n,r){this.map=e,this.step=t,this.selection=n,this.mirrorOffset=r}merge(e){if(this.step&&e.step&&!e.selection){let t=e.step.merge(this.step);if(t)return new Do(t.getMap().invert(),t,this.selection)}}}class To{constructor(e,t,n,r){this.done=e,this.undone=t,this.prevRanges=n,this.prevTime=r}}const Eo=20;function Ao(e){let t=[];return e.forEach(((e,n,r,i)=>t.push(r,i))),t}function $o(e,t){if(!e)return null;let n=[];for(let r=0;rnew To(No.empty,No.empty,null,0),apply:(t,n,r)=>function(e,t,n,r){let i,o=n.getMeta(_o);if(o)return o.historyState;n.getMeta(Bo)&&(e=new To(e.done,e.undone,null,0));let s=n.getMeta("appendedTransaction");if(0==n.steps.length)return e;if(s&&s.getMeta(_o))return s.getMeta(_o).redo?new To(e.done.addTransform(n,void 0,r,Ro(t)),e.undone,Ao(n.mapping.maps[n.steps.length-1]),e.prevTime):new To(e.done,e.undone.addTransform(n,void 0,r,Ro(t)),null,e.prevTime);if(!1===n.getMeta("addToHistory")||s&&!1===s.getMeta("addToHistory"))return(i=n.getMeta("rebased"))?new To(e.done.rebased(n,i),e.undone.rebased(n,i),$o(e.prevRanges,n.mapping),e.prevTime):new To(e.done.addMaps(n.mapping.maps),e.undone.addMaps(n.mapping.maps),$o(e.prevRanges,n.mapping),e.prevTime);{let i=0==e.prevTime||!s&&(e.prevTime<(n.time||0)-r.newGroupDelay||!function(e,t){if(!t)return!1;if(!e.docChanged)return!0;let n=!1;return e.mapping.maps[0].forEach(((e,r)=>{for(let i=0;i=t[i]&&(n=!0)})),n}(n,e.prevRanges)),o=s?$o(e.prevRanges,n.mapping):Ao(n.mapping.maps[n.steps.length-1]);return new To(e.done.addTransform(n,i?t.selection.getBookmark():void 0,r,Ro(t)),No.empty,o,n.time)}}(n,r,t,e)},config:e,props:{handleDOMEvents:{beforeinput(e,t){let n=t.inputType,r="historyUndo"==n?jo:"historyRedo"==n?Fo:null;return!!r&&(t.preventDefault(),r(e.state,e.dispatch))}}}})}const jo=(e,t)=>{let n=_o.getState(e);return!(!n||0==n.done.eventCount)&&(t&&Po(n,e,t,!1),!0)},Fo=(e,t)=>{let n=_o.getState(e);return!(!n||0==n.undone.eventCount)&&(t&&Po(n,e,t,!0),!0)};function Lo(e){let t=_o.getState(e);return t?t.done.eventCount:0}function qo(e){let t=_o.getState(e);return t?t.undone.eventCount:0}"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self&&self;function Wo(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var Jo={},Ko={},Ho={},Yo={},Uo={};function Go(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function Zo(e){for(var t=1;t=+n}))};var ms={};Object.defineProperty(ms,"__esModule",{value:!0}),ms.default=void 0;var gs=(0,Ho.regex)("email",/^(?:[A-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[A-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9]{2,}(?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])$/i);ms.default=gs;var ys={};Object.defineProperty(ys,"__esModule",{value:!0}),ys.default=void 0;var vs=Ho,ws=(0,vs.withParams)({type:"ipAddress"},(function(e){if(!(0,vs.req)(e))return!0;if("string"!=typeof e)return!1;var t=e.split(".");return 4===t.length&&t.every(bs)}));ys.default=ws;var bs=function(e){if(e.length>3||0===e.length)return!1;if("0"===e[0]&&"0"!==e)return!1;if(!e.match(/^\d+$/))return!1;var t=0|+e;return t>=0&&t<=255},xs={};Object.defineProperty(xs,"__esModule",{value:!0}),xs.default=void 0;var Ss=Ho;xs.default=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:":";return(0,Ss.withParams)({type:"macAddress"},(function(t){if(!(0,Ss.req)(t))return!0;if("string"!=typeof t)return!1;var n="string"==typeof e&&""!==e?t.split(e):12===t.length||16===t.length?t.match(/.{2}/g):null;return null!==n&&(6===n.length||8===n.length)&&n.every(ks)}))};var ks=function(e){return e.toLowerCase().match(/^[0-9a-f]{2}$/)},Ms={};Object.defineProperty(Ms,"__esModule",{value:!0}),Ms.default=void 0;var Os=Ho;Ms.default=function(e){return(0,Os.withParams)({type:"maxLength",max:e},(function(t){return!(0,Os.req)(t)||(0,Os.len)(t)<=e}))};var Cs={};Object.defineProperty(Cs,"__esModule",{value:!0}),Cs.default=void 0;var Ns=Ho;Cs.default=function(e){return(0,Ns.withParams)({type:"minLength",min:e},(function(t){return!(0,Ns.req)(t)||(0,Ns.len)(t)>=e}))};var Ds={};Object.defineProperty(Ds,"__esModule",{value:!0}),Ds.default=void 0;var Ts=Ho,Es=(0,Ts.withParams)({type:"required"},(function(e){return(0,Ts.req)("string"==typeof e?e.trim():e)}));Ds.default=Es;var As={};Object.defineProperty(As,"__esModule",{value:!0}),As.default=void 0;var $s=Ho;As.default=function(e){return(0,$s.withParams)({type:"requiredIf",prop:e},(function(t,n){return!(0,$s.ref)(e,this,n)||(0,$s.req)(t)}))};var Ps={};Object.defineProperty(Ps,"__esModule",{value:!0}),Ps.default=void 0;var Is=Ho;Ps.default=function(e){return(0,Is.withParams)({type:"requiredUnless",prop:e},(function(t,n){return!!(0,Is.ref)(e,this,n)||(0,Is.req)(t)}))};var zs={};Object.defineProperty(zs,"__esModule",{value:!0}),zs.default=void 0;var Rs=Ho;zs.default=function(e){return(0,Rs.withParams)({type:"sameAs",eq:e},(function(t,n){return t===(0,Rs.ref)(e,this,n)}))};var _s={};Object.defineProperty(_s,"__esModule",{value:!0}),_s.default=void 0;var Bs=(0,Ho.regex)("url",/^(?:(?:(?:https?|ftp):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z0-9\u00a1-\uffff][a-z0-9\u00a1-\uffff_-]{0,62})?[a-z0-9\u00a1-\uffff]\.)+(?:[a-z\u00a1-\uffff]{2,}\.?))(?::\d{2,5})?(?:[/?#]\S*)?$/i);_s.default=Bs;var Vs={};Object.defineProperty(Vs,"__esModule",{value:!0}),Vs.default=void 0;var js=Ho;Vs.default=function(){for(var e=arguments.length,t=new Array(e),n=0;n0&&t.reduce((function(t,n){return t||n.apply(e,r)}),!1)}))};var Fs={};Object.defineProperty(Fs,"__esModule",{value:!0}),Fs.default=void 0;var Ls=Ho;Fs.default=function(){for(var e=arguments.length,t=new Array(e),n=0;n0&&t.reduce((function(t,n){return t&&n.apply(e,r)}),!0)}))};var qs={};Object.defineProperty(qs,"__esModule",{value:!0}),qs.default=void 0;var Ws=Ho;qs.default=function(e){return(0,Ws.withParams)({type:"not"},(function(t,n){return!(0,Ws.req)(t)||!e.call(this,t,n)}))};var Js={};Object.defineProperty(Js,"__esModule",{value:!0}),Js.default=void 0;var Ks=Ho;Js.default=function(e){return(0,Ks.withParams)({type:"minValue",min:e},(function(t){return!(0,Ks.req)(t)||(!/\s/.test(t)||t instanceof Date)&&+t>=+e}))};var Hs={};Object.defineProperty(Hs,"__esModule",{value:!0}),Hs.default=void 0;var Ys=Ho;Hs.default=function(e){return(0,Ys.withParams)({type:"maxValue",max:e},(function(t){return!(0,Ys.req)(t)||(!/\s/.test(t)||t instanceof Date)&&+t<=+e}))};var Us={};Object.defineProperty(Us,"__esModule",{value:!0}),Us.default=void 0;var Gs=(0,Ho.regex)("integer",/(^[0-9]*$)|(^-[0-9]+$)/);Us.default=Gs;var Zs={};Object.defineProperty(Zs,"__esModule",{value:!0}),Zs.default=void 0;var Xs=(0,Ho.regex)("decimal",/^[-]?\d*(\.\d+)?$/);Zs.default=Xs,function(e){function t(e){return(t="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}Object.defineProperty(e,"__esModule",{value:!0}),Object.defineProperty(e,"alpha",{enumerable:!0,get:function(){return n.default}}),Object.defineProperty(e,"alphaNum",{enumerable:!0,get:function(){return r.default}}),Object.defineProperty(e,"and",{enumerable:!0,get:function(){return y.default}}),Object.defineProperty(e,"between",{enumerable:!0,get:function(){return o.default}}),Object.defineProperty(e,"decimal",{enumerable:!0,get:function(){return S.default}}),Object.defineProperty(e,"email",{enumerable:!0,get:function(){return s.default}}),e.helpers=void 0,Object.defineProperty(e,"integer",{enumerable:!0,get:function(){return x.default}}),Object.defineProperty(e,"ipAddress",{enumerable:!0,get:function(){return l.default}}),Object.defineProperty(e,"macAddress",{enumerable:!0,get:function(){return a.default}}),Object.defineProperty(e,"maxLength",{enumerable:!0,get:function(){return c.default}}),Object.defineProperty(e,"maxValue",{enumerable:!0,get:function(){return b.default}}),Object.defineProperty(e,"minLength",{enumerable:!0,get:function(){return h.default}}),Object.defineProperty(e,"minValue",{enumerable:!0,get:function(){return w.default}}),Object.defineProperty(e,"not",{enumerable:!0,get:function(){return v.default}}),Object.defineProperty(e,"numeric",{enumerable:!0,get:function(){return i.default}}),Object.defineProperty(e,"or",{enumerable:!0,get:function(){return g.default}}),Object.defineProperty(e,"required",{enumerable:!0,get:function(){return d.default}}),Object.defineProperty(e,"requiredIf",{enumerable:!0,get:function(){return u.default}}),Object.defineProperty(e,"requiredUnless",{enumerable:!0,get:function(){return f.default}}),Object.defineProperty(e,"sameAs",{enumerable:!0,get:function(){return p.default}}),Object.defineProperty(e,"url",{enumerable:!0,get:function(){return m.default}});var n=O(Ko),r=O(cs),i=O(ds),o=O(fs),s=O(ms),l=O(ys),a=O(xs),c=O(Ms),h=O(Cs),d=O(Ds),u=O(As),f=O(Ps),p=O(zs),m=O(_s),g=O(Vs),y=O(Fs),v=O(qs),w=O(Js),b=O(Hs),x=O(Us),S=O(Zs),k=function(e,n){if(!n&&e&&e.__esModule)return e;if(null===e||"object"!==t(e)&&"function"!=typeof e)return{default:e};var r=M(n);if(r&&r.has(e))return r.get(e);var i={},o=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var s in e)if("default"!==s&&Object.prototype.hasOwnProperty.call(e,s)){var l=o?Object.getOwnPropertyDescriptor(e,s):null;l&&(l.get||l.set)?Object.defineProperty(i,s,l):i[s]=e[s]}i.default=e,r&&r.set(e,i);return i}(Ho);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 O(e){return e&&e.__esModule?e:{default:e}}e.helpers=k}(Jo);function Qs(e){return(Qs="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}var el={selector:"vue-portal-target-".concat(((e=21)=>{let t="",n=e;for(;n--;)t+="useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict"[64*Math.random()|0];return t})())},tl="undefined"!=typeof window&&void 0!==("undefined"==typeof document?"undefined":Qs(document)),nl=Vue.extend({abstract:!0,name:"PortalOutlet",props:["nodes","tag"],data:function(e){return{updatedNodes:e.nodes}},render:function(e){var t=this.updatedNodes&&this.updatedNodes();return t?1!==t.length||t[0].text?e(this.tag||"DIV",t):t:e()},destroyed:function(){var e=this.$el;e&&e.parentNode.removeChild(e)}}),rl=Vue.extend({name:"VueSimplePortal",props:{disabled:{type:Boolean},prepend:{type:Boolean},selector:{type:String,default:function(){return"#".concat(el.selector)}},tag:{type:String,default:"DIV"}},render:function(e){if(this.disabled){var t=this.$scopedSlots&&this.$scopedSlots.default();return t?t.length<2&&!t[0].text?t:e(this.tag,t):e()}return e()},created:function(){this.getTargetEl()||this.insertTargetEl()},updated:function(){var e=this;this.$nextTick((function(){e.disabled||e.slotFn===e.$scopedSlots.default||(e.container.updatedNodes=e.$scopedSlots.default),e.slotFn=e.$scopedSlots.default}))},beforeDestroy:function(){this.unmount()},watch:{disabled:{immediate:!0,handler:function(e){e?this.unmount():this.$nextTick(this.mount)}}},methods:{getTargetEl:function(){if(tl)return document.querySelector(this.selector)},insertTargetEl:function(){if(tl){var e=document.querySelector("body"),t=document.createElement(this.tag);t.id=this.selector.substring(1),e.appendChild(t)}},mount:function(){if(tl){var e=this.getTargetEl(),t=document.createElement("DIV");this.prepend&&e.firstChild?e.insertBefore(t,e.firstChild):e.appendChild(t),this.container=new nl({el:t,parent:this,propsData:{tag:this.tag,nodes:this.$scopedSlots.default}})}},unmount:function(){this.container&&(this.container.$destroy(),delete this.container)}}});function il(e){var t,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};e.component(n.name||"portal",rl),n.defaultSelector&&(t=n.defaultSelector,el.selector=t)}"undefined"!=typeof window&&window.Vue&&window.Vue===Vue&&Vue.use(il) -/*! - * vuex v3.6.2 - * (c) 2021 Evan You - * @license MIT - */;var ol=("undefined"!=typeof window?window:"undefined"!=typeof global?global:{}).__VUE_DEVTOOLS_GLOBAL_HOOK__;function sl(e,t){if(void 0===t&&(t=[]),null===e||"object"!=typeof e)return e;var n,r=(n=function(t){return t.original===e},t.filter(n)[0]);if(r)return r.copy;var i=Array.isArray(e)?[]:{};return t.push({original:e,copy:i}),Object.keys(e).forEach((function(n){i[n]=sl(e[n],t)})),i}function ll(e,t){Object.keys(e).forEach((function(n){return t(e[n],n)}))}function al(e){return null!==e&&"object"==typeof e}var cl=function(e,t){this.runtime=t,this._children=Object.create(null),this._rawModule=e;var n=e.state;this.state=("function"==typeof n?n():n)||{}},hl={namespaced:{configurable:!0}};hl.namespaced.get=function(){return!!this._rawModule.namespaced},cl.prototype.addChild=function(e,t){this._children[e]=t},cl.prototype.removeChild=function(e){delete this._children[e]},cl.prototype.getChild=function(e){return this._children[e]},cl.prototype.hasChild=function(e){return e in this._children},cl.prototype.update=function(e){this._rawModule.namespaced=e.namespaced,e.actions&&(this._rawModule.actions=e.actions),e.mutations&&(this._rawModule.mutations=e.mutations),e.getters&&(this._rawModule.getters=e.getters)},cl.prototype.forEachChild=function(e){ll(this._children,e)},cl.prototype.forEachGetter=function(e){this._rawModule.getters&&ll(this._rawModule.getters,e)},cl.prototype.forEachAction=function(e){this._rawModule.actions&&ll(this._rawModule.actions,e)},cl.prototype.forEachMutation=function(e){this._rawModule.mutations&&ll(this._rawModule.mutations,e)},Object.defineProperties(cl.prototype,hl);var dl,ul=function(e){this.register([],e,!1)};function fl(e,t,n){if(t.update(n),n.modules)for(var r in n.modules){if(!t.getChild(r))return;fl(e.concat(r),t.getChild(r),n.modules[r])}}ul.prototype.get=function(e){return e.reduce((function(e,t){return e.getChild(t)}),this.root)},ul.prototype.getNamespace=function(e){var t=this.root;return e.reduce((function(e,n){return e+((t=t.getChild(n)).namespaced?n+"/":"")}),"")},ul.prototype.update=function(e){fl([],this.root,e)},ul.prototype.register=function(e,t,n){var r=this;void 0===n&&(n=!0);var i=new cl(t,n);0===e.length?this.root=i:this.get(e.slice(0,-1)).addChild(e[e.length-1],i);t.modules&&ll(t.modules,(function(t,i){r.register(e.concat(i),t,n)}))},ul.prototype.unregister=function(e){var t=this.get(e.slice(0,-1)),n=e[e.length-1],r=t.getChild(n);r&&r.runtime&&t.removeChild(n)},ul.prototype.isRegistered=function(e){var t=this.get(e.slice(0,-1)),n=e[e.length-1];return!!t&&t.hasChild(n)};var pl=function(e){var t=this;void 0===e&&(e={}),!dl&&"undefined"!=typeof window&&window.Vue&&Sl(window.Vue);var n=e.plugins;void 0===n&&(n=[]);var r=e.strict;void 0===r&&(r=!1),this._committing=!1,this._actions=Object.create(null),this._actionSubscribers=[],this._mutations=Object.create(null),this._wrappedGetters=Object.create(null),this._modules=new ul(e),this._modulesNamespaceMap=Object.create(null),this._subscribers=[],this._watcherVM=new dl,this._makeLocalGettersCache=Object.create(null);var i=this,o=this.dispatch,s=this.commit;this.dispatch=function(e,t){return o.call(i,e,t)},this.commit=function(e,t,n){return s.call(i,e,t,n)},this.strict=r;var l=this._modules.root.state;wl(this,l,[],this._modules.root),vl(this,l),n.forEach((function(e){return e(t)})),(void 0!==e.devtools?e.devtools:dl.config.devtools)&&function(e){ol&&(e._devtoolHook=ol,ol.emit("vuex:init",e),ol.on("vuex:travel-to-state",(function(t){e.replaceState(t)})),e.subscribe((function(e,t){ol.emit("vuex:mutation",e,t)}),{prepend:!0}),e.subscribeAction((function(e,t){ol.emit("vuex:action",e,t)}),{prepend:!0}))}(this)},ml={state:{configurable:!0}};function gl(e,t,n){return t.indexOf(e)<0&&(n&&n.prepend?t.unshift(e):t.push(e)),function(){var n=t.indexOf(e);n>-1&&t.splice(n,1)}}function yl(e,t){e._actions=Object.create(null),e._mutations=Object.create(null),e._wrappedGetters=Object.create(null),e._modulesNamespaceMap=Object.create(null);var n=e.state;wl(e,n,[],e._modules.root,!0),vl(e,n,t)}function vl(e,t,n){var r=e._vm;e.getters={},e._makeLocalGettersCache=Object.create(null);var i=e._wrappedGetters,o={};ll(i,(function(t,n){o[n]=function(e,t){return function(){return e(t)}}(t,e),Object.defineProperty(e.getters,n,{get:function(){return e._vm[n]},enumerable:!0})}));var s=dl.config.silent;dl.config.silent=!0,e._vm=new dl({data:{$$state:t},computed:o}),dl.config.silent=s,e.strict&&function(e){e._vm.$watch((function(){return this._data.$$state}),(function(){}),{deep:!0,sync:!0})}(e),r&&(n&&e._withCommit((function(){r._data.$$state=null})),dl.nextTick((function(){return r.$destroy()})))}function wl(e,t,n,r,i){var o=!n.length,s=e._modules.getNamespace(n);if(r.namespaced&&(e._modulesNamespaceMap[s],e._modulesNamespaceMap[s]=r),!o&&!i){var l=bl(t,n.slice(0,-1)),a=n[n.length-1];e._withCommit((function(){dl.set(l,a,r.state)}))}var c=r.context=function(e,t,n){var r=""===t,i={dispatch:r?e.dispatch:function(n,r,i){var o=xl(n,r,i),s=o.payload,l=o.options,a=o.type;return l&&l.root||(a=t+a),e.dispatch(a,s)},commit:r?e.commit:function(n,r,i){var o=xl(n,r,i),s=o.payload,l=o.options,a=o.type;l&&l.root||(a=t+a),e.commit(a,s,l)}};return Object.defineProperties(i,{getters:{get:r?function(){return e.getters}:function(){return function(e,t){if(!e._makeLocalGettersCache[t]){var n={},r=t.length;Object.keys(e.getters).forEach((function(i){if(i.slice(0,r)===t){var o=i.slice(r);Object.defineProperty(n,o,{get:function(){return e.getters[i]},enumerable:!0})}})),e._makeLocalGettersCache[t]=n}return e._makeLocalGettersCache[t]}(e,t)}},state:{get:function(){return bl(e.state,n)}}}),i}(e,s,n);r.forEachMutation((function(t,n){!function(e,t,n,r){var i=e._mutations[t]||(e._mutations[t]=[]);i.push((function(t){n.call(e,r.state,t)}))}(e,s+n,t,c)})),r.forEachAction((function(t,n){var r=t.root?n:s+n,i=t.handler||t;!function(e,t,n,r){var i=e._actions[t]||(e._actions[t]=[]);i.push((function(t){var i,o=n.call(e,{dispatch:r.dispatch,commit:r.commit,getters:r.getters,state:r.state,rootGetters:e.getters,rootState:e.state},t);return(i=o)&&"function"==typeof i.then||(o=Promise.resolve(o)),e._devtoolHook?o.catch((function(t){throw e._devtoolHook.emit("vuex:error",t),t})):o}))}(e,r,i,c)})),r.forEachGetter((function(t,n){!function(e,t,n,r){if(e._wrappedGetters[t])return;e._wrappedGetters[t]=function(e){return n(r.state,r.getters,e.state,e.getters)}}(e,s+n,t,c)})),r.forEachChild((function(r,o){wl(e,t,n.concat(o),r,i)}))}function bl(e,t){return t.reduce((function(e,t){return e[t]}),e)}function xl(e,t,n){return al(e)&&e.type&&(n=t,t=e,e=e.type),{type:e,payload:t,options:n}}function Sl(e){dl&&e===dl||function(e){if(Number(e.version.split(".")[0])>=2)e.mixin({beforeCreate:n});else{var t=e.prototype._init;e.prototype._init=function(e){void 0===e&&(e={}),e.init=e.init?[n].concat(e.init):n,t.call(this,e)}}function n(){var e=this.$options;e.store?this.$store="function"==typeof e.store?e.store():e.store:e.parent&&e.parent.$store&&(this.$store=e.parent.$store)}}(dl=e)}ml.state.get=function(){return this._vm._data.$$state},ml.state.set=function(e){},pl.prototype.commit=function(e,t,n){var r=this,i=xl(e,t,n),o=i.type,s=i.payload,l={type:o,payload:s},a=this._mutations[o];a&&(this._withCommit((function(){a.forEach((function(e){e(s)}))})),this._subscribers.slice().forEach((function(e){return e(l,r.state)})))},pl.prototype.dispatch=function(e,t){var n=this,r=xl(e,t),i=r.type,o=r.payload,s={type:i,payload:o},l=this._actions[i];if(l){try{this._actionSubscribers.slice().filter((function(e){return e.before})).forEach((function(e){return e.before(s,n.state)}))}catch(c){}var a=l.length>1?Promise.all(l.map((function(e){return e(o)}))):l[0](o);return new Promise((function(e,t){a.then((function(t){try{n._actionSubscribers.filter((function(e){return e.after})).forEach((function(e){return e.after(s,n.state)}))}catch(c){}e(t)}),(function(e){try{n._actionSubscribers.filter((function(e){return e.error})).forEach((function(t){return t.error(s,n.state,e)}))}catch(c){}t(e)}))}))}},pl.prototype.subscribe=function(e,t){return gl(e,this._subscribers,t)},pl.prototype.subscribeAction=function(e,t){return gl("function"==typeof e?{before:e}:e,this._actionSubscribers,t)},pl.prototype.watch=function(e,t,n){var r=this;return this._watcherVM.$watch((function(){return e(r.state,r.getters)}),t,n)},pl.prototype.replaceState=function(e){var t=this;this._withCommit((function(){t._vm._data.$$state=e}))},pl.prototype.registerModule=function(e,t,n){void 0===n&&(n={}),"string"==typeof e&&(e=[e]),this._modules.register(e,t),wl(this,this.state,e,this._modules.get(e),n.preserveState),vl(this,this.state)},pl.prototype.unregisterModule=function(e){var t=this;"string"==typeof e&&(e=[e]),this._modules.unregister(e),this._withCommit((function(){var n=bl(t.state,e.slice(0,-1));dl.delete(n,e[e.length-1])})),yl(this)},pl.prototype.hasModule=function(e){return"string"==typeof e&&(e=[e]),this._modules.isRegistered(e)},pl.prototype.hotUpdate=function(e){this._modules.update(e),yl(this,!0)},pl.prototype._withCommit=function(e){var t=this._committing;this._committing=!0,e(),this._committing=t},Object.defineProperties(pl.prototype,ml);var kl=Dl((function(e,t){var n={};return Nl(t).forEach((function(t){var r=t.key,i=t.val;n[r]=function(){var t=this.$store.state,n=this.$store.getters;if(e){var r=Tl(this.$store,"mapState",e);if(!r)return;t=r.context.state,n=r.context.getters}return"function"==typeof i?i.call(this,t,n):t[i]},n[r].vuex=!0})),n})),Ml=Dl((function(e,t){var n={};return Nl(t).forEach((function(t){var r=t.key,i=t.val;n[r]=function(){for(var t=[],n=arguments.length;n--;)t[n]=arguments[n];var r=this.$store.commit;if(e){var o=Tl(this.$store,"mapMutations",e);if(!o)return;r=o.context.commit}return"function"==typeof i?i.apply(this,[r].concat(t)):r.apply(this.$store,[i].concat(t))}})),n})),Ol=Dl((function(e,t){var n={};return Nl(t).forEach((function(t){var r=t.key,i=t.val;i=e+i,n[r]=function(){if(!e||Tl(this.$store,"mapGetters",e))return this.$store.getters[i]},n[r].vuex=!0})),n})),Cl=Dl((function(e,t){var n={};return Nl(t).forEach((function(t){var r=t.key,i=t.val;n[r]=function(){for(var t=[],n=arguments.length;n--;)t[n]=arguments[n];var r=this.$store.dispatch;if(e){var o=Tl(this.$store,"mapActions",e);if(!o)return;r=o.context.dispatch}return"function"==typeof i?i.apply(this,[r].concat(t)):r.apply(this.$store,[i].concat(t))}})),n}));function Nl(e){return function(e){return Array.isArray(e)||al(e)}(e)?Array.isArray(e)?e.map((function(e){return{key:e,val:e}})):Object.keys(e).map((function(t){return{key:t,val:e[t]}})):[]}function Dl(e){return function(t,n){return"string"!=typeof t?(n=t,t=""):"/"!==t.charAt(t.length-1)&&(t+="/"),e(t,n)}}function Tl(e,t,n){return e._modulesNamespaceMap[n]}function El(e,t,n){var r=n?e.groupCollapsed:e.group;try{r.call(e,t)}catch(i){e.log(t)}}function Al(e){try{e.groupEnd()}catch(t){e.log("—— log end ——")}}function $l(){var e=new Date;return" @ "+Pl(e.getHours(),2)+":"+Pl(e.getMinutes(),2)+":"+Pl(e.getSeconds(),2)+"."+Pl(e.getMilliseconds(),3)}function Pl(e,t){return n="0",r=t-e.toString().length,new Array(r+1).join(n)+e;var n,r}const Il={Store:pl,install:Sl,version:"3.6.2",mapState:kl,mapMutations:Ml,mapGetters:Ol,mapActions:Cl,createNamespacedHelpers:function(e){return{mapState:kl.bind(null,e),mapGetters:Ol.bind(null,e),mapMutations:Ml.bind(null,e),mapActions:Cl.bind(null,e)}},createLogger:function(e){void 0===e&&(e={});var t=e.collapsed;void 0===t&&(t=!0);var n=e.filter;void 0===n&&(n=function(e,t,n){return!0});var r=e.transformer;void 0===r&&(r=function(e){return e});var i=e.mutationTransformer;void 0===i&&(i=function(e){return e});var o=e.actionFilter;void 0===o&&(o=function(e,t){return!0});var s=e.actionTransformer;void 0===s&&(s=function(e){return e});var l=e.logMutations;void 0===l&&(l=!0);var a=e.logActions;void 0===a&&(a=!0);var c=e.logger;return void 0===c&&(c=console),function(e){var h=sl(e.state);void 0!==c&&(l&&e.subscribe((function(e,o){var s=sl(o);if(n(e,h,s)){var l=$l(),a=i(e),d="mutation "+e.type+l;El(c,d,t),c.log("%c prev state","color: #9E9E9E; font-weight: bold",r(h)),c.log("%c mutation","color: #03A9F4; font-weight: bold",a),c.log("%c next state","color: #4CAF50; font-weight: bold",r(s)),Al(c)}h=s})),a&&e.subscribeAction((function(e,n){if(o(e,n)){var r=$l(),i=s(e),l="action "+e.type+r;El(c,l,t),c.log("%c action","color: #03A9F4; font-weight: bold",i),Al(c)}})))}}};function zl(e){return{all:e=e||new Map,on:function(t,n){var r=e.get(t);r?r.push(n):e.set(t,[n])},off:function(t,n){var r=e.get(t);r&&(n?r.splice(r.indexOf(n)>>>0,1):e.set(t,[]))},emit:function(t,n){var r=e.get(t);r&&r.slice().map((function(e){e(n)})),(r=e.get("*"))&&r.slice().map((function(e){e(t,n)}))}}}var Rl,_l,Bl="function"==typeof Map?new Map:(Rl=[],_l=[],{has:function(e){return Rl.indexOf(e)>-1},get:function(e){return _l[Rl.indexOf(e)]},set:function(e,t){-1===Rl.indexOf(e)&&(Rl.push(e),_l.push(t))},delete:function(e){var t=Rl.indexOf(e);t>-1&&(Rl.splice(t,1),_l.splice(t,1))}}),Vl=function(e){return new Event(e,{bubbles:!0})};try{new Event("test")}catch(oa){Vl=function(e){var t=document.createEvent("Event");return t.initEvent(e,!0,!1),t}}function jl(e){var t=Bl.get(e);t&&t.destroy()}function Fl(e){var t=Bl.get(e);t&&t.update()}var Ll=null;"undefined"==typeof window||"function"!=typeof window.getComputedStyle?((Ll=function(e){return e}).destroy=function(e){return e},Ll.update=function(e){return e}):((Ll=function(e,t){return e&&Array.prototype.forEach.call(e.length?e:[e],(function(e){return function(e){if(e&&e.nodeName&&"TEXTAREA"===e.nodeName&&!Bl.has(e)){var t,n=null,r=null,i=null,o=function(){e.clientWidth!==r&&c()},s=function(t){window.removeEventListener("resize",o,!1),e.removeEventListener("input",c,!1),e.removeEventListener("keyup",c,!1),e.removeEventListener("autosize:destroy",s,!1),e.removeEventListener("autosize:update",c,!1),Object.keys(t).forEach((function(n){e.style[n]=t[n]})),Bl.delete(e)}.bind(e,{height:e.style.height,resize:e.style.resize,overflowY:e.style.overflowY,overflowX:e.style.overflowX,wordWrap:e.style.wordWrap});e.addEventListener("autosize:destroy",s,!1),"onpropertychange"in e&&"oninput"in e&&e.addEventListener("keyup",c,!1),window.addEventListener("resize",o,!1),e.addEventListener("input",c,!1),e.addEventListener("autosize:update",c,!1),e.style.overflowX="hidden",e.style.wordWrap="break-word",Bl.set(e,{destroy:s,update:c}),"vertical"===(t=window.getComputedStyle(e,null)).resize?e.style.resize="none":"both"===t.resize&&(e.style.resize="horizontal"),n="content-box"===t.boxSizing?-(parseFloat(t.paddingTop)+parseFloat(t.paddingBottom)):parseFloat(t.borderTopWidth)+parseFloat(t.borderBottomWidth),isNaN(n)&&(n=0),c()}function l(t){var n=e.style.width;e.style.width="0px",e.style.width=n,e.style.overflowY=t}function a(){if(0!==e.scrollHeight){var t=function(e){for(var t=[];e&&e.parentNode&&e.parentNode instanceof Element;)e.parentNode.scrollTop&&(e.parentNode.style.scrollBehavior="auto",t.push([e.parentNode,e.parentNode.scrollTop])),e=e.parentNode;return function(){return t.forEach((function(e){var t=e[0];t.scrollTop=e[1],t.style.scrollBehavior=null}))}}(e);e.style.height="",e.style.height=e.scrollHeight+n+"px",r=e.clientWidth,t()}}function c(){a();var t=Math.round(parseFloat(e.style.height)),n=window.getComputedStyle(e,null),r="content-box"===n.boxSizing?Math.round(parseFloat(n.height)):e.offsetHeight;if(r=t?e:""+Array(t+1-r.length).join(n)+e},v={s:y,z:function(e){var t=-e.utcOffset(),n=Math.abs(t),r=Math.floor(n/60),i=n%60;return(t<=0?"+":"-")+y(r,2,"0")+":"+y(i,2,"0")},m:function e(t,n){if(t.date()1)return e(s[0])}else{var l=t.name;b[l]=t,i=l}return!r&&i&&(w=i),i||!r&&w},k=function(e,t){if(x(e))return e.clone();var n="object"==typeof t?t:{};return n.date=e,n.args=arguments,new O(n)},M=v;M.l=S,M.i=x,M.w=function(e,t){return k(e,{locale:t.$L,utc:t.$u,x:t.$x,$offset:t.$offset})};var O=function(){function g(e){this.$L=S(e.locale,null,!0),this.parse(e)}var y=g.prototype;return y.parse=function(e){this.$d=function(e){var t=e.date,n=e.utc;if(null===t)return new Date(NaN);if(M.u(t))return new Date;if(t instanceof Date)return new Date(t);if("string"==typeof t&&!/Z$/i.test(t)){var r=t.match(p);if(r){var i=r[2]-1||0,o=(r[7]||"0").substring(0,3);return n?new Date(Date.UTC(r[1],i,r[3]||1,r[4]||0,r[5]||0,r[6]||0,o)):new Date(r[1],i,r[3]||1,r[4]||0,r[5]||0,r[6]||0,o)}}return new Date(t)}(e),this.$x=e.x||{},this.init()},y.init=function(){var e=this.$d;this.$y=e.getFullYear(),this.$M=e.getMonth(),this.$D=e.getDate(),this.$W=e.getDay(),this.$H=e.getHours(),this.$m=e.getMinutes(),this.$s=e.getSeconds(),this.$ms=e.getMilliseconds()},y.$utils=function(){return M},y.isValid=function(){return!(this.$d.toString()===f)},y.isSame=function(e,t){var n=k(e);return this.startOf(t)<=n&&n<=this.endOf(t)},y.isAfter=function(e,t){return k(e)68?1900:2e3)},l=function(e){return function(t){this[e]=+t}},a=[/[+-]\d\d:?(\d\d)?|Z/,function(e){(this.zone||(this.zone={})).offset=function(e){if(!e)return 0;if("Z"===e)return 0;var t=e.match(/([+-]|\d\d)/g),n=60*t[1]+(+t[2]||0);return 0===n?0:"+"===t[0]?-n:n}(e)}],c=function(e){var t=o[e];return t&&(t.indexOf?t:t.s.concat(t.f))},h=function(e,t){var n,r=o.meridiem;if(r){for(var i=1;i<=24;i+=1)if(e.indexOf(r(i,0,t))>-1){n=i>12;break}}else n=e===(t?"pm":"PM");return n},d={A:[i,function(e){this.afternoon=h(e,!1)}],a:[i,function(e){this.afternoon=h(e,!0)}],S:[/\d/,function(e){this.milliseconds=100*+e}],SS:[n,function(e){this.milliseconds=10*+e}],SSS:[/\d{3}/,function(e){this.milliseconds=+e}],s:[r,l("seconds")],ss:[r,l("seconds")],m:[r,l("minutes")],mm:[r,l("minutes")],H:[r,l("hours")],h:[r,l("hours")],HH:[r,l("hours")],hh:[r,l("hours")],D:[r,l("day")],DD:[n,l("day")],Do:[i,function(e){var t=o.ordinal,n=e.match(/\d+/);if(this.day=n[0],t)for(var r=1;r<=31;r+=1)t(r).replace(/\[|\]/g,"")===e&&(this.day=r)}],M:[r,l("month")],MM:[n,l("month")],MMM:[i,function(e){var t=c("months"),n=(c("monthsShort")||t.map((function(e){return e.slice(0,3)}))).indexOf(e)+1;if(n<1)throw new Error;this.month=n%12||n}],MMMM:[i,function(e){var t=c("months").indexOf(e)+1;if(t<1)throw new Error;this.month=t%12||t}],Y:[/[+-]?\d+/,l("year")],YY:[n,function(e){this.year=s(e)}],YYYY:[/\d{4}/,l("year")],Z:a,ZZ:a};function u(n){var r,i;r=n,i=o&&o.formats;for(var s=(n=r.replace(/(\[[^\]]+])|(LTS?|l{1,4}|L{1,4})/g,(function(t,n,r){var o=r&&r.toUpperCase();return n||i[r]||e[r]||i[o].replace(/(\[[^\]]+])|(MMMM|MM|DD|dddd)/g,(function(e,t,n){return t||n.slice(1)}))}))).match(t),l=s.length,a=0;a-1)return new Date(("X"===t?1e3:1)*e);var r=u(t)(e),i=r.year,o=r.month,s=r.day,l=r.hours,a=r.minutes,c=r.seconds,h=r.milliseconds,d=r.zone,f=new Date,p=s||(i||o?1:f.getDate()),m=i||f.getFullYear(),g=0;i&&!o||(g=o>0?o-1:f.getMonth());var y=l||0,v=a||0,w=c||0,b=h||0;return d?new Date(Date.UTC(m,g,p,y,v,w,b+60*d.offset*1e3)):n?new Date(Date.UTC(m,g,p,y,v,w,b)):new Date(m,g,p,y,v,w,b)}catch(x){return new Date("")}}(t,l,r),this.init(),d&&!0!==d&&(this.$L=this.locale(d).$L),h&&t!=this.format(l)&&(this.$d=new Date("")),o={}}else if(l instanceof Array)for(var f=l.length,p=1;p<=f;p+=1){s[1]=l[p-1];var m=n.apply(this,s);if(m.isValid()){this.$d=m.$d,this.$L=m.$L,this.init();break}p===f&&(this.$d=new Date(""))}else i.call(this,e)}}}());var Yl={},Ul={};function Gl(e){return null==e}function Zl(e){return null!=e}function Xl(e,t){return t.tag===e.tag&&t.key===e.key}function Ql(e){var t=e.tag;e.vm=new t({data:e.args})}function ea(e,t,n){var r,i,o={};for(r=t;r<=n;++r)Zl(i=e[r].key)&&(o[i]=r);return o}function ta(e,t,n){for(;t<=n;++t)Ql(e[t])}function na(e,t,n){for(;t<=n;++t){var r=e[t];Zl(r)&&(r.vm.$destroy(),r.vm=null)}}function ra(e,t){e!==t&&(t.vm=e.vm,function(e){for(var t=Object.keys(e.args),n=0;nl?ta(t,s,h):s>h&&na(e,o,l)}(e,t):Zl(t)?ta(t,0,t.length-1):Zl(e)&&na(e,0,e.length-1)},function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.Vuelidate=O,e.validationMixin=e.default=void 0,Object.defineProperty(e,"withParams",{enumerable:!0,get:function(){return n.withParams}});var t=Ul,n=Uo;function r(e){return function(e){if(Array.isArray(e))return i(e)}(e)||function(e){if("undefined"!=typeof Symbol&&null!=e[Symbol.iterator]||null!=e["@@iterator"])return Array.from(e)}(e)||function(e,t){if(!e)return;if("string"==typeof e)return i(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);"Object"===n&&e.constructor&&(n=e.constructor.name);if("Map"===n||"Set"===n)return Array.from(e);if("Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return i(e,t)}(e)||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.")}()}function i(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n1?l:l.$sub[0]:null}}},computed:{run:function(){var e=this,t=this.lazyParentModel();if(Array.isArray(t)&&t.__ob__){var n=t.__ob__.dep;n.depend();var r=n.constructor.target;if(!this._indirectWatcher){var i=r.constructor;this._indirectWatcher=new i(this,(function(){return e.runRule(t)}),null,{lazy:!0})}var o=this.getModel();if(!this._indirectWatcher.dirty&&this._lastModel===o)return this._indirectWatcher.depend(),r.value;this._lastModel=o,this._indirectWatcher.evaluate(),this._indirectWatcher.depend()}else this._indirectWatcher&&(this._indirectWatcher.teardown(),this._indirectWatcher=null);return this._indirectWatcher?this._indirectWatcher.value:this.runRule(t)},$params:function(){return this.run.params},proxy:function(){var e=this.run.output;return e[p]?!!e.v:!!e},$pending:function(){var e=this.run.output;return!!e[p]&&e.p}},destroyed:function(){this._indirectWatcher&&(this._indirectWatcher.teardown(),this._indirectWatcher=null)}}),l=i.extend({data:function(){return{dirty:!1,validations:null,lazyModel:null,model:null,prop:null,lazyParentModel:null,rootModel:null}},methods:s(s({},y),{},{refProxy:function(e){return this.getRef(e).proxy},getRef:function(e){return this.refs[e]},isNested:function(e){return"function"!=typeof this.validations[e]}}),computed:s(s({},m),{},{nestedKeys:function(){return this.keys.filter(this.isNested)},ruleKeys:function(){var e=this;return this.keys.filter((function(t){return!e.isNested(t)}))},keys:function(){return Object.keys(this.validations).filter((function(e){return"$params"!==e}))},proxy:function(){var e=this,t=h(this.keys,(function(t){return{enumerable:!0,configurable:!0,get:function(){return e.refProxy(t)}}})),n=h(v,(function(t){return{enumerable:!0,configurable:!0,get:function(){return e[t]}}})),r=h(w,(function(t){return{enumerable:!1,configurable:!0,get:function(){return e[t]}}})),i=this.hasIter()?{$iter:{enumerable:!0,value:Object.defineProperties({},s({},t))}}:{};return Object.defineProperties({},s(s(s(s({},t),i),{},{$model:{enumerable:!0,get:function(){var t=e.lazyParentModel();return null!=t?t[e.prop]:null},set:function(t){var n=e.lazyParentModel();null!=n&&(n[e.prop]=t,e.$touch())}}},n),r))},children:function(){var e=this;return[].concat(r(this.nestedKeys.map((function(t){return x(e,t)}))),r(this.ruleKeys.map((function(t){return S(e,t)})))).filter(Boolean)}})}),a=l.extend({methods:{isNested:function(e){return void 0!==this.validations[e]()},getRef:function(e){var t=this;return{get proxy(){return t.validations[e]()||!1}}}}}),g=l.extend({computed:{keys:function(){var e=this.getModel();return u(e)?Object.keys(e):[]},tracker:function(){var e=this,t=this.validations.$trackBy;return t?function(n){return"".concat(f(e.rootModel,e.getModelKey(n),t))}:function(e){return"".concat(e)}},getModelLazy:function(){var e=this;return function(){return e.getModel()}},children:function(){var e=this,n=this.validations,r=this.getModel(),i=s({},n);delete i.$trackBy;var o={};return this.keys.map((function(n){var s=e.tracker(n);return o.hasOwnProperty(s)?null:(o[s]=!0,(0,t.h)(l,s,{validations:i,prop:n,lazyParentModel:e.getModelLazy,model:r[n],rootModel:e.rootModel}))})).filter(Boolean)}},methods:{isNested:function(){return!0},getRef:function(e){return this.refs[this.tracker(e)]},hasIter:function(){return!0}}}),x=function(e,n){if("$each"===n)return(0,t.h)(g,n,{validations:e.validations[n],lazyParentModel:e.lazyParentModel,prop:n,lazyModel:e.getModel,rootModel:e.rootModel});var r=e.validations[n];if(Array.isArray(r)){var i=e.rootModel,o=h(r,(function(e){return function(){return f(i,i.$v,e)}}),(function(e){return Array.isArray(e)?e.join("."):e}));return(0,t.h)(a,n,{validations:o,lazyParentModel:c,prop:n,lazyModel:c,rootModel:i})}return(0,t.h)(l,n,{validations:r,lazyParentModel:e.getModel,prop:n,lazyModel:e.getModelKey,rootModel:e.rootModel})},S=function(e,n){return(0,t.h)(o,n,{rule:e.validations[n],lazyParentModel:e.lazyParentModel,lazyModel:e.getModel,rootModel:e.rootModel})};return b={VBase:i,Validation:l}},S=null;var k=function(e,n){var r=function(e){if(S)return S;for(var t=e.constructor;t.super;)t=t.super;return S=t,t}(e),i=x(r),o=i.Validation;return new(0,i.VBase)({computed:{children:function(){var r="function"==typeof n?n.call(e):n;return[(0,t.h)(o,"$v",{validations:r,lazyParentModel:c,prop:"$v",model:e,rootModel:e})]}}})},M={data:function(){var e=this.$options.validations;return e&&(this._vuelidate=k(this,e)),{}},beforeCreate:function(){var e=this.$options;e.validations&&(e.computed||(e.computed={}),e.computed.$v||(e.computed.$v=function(){return this._vuelidate?this._vuelidate.refs.$v.proxy:null}))},beforeDestroy:function(){this._vuelidate&&(this._vuelidate.$destroy(),this._vuelidate=null)}};function O(e){e.mixin(M)}e.validationMixin=M;var C=O;e.default=C}(Yl);const ia=Wo(Yl);export{Jl as A,Hl as B,ql as C,H as D,ct as E,r as F,ia as G,no as I,Xe as N,dt as P,c as S,Ge as T,Il as V,lo as a,ao as b,Gi as c,yo as d,Fi as e,wo as f,xo as g,J as h,io as i,yi as j,Ii as k,bo as l,se as m,to as n,jo as o,Lo as p,qo as q,Fo as r,Yi as s,Ui as t,so as u,Vo as v,vo as w,Jo as x,il as y,zl as z}; diff --git a/kirby/panel/dist/js/vendor.min.js b/kirby/panel/dist/js/vendor.min.js new file mode 100644 index 0000000..08a8016 --- /dev/null +++ b/kirby/panel/dist/js/vendor.min.js @@ -0,0 +1,16 @@ +function e(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var t,n,r,i,o={},s={},l={},a={},c={};function h(){if(t)return c;function e(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function n(t){for(var n=1;n=+r}))},x}var k,M={};function O(){if(k)return M;k=1,Object.defineProperty(M,"__esModule",{value:!0}),M.default=void 0;var e=(0,d().regex)("email",/^(?:[A-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[A-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9]{2,}(?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])$/i);return M.default=e,M}var C,N={};function D(){if(C)return N;C=1,Object.defineProperty(N,"__esModule",{value:!0}),N.default=void 0;var e=d(),t=(0,e.withParams)({type:"ipAddress"},(function(t){if(!(0,e.req)(t))return!0;if("string"!=typeof t)return!1;var r=t.split(".");return 4===r.length&&r.every(n)}));N.default=t;var n=function(e){if(e.length>3||0===e.length)return!1;if("0"===e[0]&&"0"!==e)return!1;if(!e.match(/^\d+$/))return!1;var t=0|+e;return t>=0&&t<=255};return N}var T,A={};function $(){if(T)return A;T=1,Object.defineProperty(A,"__esModule",{value:!0}),A.default=void 0;var e=d();A.default=function(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:":";return(0,e.withParams)({type:"macAddress"},(function(r){if(!(0,e.req)(r))return!0;if("string"!=typeof r)return!1;var i="string"==typeof n&&""!==n?r.split(n):12===r.length||16===r.length?r.match(/.{2}/g):null;return null!==i&&(6===i.length||8===i.length)&&i.every(t)}))};var t=function(e){return e.toLowerCase().match(/^[0-9a-f]{2}$/)};return A}var E,P={};function R(){if(E)return P;E=1,Object.defineProperty(P,"__esModule",{value:!0}),P.default=void 0;var e=d();return P.default=function(t){return(0,e.withParams)({type:"maxLength",max:t},(function(n){return!(0,e.req)(n)||(0,e.len)(n)<=t}))},P}var I,z={};function _(){if(I)return z;I=1,Object.defineProperty(z,"__esModule",{value:!0}),z.default=void 0;var e=d();return z.default=function(t){return(0,e.withParams)({type:"minLength",min:t},(function(n){return!(0,e.req)(n)||(0,e.len)(n)>=t}))},z}var B,V={};function j(){if(B)return V;B=1,Object.defineProperty(V,"__esModule",{value:!0}),V.default=void 0;var e=d(),t=(0,e.withParams)({type:"required"},(function(t){return"string"==typeof t?(0,e.req)(t.trim()):(0,e.req)(t)}));return V.default=t,V}var F,L={};function W(){if(F)return L;F=1,Object.defineProperty(L,"__esModule",{value:!0}),L.default=void 0;var e=d();return L.default=function(t){return(0,e.withParams)({type:"requiredIf",prop:t},(function(n,r){return!(0,e.ref)(t,this,r)||(0,e.req)(n)}))},L}var q,J={};function K(){if(q)return J;q=1,Object.defineProperty(J,"__esModule",{value:!0}),J.default=void 0;var e=d();return J.default=function(t){return(0,e.withParams)({type:"requiredUnless",prop:t},(function(n,r){return!!(0,e.ref)(t,this,r)||(0,e.req)(n)}))},J}var H,Y={};function U(){if(H)return Y;H=1,Object.defineProperty(Y,"__esModule",{value:!0}),Y.default=void 0;var e=d();return Y.default=function(t){return(0,e.withParams)({type:"sameAs",eq:t},(function(n,r){return n===(0,e.ref)(t,this,r)}))},Y}var G,Z={};function X(){if(G)return Z;G=1,Object.defineProperty(Z,"__esModule",{value:!0}),Z.default=void 0;var e=(0,d().regex)("url",/^(?:(?:(?:https?|ftp):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z0-9\u00a1-\uffff][a-z0-9\u00a1-\uffff_-]{0,62})?[a-z0-9\u00a1-\uffff]\.)+(?:[a-z\u00a1-\uffff]{2,}\.?))(?::\d{2,5})?(?:[/?#]\S*)?$/i);return Z.default=e,Z}var Q,ee={};function te(){if(Q)return ee;Q=1,Object.defineProperty(ee,"__esModule",{value:!0}),ee.default=void 0;var e=d();return ee.default=function(){for(var t=arguments.length,n=new Array(t),r=0;r0&&n.reduce((function(t,n){return t||n.apply(e,r)}),!1)}))},ee}var ne,re={};function ie(){if(ne)return re;ne=1,Object.defineProperty(re,"__esModule",{value:!0}),re.default=void 0;var e=d();return re.default=function(){for(var t=arguments.length,n=new Array(t),r=0;r0&&n.reduce((function(t,n){return t&&n.apply(e,r)}),!0)}))},re}var oe,se={};function le(){if(oe)return se;oe=1,Object.defineProperty(se,"__esModule",{value:!0}),se.default=void 0;var e=d();return se.default=function(t){return(0,e.withParams)({type:"not"},(function(n,r){return!(0,e.req)(n)||!t.call(this,n,r)}))},se}var ae,ce={};function he(){if(ae)return ce;ae=1,Object.defineProperty(ce,"__esModule",{value:!0}),ce.default=void 0;var e=d();return ce.default=function(t){return(0,e.withParams)({type:"minValue",min:t},(function(n){return!(0,e.req)(n)||(!/\s/.test(n)||n instanceof Date)&&+n>=+t}))},ce}var ue,de={};function fe(){if(ue)return de;ue=1,Object.defineProperty(de,"__esModule",{value:!0}),de.default=void 0;var e=d();return de.default=function(t){return(0,e.withParams)({type:"maxValue",max:t},(function(n){return!(0,e.req)(n)||(!/\s/.test(n)||n instanceof Date)&&+n<=+t}))},de}var pe,me={};function ge(){if(pe)return me;pe=1,Object.defineProperty(me,"__esModule",{value:!0}),me.default=void 0;var e=(0,d().regex)("integer",/(^[0-9]*$)|(^-[0-9]+$)/);return me.default=e,me}var ye,ve,we={};function be(){if(ye)return we;ye=1,Object.defineProperty(we,"__esModule",{value:!0}),we.default=void 0;var e=(0,d().regex)("decimal",/^[-]?\d*(\.\d+)?$/);return we.default=e,we}function xe(){return ve||(ve=1,function(e){function t(e){return(t="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}Object.defineProperty(e,"__esModule",{value:!0}),Object.defineProperty(e,"alpha",{enumerable:!0,get:function(){return n.default}}),Object.defineProperty(e,"alphaNum",{enumerable:!0,get:function(){return r.default}}),Object.defineProperty(e,"and",{enumerable:!0,get:function(){return x.default}}),Object.defineProperty(e,"between",{enumerable:!0,get:function(){return o.default}}),Object.defineProperty(e,"decimal",{enumerable:!0,get:function(){return T.default}}),Object.defineProperty(e,"email",{enumerable:!0,get:function(){return s.default}}),e.helpers=void 0,Object.defineProperty(e,"integer",{enumerable:!0,get:function(){return N.default}}),Object.defineProperty(e,"ipAddress",{enumerable:!0,get:function(){return l.default}}),Object.defineProperty(e,"macAddress",{enumerable:!0,get:function(){return a.default}}),Object.defineProperty(e,"maxLength",{enumerable:!0,get:function(){return c.default}}),Object.defineProperty(e,"maxValue",{enumerable:!0,get:function(){return C.default}}),Object.defineProperty(e,"minLength",{enumerable:!0,get:function(){return h.default}}),Object.defineProperty(e,"minValue",{enumerable:!0,get:function(){return M.default}}),Object.defineProperty(e,"not",{enumerable:!0,get:function(){return k.default}}),Object.defineProperty(e,"numeric",{enumerable:!0,get:function(){return i.default}}),Object.defineProperty(e,"or",{enumerable:!0,get:function(){return b.default}}),Object.defineProperty(e,"required",{enumerable:!0,get:function(){return u.default}}),Object.defineProperty(e,"requiredIf",{enumerable:!0,get:function(){return p.default}}),Object.defineProperty(e,"requiredUnless",{enumerable:!0,get:function(){return m.default}}),Object.defineProperty(e,"sameAs",{enumerable:!0,get:function(){return y.default}}),Object.defineProperty(e,"url",{enumerable:!0,get:function(){return v.default}});var n=P(f()),r=P(g()),i=P(w()),o=P(S()),s=P(O()),l=P(D()),a=P($()),c=P(R()),h=P(_()),u=P(j()),p=P(W()),m=P(K()),y=P(U()),v=P(X()),b=P(te()),x=P(ie()),k=P(le()),M=P(he()),C=P(fe()),N=P(ge()),T=P(be()),A=function(e,n){if(e&&e.__esModule)return e;if(null===e||"object"!==t(e)&&"function"!=typeof e)return{default:e};var r=E(n);if(r&&r.has(e))return r.get(e);var i={},o=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var s in e)if("default"!==s&&Object.prototype.hasOwnProperty.call(e,s)){var l=o?Object.getOwnPropertyDescriptor(e,s):null;l&&(l.get||l.set)?Object.defineProperty(i,s,l):i[s]=e[s]}i.default=e,r&&r.set(e,i);return i}(d());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 P(e){return e&&e.__esModule?e:{default:e}}e.helpers=A}(o)),o}var Se=xe();function ke(e){this.content=e}function Me(e,t,n){for(let r=0;;r++){if(r==e.childCount||r==t.childCount)return e.childCount==t.childCount?null:n;let i=e.child(r),o=t.child(r);if(i!=o){if(!i.sameMarkup(o))return n;if(i.isText&&i.text!=o.text){for(let e=0;i.text[e]==o.text[e];e++)n++;return n}if(i.content.size||o.content.size){let e=Me(i.content,o.content,n+1);if(null!=e)return e}n+=i.nodeSize}else n+=i.nodeSize}}function Oe(e,t,n,r){for(let i=e.childCount,o=t.childCount;;){if(0==i||0==o)return i==o?null:{a:n,b:r};let s=e.child(--i),l=t.child(--o),a=s.nodeSize;if(s!=l){if(!s.sameMarkup(l))return{a:n,b:r};if(s.isText&&s.text!=l.text){let e=0,t=Math.min(s.text.length,l.text.length);for(;e>1}},ke.from=function(e){if(e instanceof ke)return e;var t=[];if(e)for(var n in e)t.push(n,e[n]);return new ke(t)};class Ce{constructor(e,t){if(this.content=e,this.size=t||0,null==t)for(let n=0;ne&&!1!==n(l,r+s,i||null,o)&&l.content.size){let i=s+1;l.nodesBetween(Math.max(0,e-i),Math.min(l.content.size,t-i),n,r+i)}s=a}}descendants(e){this.nodesBetween(0,this.size,e)}textBetween(e,t,n,r){let i="",o=!0;return this.nodesBetween(e,t,((s,l)=>{let a=s.isText?s.text.slice(Math.max(e,l)-l,t-l):s.isLeaf?r?"function"==typeof r?r(s):r:s.type.spec.leafText?s.type.spec.leafText(s):"":"";s.isBlock&&(s.isLeaf&&a||s.isTextblock)&&n&&(o?o=!1:i+=n),i+=a}),0),i}append(e){if(!e.size)return this;if(!this.size)return e;let t=this.lastChild,n=e.firstChild,r=this.content.slice(),i=0;for(t.isText&&t.sameMarkup(n)&&(r[r.length-1]=t.withText(t.text+n.text),i=1);ie)for(let i=0,o=0;oe&&((ot)&&(s=s.isText?s.cut(Math.max(0,e-o),Math.min(s.text.length,t-o)):s.cut(Math.max(0,e-o-1),Math.min(s.content.size,t-o-1))),n.push(s),r+=s.nodeSize),o=l}return new Ce(n,r)}cutByIndex(e,t){return e==t?Ce.empty:0==e&&t==this.content.length?this:new Ce(this.content.slice(e,t))}replaceChild(e,t){let n=this.content[e];if(n==t)return this;let r=this.content.slice(),i=this.size+t.nodeSize-n.nodeSize;return r[e]=t,new Ce(r,i)}addToStart(e){return new Ce([e].concat(this.content),this.size+e.nodeSize)}addToEnd(e){return new Ce(this.content.concat(e),this.size+e.nodeSize)}eq(e){if(this.content.length!=e.content.length)return!1;for(let t=0;tthis.size||e<0)throw new RangeError(`Position ${e} outside of fragment (${this})`);for(let n=0,r=0;;n++){let i=r+this.child(n).nodeSize;if(i>=e)return i==e||t>0?De(n+1,i):De(n,r);r=i}}toString(){return"<"+this.toStringInner()+">"}toStringInner(){return this.content.join(", ")}toJSON(){return this.content.length?this.content.map((e=>e.toJSON())):null}static fromJSON(e,t){if(!t)return Ce.empty;if(!Array.isArray(t))throw new RangeError("Invalid input for Fragment.fromJSON");return new Ce(t.map(e.nodeFromJSON))}static fromArray(e){if(!e.length)return Ce.empty;let t,n=0;for(let r=0;rthis.type.rank&&(t||(t=e.slice(0,r)),t.push(this),n=!0),t&&t.push(i)}}return t||(t=e.slice()),n||t.push(this),t}removeFromSet(e){for(let t=0;te.type.rank-t.type.rank)),t}}Ae.none=[];class $e extends Error{}class Ee{constructor(e,t,n){this.content=e,this.openStart=t,this.openEnd=n}get size(){return this.content.size-this.openStart-this.openEnd}insertAt(e,t){let n=Re(this.content,e+this.openStart,t);return n&&new Ee(n,this.openStart,this.openEnd)}removeBetween(e,t){return new Ee(Pe(this.content,e+this.openStart,t+this.openStart),this.openStart,this.openEnd)}eq(e){return this.content.eq(e.content)&&this.openStart==e.openStart&&this.openEnd==e.openEnd}toString(){return this.content+"("+this.openStart+","+this.openEnd+")"}toJSON(){if(!this.content.size)return null;let e={content:this.content.toJSON()};return this.openStart>0&&(e.openStart=this.openStart),this.openEnd>0&&(e.openEnd=this.openEnd),e}static fromJSON(e,t){if(!t)return Ee.empty;let n=t.openStart||0,r=t.openEnd||0;if("number"!=typeof n||"number"!=typeof r)throw new RangeError("Invalid input for Slice.fromJSON");return new Ee(Ce.fromJSON(e,t.content),n,r)}static maxOpen(e,t=!0){let n=0,r=0;for(let i=e.firstChild;i&&!i.isLeaf&&(t||!i.type.spec.isolating);i=i.firstChild)n++;for(let i=e.lastChild;i&&!i.isLeaf&&(t||!i.type.spec.isolating);i=i.lastChild)r++;return new Ee(e,n,r)}}function Pe(e,t,n){let{index:r,offset:i}=e.findIndex(t),o=e.maybeChild(r),{index:s,offset:l}=e.findIndex(n);if(i==t||o.isText){if(l!=n&&!e.child(s).isText)throw new RangeError("Removing non-flat range");return e.cut(0,t).append(e.cut(n))}if(r!=s)throw new RangeError("Removing non-flat range");return e.replaceChild(r,o.copy(Pe(o.content,t-i-1,n-i-1)))}function Re(e,t,n,r){let{index:i,offset:o}=e.findIndex(t),s=e.maybeChild(i);if(o==t||s.isText)return e.cut(0,t).append(n).append(e.cut(t));let l=Re(s.content,t-o-1,n);return l&&e.replaceChild(i,s.copy(l))}function Ie(e,t,n){if(n.openStart>e.depth)throw new $e("Inserted content deeper than insertion position");if(e.depth-n.openStart!=t.depth-n.openEnd)throw new $e("Inconsistent open depths");return ze(e,t,n,0)}function ze(e,t,n,r){let i=e.index(r),o=e.node(r);if(i==t.index(r)&&r=0;i--)r=t.node(i).copy(Ce.from(r));return{start:r.resolveNoCache(e.openStart+n),end:r.resolveNoCache(r.content.size-e.openEnd-n)}}(n,e);return Fe(o,Le(e,i,s,t,r))}{let r=e.parent,i=r.content;return Fe(r,i.cut(0,e.parentOffset).append(n.content).append(i.cut(t.parentOffset)))}}return Fe(o,We(e,t,r))}function _e(e,t){if(!t.type.compatibleContent(e.type))throw new $e("Cannot join "+t.type.name+" onto "+e.type.name)}function Be(e,t,n){let r=e.node(n);return _e(r,t.node(n)),r}function Ve(e,t){let n=t.length-1;n>=0&&e.isText&&e.sameMarkup(t[n])?t[n]=e.withText(t[n].text+e.text):t.push(e)}function je(e,t,n,r){let i=(t||e).node(n),o=0,s=t?t.index(n):i.childCount;e&&(o=e.index(n),e.depth>n?o++:e.textOffset&&(Ve(e.nodeAfter,r),o++));for(let l=o;li&&Be(e,t,i+1),s=r.depth>i&&Be(n,r,i+1),l=[];return je(null,e,i,l),o&&s&&t.index(i)==n.index(i)?(_e(o,s),Ve(Fe(o,Le(e,t,n,r,i+1)),l)):(o&&Ve(Fe(o,We(e,t,i+1)),l),je(t,n,i,l),s&&Ve(Fe(s,We(n,r,i+1)),l)),je(r,null,i,l),new Ce(l)}function We(e,t,n){let r=[];if(je(null,e,n,r),e.depth>n){Ve(Fe(Be(e,t,n+1),We(e,t,n+1)),r)}return je(t,null,n,r),new Ce(r)}Ee.empty=new Ee(Ce.empty,0,0);class qe{constructor(e,t,n){this.pos=e,this.path=t,this.parentOffset=n,this.depth=t.length/3-1}resolveDepth(e){return null==e?this.depth:e<0?this.depth+e:e}get parent(){return this.node(this.depth)}get doc(){return this.node(0)}node(e){return this.path[3*this.resolveDepth(e)]}index(e){return this.path[3*this.resolveDepth(e)+1]}indexAfter(e){return e=this.resolveDepth(e),this.index(e)+(e!=this.depth||this.textOffset?1:0)}start(e){return 0==(e=this.resolveDepth(e))?0:this.path[3*e-1]+1}end(e){return e=this.resolveDepth(e),this.start(e)+this.node(e).content.size}before(e){if(!(e=this.resolveDepth(e)))throw new RangeError("There is no position before the top-level node");return e==this.depth+1?this.pos:this.path[3*e-1]}after(e){if(!(e=this.resolveDepth(e)))throw new RangeError("There is no position after the top-level node");return e==this.depth+1?this.pos:this.path[3*e-1]+this.path[3*e].nodeSize}get textOffset(){return this.pos-this.path[this.path.length-1]}get nodeAfter(){let e=this.parent,t=this.index(this.depth);if(t==e.childCount)return null;let n=this.pos-this.path[this.path.length-1],r=e.child(t);return n?e.child(t).cut(n):r}get nodeBefore(){let e=this.index(this.depth),t=this.pos-this.path[this.path.length-1];return t?this.parent.child(e).cut(0,t):0==e?null:this.parent.child(e-1)}posAtIndex(e,t){t=this.resolveDepth(t);let n=this.path[3*t],r=0==t?0:this.path[3*t-1]+1;for(let i=0;i0;t--)if(this.start(t)<=e&&this.end(t)>=e)return t;return 0}blockRange(e=this,t){if(e.pos=0;n--)if(e.pos<=this.end(n)&&(!t||t(this.node(n))))return new Ye(this,e,n);return null}sameParent(e){return this.pos-this.parentOffset==e.pos-e.parentOffset}max(e){return e.pos>this.pos?e:this}min(e){return e.pos=0&&t<=e.content.size))throw new RangeError("Position "+t+" out of range");let n=[],r=0,i=t;for(let o=e;;){let{index:e,offset:t}=o.content.findIndex(i),s=i-t;if(n.push(o,e,r+t),!s)break;if(o=o.child(e),o.isText)break;i=s-1,r+=t+1}return new qe(t,n,i)}static resolveCached(e,t){let n=He.get(e);if(n)for(let i=0;ie&&this.nodesBetween(e,t,(e=>(n.isInSet(e.marks)&&(r=!0),!r))),r}get isBlock(){return this.type.isBlock}get isTextblock(){return this.type.isTextblock}get inlineContent(){return this.type.inlineContent}get isInline(){return this.type.isInline}get isText(){return this.type.isText}get isLeaf(){return this.type.isLeaf}get isAtom(){return this.type.isAtom}toString(){if(this.type.spec.toDebugString)return this.type.spec.toDebugString(this);let e=this.type.name;return this.content.size&&(e+="("+this.content.toStringInner()+")"),Xe(this.marks,e)}contentMatchAt(e){let t=this.type.contentMatch.matchFragment(this.content,0,e);if(!t)throw new Error("Called contentMatchAt on a node with invalid content");return t}canReplace(e,t,n=Ce.empty,r=0,i=n.childCount){let o=this.contentMatchAt(e).matchFragment(n,r,i),s=o&&o.matchFragment(this.content,t);if(!s||!s.validEnd)return!1;for(let l=r;le.type.name))}`);this.content.forEach((e=>e.check()))}toJSON(){let e={type:this.type.name};for(let t in this.attrs){e.attrs=this.attrs;break}return this.content.size&&(e.content=this.content.toJSON()),this.marks.length&&(e.marks=this.marks.map((e=>e.toJSON()))),e}static fromJSON(e,t){if(!t)throw new RangeError("Invalid input for Node.fromJSON");let n;if(t.marks){if(!Array.isArray(t.marks))throw new RangeError("Invalid mark data for Node.fromJSON");n=t.marks.map(e.markFromJSON)}if("text"==t.type){if("string"!=typeof t.text)throw new RangeError("Invalid text node in JSON");return e.text(t.text,n)}let r=Ce.fromJSON(e,t.content),i=e.nodeType(t.type).create(t.attrs,r,n);return i.type.checkAttrs(i.attrs),i}}Ge.prototype.text=void 0;class Ze extends Ge{constructor(e,t,n,r){if(super(e,t,null,r),!n)throw new RangeError("Empty text nodes are not allowed");this.text=n}toString(){return this.type.spec.toDebugString?this.type.spec.toDebugString(this):Xe(this.marks,JSON.stringify(this.text))}get textContent(){return this.text}textBetween(e,t){return this.text.slice(e,t)}get nodeSize(){return this.text.length}mark(e){return e==this.marks?this:new Ze(this.type,this.attrs,this.text,e)}withText(e){return e==this.text?this:new Ze(this.type,this.attrs,e,this.marks)}cut(e=0,t=this.text.length){return 0==e&&t==this.text.length?this:this.withText(this.text.slice(e,t))}eq(e){return this.sameMarkup(e)&&this.text==e.text}toJSON(){let e=super.toJSON();return e.text=this.text,e}}function Xe(e,t){for(let n=e.length-1;n>=0;n--)t=e[n].type.name+"("+t+")";return t}class Qe{constructor(e){this.validEnd=e,this.next=[],this.wrapCache=[]}static parse(e,t){let n=new et(e,t);if(null==n.next)return Qe.empty;let r=tt(n);n.next&&n.err("Unexpected trailing text");let i=function(e){let t=Object.create(null);return n(lt(e,0));function n(r){let i=[];r.forEach((t=>{e[t].forEach((({term:t,to:n})=>{if(!t)return;let r;for(let e=0;e{r||i.push([t,r=[]]),-1==r.indexOf(e)&&r.push(e)}))}))}));let o=t[r.join(",")]=new Qe(r.indexOf(e.length-1)>-1);for(let e=0;ee.to=t))}function o(e,t){if("choice"==e.type)return e.exprs.reduce(((e,n)=>e.concat(o(n,t))),[]);if("seq"!=e.type){if("star"==e.type){let s=n();return r(t,s),i(o(e.expr,s),s),[r(s)]}if("plus"==e.type){let s=n();return i(o(e.expr,t),s),i(o(e.expr,s),s),[r(s)]}if("opt"==e.type)return[r(t)].concat(o(e.expr,t));if("range"==e.type){let s=t;for(let t=0;te.createAndFill())));for(let e=0;e=this.next.length)throw new RangeError(`There's no ${e}th edge in this content match`);return this.next[e]}toString(){let e=[];return function t(n){e.push(n);for(let r=0;r{let r=n+(t.validEnd?"*":" ")+" ";for(let i=0;i"+e.indexOf(t.next[i].next);return r})).join("\n")}}Qe.empty=new Qe(!0);class et{constructor(e,t){this.string=e,this.nodeTypes=t,this.inline=null,this.pos=0,this.tokens=e.split(/\s*(?=\b|\W|$)/),""==this.tokens[this.tokens.length-1]&&this.tokens.pop(),""==this.tokens[0]&&this.tokens.shift()}get next(){return this.tokens[this.pos]}eat(e){return this.next==e&&(this.pos++||!0)}err(e){throw new SyntaxError(e+" (in content expression '"+this.string+"')")}}function tt(e){let t=[];do{t.push(nt(e))}while(e.eat("|"));return 1==t.length?t[0]:{type:"choice",exprs:t}}function nt(e){let t=[];do{t.push(rt(e))}while(e.next&&")"!=e.next&&"|"!=e.next);return 1==t.length?t[0]:{type:"seq",exprs:t}}function rt(e){let t=function(e){if(e.eat("(")){let t=tt(e);return e.eat(")")||e.err("Missing closing paren"),t}if(!/\W/.test(e.next)){let t=function(e,t){let n=e.nodeTypes,r=n[t];if(r)return[r];let i=[];for(let o in n){let e=n[o];e.isInGroup(t)&&i.push(e)}0==i.length&&e.err("No node type or group '"+t+"' found");return i}(e,e.next).map((t=>(null==e.inline?e.inline=t.isInline:e.inline!=t.isInline&&e.err("Mixing inline and block content"),{type:"name",value:t})));return e.pos++,1==t.length?t[0]:{type:"choice",exprs:t}}e.err("Unexpected token '"+e.next+"'")}(e);for(;;)if(e.eat("+"))t={type:"plus",expr:t};else if(e.eat("*"))t={type:"star",expr:t};else if(e.eat("?"))t={type:"opt",expr:t};else{if(!e.eat("{"))break;t=ot(e,t)}return t}function it(e){/\D/.test(e.next)&&e.err("Expected number, got '"+e.next+"'");let t=Number(e.next);return e.pos++,t}function ot(e,t){let n=it(e),r=n;return e.eat(",")&&(r="}"!=e.next?it(e):-1),e.eat("}")||e.err("Unclosed braced range"),{type:"range",min:n,max:r,expr:t}}function st(e,t){return t-e}function lt(e,t){let n=[];return function t(r){let i=e[r];if(1==i.length&&!i[0].term)return t(i[0].to);n.push(r);for(let e=0;e-1}get whitespace(){return this.spec.whitespace||(this.spec.code?"pre":"normal")}hasRequiredAttrs(){for(let e in this.attrs)if(this.attrs[e].isRequired)return!0;return!1}compatibleContent(e){return this==e||this.contentMatch.compatible(e.contentMatch)}computeAttrs(e){return!e&&this.defaultAttrs?this.defaultAttrs:ct(this.attrs,e)}create(e=null,t,n){if(this.isText)throw new Error("NodeType.create can't construct text nodes");return new Ge(this,this.computeAttrs(e),Ce.from(t),Ae.setFrom(n))}createChecked(e=null,t,n){return t=Ce.from(t),this.checkContent(t),new Ge(this,this.computeAttrs(e),t,Ae.setFrom(n))}createAndFill(e=null,t,n){if(e=this.computeAttrs(e),(t=Ce.from(t)).size){let e=this.contentMatch.fillBefore(t);if(!e)return null;t=e.append(t)}let r=this.contentMatch.matchFragment(t),i=r&&r.fillBefore(Ce.empty,!0);return i?new Ge(this,e,t.append(i),Ae.setFrom(n)):null}validContent(e){let t=this.contentMatch.matchFragment(e);if(!t||!t.validEnd)return!1;for(let n=0;n-1}allowsMarks(e){if(null==this.markSet)return!0;for(let t=0;tr[t]=new e(t,n,i)));let i=n.spec.topNode||"doc";if(!r[i])throw new RangeError("Schema is missing its top node type ('"+i+"')");if(!r.text)throw new RangeError("Every schema needs a 'text' type");for(let e in r.text.attrs)throw new RangeError("The text node type should not have attributes");return r}};class ft{constructor(e,t,n){this.hasDefault=Object.prototype.hasOwnProperty.call(n,"default"),this.default=n.default,this.validate="string"==typeof n.validate?function(e,t,n){let r=n.split("|");return n=>{let i=null===n?"null":typeof n;if(r.indexOf(i)<0)throw new RangeError(`Expected value of type ${r} for attribute ${t} on type ${e}, got ${i}`)}}(e,t,n.validate):n.validate}get isRequired(){return!this.hasDefault}}class pt{constructor(e,t,n,r){this.name=e,this.rank=t,this.schema=n,this.spec=r,this.attrs=ut(e,r.attrs),this.excluded=null;let i=at(this.attrs);this.instance=i?new Ae(this,i):null}create(e=null){return!e&&this.instance?this.instance:new Ae(this,ct(this.attrs,e))}static compile(e,t){let n=Object.create(null),r=0;return e.forEach(((e,i)=>n[e]=new pt(e,r++,t,i))),n}removeFromSet(e){for(var t=0;t-1}}class mt{constructor(e){this.linebreakReplacement=null,this.cached=Object.create(null);let t=this.spec={};for(let r in e)t[r]=e[r];t.nodes=ke.from(e.nodes),t.marks=ke.from(e.marks||{}),this.nodes=dt.compile(this.spec.nodes,this),this.marks=pt.compile(this.spec.marks,this);let n=Object.create(null);for(let r in this.nodes){if(r in this.marks)throw new RangeError(r+" can not be both a node and a mark");let e=this.nodes[r],t=e.spec.content||"",i=e.spec.marks;if(e.contentMatch=n[t]||(n[t]=Qe.parse(t,this.nodes)),e.inlineContent=e.contentMatch.inlineContent,e.spec.linebreakReplacement){if(this.linebreakReplacement)throw new RangeError("Multiple linebreak nodes defined");if(!e.isInline||!e.isLeaf)throw new RangeError("Linebreak replacement nodes must be inline leaf nodes");this.linebreakReplacement=e}e.markSet="_"==i?null:i?gt(this,i.split(" ")):""!=i&&e.inlineContent?null:[]}for(let r in this.marks){let e=this.marks[r],t=e.spec.excludes;e.excluded=null==t?[e]:""==t?[]:gt(this,t.split(" "))}this.nodeFromJSON=this.nodeFromJSON.bind(this),this.markFromJSON=this.markFromJSON.bind(this),this.topNodeType=this.nodes[this.spec.topNode||"doc"],this.cached.wrappings=Object.create(null)}node(e,t=null,n,r){if("string"==typeof e)e=this.nodeType(e);else{if(!(e instanceof dt))throw new RangeError("Invalid node type: "+e);if(e.schema!=this)throw new RangeError("Node type from different schema used ("+e.name+")")}return e.createChecked(t,n,r)}text(e,t){let n=this.nodes.text;return new Ze(n,n.defaultAttrs,e,Ae.setFrom(t))}mark(e,t){return"string"==typeof e&&(e=this.marks[e]),e.create(t)}nodeFromJSON(e){return Ge.fromJSON(this,e)}markFromJSON(e){return Ae.fromJSON(this,e)}nodeType(e){let t=this.nodes[e];if(!t)throw new RangeError("Unknown node type: "+e);return t}}function gt(e,t){let n=[];for(let r=0;r-1)&&n.push(s=r)}if(!s)throw new SyntaxError("Unknown mark type: '"+t[r]+"'")}return n}class yt{constructor(e,t){this.schema=e,this.rules=t,this.tags=[],this.styles=[];let n=this.matchedStyles=[];t.forEach((e=>{if(function(e){return null!=e.tag}(e))this.tags.push(e);else if(function(e){return null!=e.style}(e)){let t=/[^=]*/.exec(e.style)[0];n.indexOf(t)<0&&n.push(t),this.styles.push(e)}})),this.normalizeLists=!this.tags.some((t=>{if(!/^(ul|ol)\b/.test(t.tag)||!t.node)return!1;let n=e.nodes[t.node];return n.contentMatch.matchType(n)}))}parse(e,t={}){let n=new kt(this,t,!1);return n.addAll(e,Ae.none,t.from,t.to),n.finish()}parseSlice(e,t={}){let n=new kt(this,t,!0);return n.addAll(e,Ae.none,t.from,t.to),Ee.maxOpen(n.finish())}matchTag(e,t,n){for(let r=n?this.tags.indexOf(n)+1:0;re.length&&(61!=o.charCodeAt(e.length)||o.slice(e.length+1)!=t))){if(r.getAttrs){let e=r.getAttrs(t);if(!1===e)continue;r.attrs=e||void 0}return r}}}static schemaRules(e){let t=[];function n(e){let n=null==e.priority?50:e.priority,r=0;for(;r{n(e=Ot(e)),e.mark||e.ignore||e.clearMark||(e.mark=r)}))}for(let r in e.nodes){let t=e.nodes[r].spec.parseDOM;t&&t.forEach((e=>{n(e=Ot(e)),e.node||e.ignore||e.mark||(e.node=r)}))}return t}static fromSchema(e){return e.cached.domParser||(e.cached.domParser=new yt(e,yt.schemaRules(e)))}}const vt={address:!0,article:!0,aside:!0,blockquote:!0,canvas:!0,dd:!0,div:!0,dl:!0,fieldset:!0,figcaption:!0,figure:!0,footer:!0,form:!0,h1:!0,h2:!0,h3:!0,h4:!0,h5:!0,h6:!0,header:!0,hgroup:!0,hr:!0,li:!0,noscript:!0,ol:!0,output:!0,p:!0,pre:!0,section:!0,table:!0,tfoot:!0,ul:!0},wt={head:!0,noscript:!0,object:!0,script:!0,style:!0,title:!0},bt={ol:!0,ul:!0};function xt(e,t,n){return null!=t?(t?1:0)|("full"===t?2:0):e&&"pre"==e.whitespace?3:-5&n}class St{constructor(e,t,n,r,i,o){this.type=e,this.attrs=t,this.marks=n,this.solid=r,this.options=o,this.content=[],this.activeMarks=Ae.none,this.match=i||(4&o?null:e.contentMatch)}findWrapping(e){if(!this.match){if(!this.type)return[];let t=this.type.contentMatch.fillBefore(Ce.from(e));if(!t){let t,n=this.type.contentMatch;return(t=n.findWrapping(e.type))?(this.match=n,t):null}this.match=this.type.contentMatch.matchFragment(t)}return this.match.findWrapping(e.type)}finish(e){if(!(1&this.options)){let e,t=this.content[this.content.length-1];if(t&&t.isText&&(e=/[ \t\r\n\u000c]+$/.exec(t.text))){let n=t;t.text.length==e[0].length?this.content.pop():this.content[this.content.length-1]=n.withText(n.text.slice(0,n.text.length-e[0].length))}}let t=Ce.from(this.content);return!e&&this.match&&(t=t.append(this.match.fillBefore(Ce.empty,!0))),this.type?this.type.create(this.attrs,t,this.marks):t}inlineContext(e){return this.type?this.type.inlineContent:this.content.length?this.content[0].isInline:e.parentNode&&!vt.hasOwnProperty(e.parentNode.nodeName.toLowerCase())}}class kt{constructor(e,t,n){this.parser=e,this.options=t,this.isOpen=n,this.open=0,this.localPreserveWS=!1;let r,i=t.topNode,o=xt(null,t.preserveWhitespace,0)|(n?4:0);r=i?new St(i.type,i.attrs,Ae.none,!0,t.topMatch||i.type.contentMatch,o):new St(n?null:e.schema.topNodeType,null,Ae.none,!0,null,o),this.nodes=[r],this.find=t.findPositions,this.needsBlock=!1}get top(){return this.nodes[this.open]}addDOM(e,t){3==e.nodeType?this.addTextNode(e,t):1==e.nodeType&&this.addElement(e,t)}addTextNode(e,t){let n=e.nodeValue,r=this.top,i=2&r.options?"full":this.localPreserveWS||(1&r.options)>0;if("full"===i||r.inlineContext(e)||/[^ \t\r\n\u000c]/.test(n)){if(i)n="full"!==i?n.replace(/\r?\n|\r/g," "):n.replace(/\r\n?/g,"\n");else if(n=n.replace(/[ \t\r\n\u000c]+/g," "),/^[ \t\r\n\u000c]/.test(n)&&this.open==this.nodes.length-1){let t=r.content[r.content.length-1],i=e.previousSibling;(!t||i&&"BR"==i.nodeName||t.isText&&/[ \t\r\n\u000c]$/.test(t.text))&&(n=n.slice(1))}n&&this.insertNode(this.parser.schema.text(n),t),this.findInText(e)}else this.findInside(e)}addElement(e,t,n){let r=this.localPreserveWS,i=this.top;("PRE"==e.tagName||/pre/.test(e.style&&e.style.whiteSpace))&&(this.localPreserveWS=!0);let o,s=e.nodeName.toLowerCase();bt.hasOwnProperty(s)&&this.parser.normalizeLists&&function(e){for(let t=e.firstChild,n=null;t;t=t.nextSibling){let e=1==t.nodeType?t.nodeName.toLowerCase():null;e&&bt.hasOwnProperty(e)&&n?(n.appendChild(t),t=n):"li"==e?n=t:e&&(n=null)}}(e);let l=this.options.ruleFromNode&&this.options.ruleFromNode(e)||(o=this.parser.matchTag(e,this,n));e:if(l?l.ignore:wt.hasOwnProperty(s))this.findInside(e),this.ignoreFallback(e,t);else if(!l||l.skip||l.closeParent){l&&l.closeParent?this.open=Math.max(0,this.open-1):l&&l.skip.nodeType&&(e=l.skip);let n,r=this.needsBlock;if(vt.hasOwnProperty(s))i.content.length&&i.content[0].isInline&&this.open&&(this.open--,i=this.top),n=!0,i.type||(this.needsBlock=!0);else if(!e.firstChild){this.leafFallback(e,t);break e}let o=l&&l.skip?t:this.readStyles(e,t);o&&this.addAll(e,o),n&&this.sync(i),this.needsBlock=r}else{let n=this.readStyles(e,t);n&&this.addElementByRule(e,l,n,!1===l.consuming?o:void 0)}this.localPreserveWS=r}leafFallback(e,t){"BR"==e.nodeName&&this.top.type&&this.top.type.inlineContent&&this.addTextNode(e.ownerDocument.createTextNode("\n"),t)}ignoreFallback(e,t){"BR"!=e.nodeName||this.top.type&&this.top.type.inlineContent||this.findPlace(this.parser.schema.text("-"),t)}readStyles(e,t){let n=e.style;if(n&&n.length)for(let r=0;r!r.clearMark(e))):t.concat(this.parser.schema.marks[r.mark].create(r.attrs)),!1!==r.consuming)break;n=r}}return t}addElementByRule(e,t,n,r){let i,o;if(t.node)if(o=this.parser.schema.nodes[t.node],o.isLeaf)this.insertNode(o.create(t.attrs),n)||this.leafFallback(e,n);else{let e=this.enter(o,t.attrs||null,n,t.preserveWhitespace);e&&(i=!0,n=e)}else{let e=this.parser.schema.marks[t.mark];n=n.concat(e.create(t.attrs))}let s=this.top;if(o&&o.isLeaf)this.findInside(e);else if(r)this.addElement(e,n,r);else if(t.getContent)this.findInside(e),t.getContent(e,this.parser.schema).forEach((e=>this.insertNode(e,n)));else{let r=e;"string"==typeof t.contentElement?r=e.querySelector(t.contentElement):"function"==typeof t.contentElement?r=t.contentElement(e):t.contentElement&&(r=t.contentElement),this.findAround(e,r,!0),this.addAll(r,n),this.findAround(e,r,!1)}i&&this.sync(s)&&this.open--}addAll(e,t,n,r){let i=n||0;for(let o=n?e.childNodes[n]:e.firstChild,s=null==r?null:e.childNodes[r];o!=s;o=o.nextSibling,++i)this.findAtPoint(e,i),this.addDOM(o,t);this.findAtPoint(e,i)}findPlace(e,t){let n,r;for(let i=this.open;i>=0;i--){let t=this.nodes[i],o=t.findWrapping(e);if(o&&(!n||n.length>o.length)&&(n=o,r=t,!o.length))break;if(t.solid)break}if(!n)return null;this.sync(r);for(let i=0;i!(o.type?o.type.allowsMarkType(t.type):Ct(t.type,e))||(l=t.addToSet(l),!1))),this.nodes.push(new St(e,t,l,r,null,s)),this.open++,n}closeExtra(e=!1){let t=this.nodes.length-1;if(t>this.open){for(;t>this.open;t--)this.nodes[t-1].content.push(this.nodes[t].finish(e));this.nodes.length=this.open+1}}finish(){return this.open=0,this.closeExtra(this.isOpen),this.nodes[0].finish(!(!this.isOpen&&!this.options.topOpen))}sync(e){for(let t=this.open;t>=0;t--){if(this.nodes[t]==e)return this.open=t,!0;this.localPreserveWS&&(this.nodes[t].options|=1)}return!1}get currentPos(){this.closeExtra();let e=0;for(let t=this.open;t>=0;t--){let n=this.nodes[t].content;for(let t=n.length-1;t>=0;t--)e+=n[t].nodeSize;t&&e++}return e}findAtPoint(e,t){if(this.find)for(let n=0;n-1)return e.split(/\s*\|\s*/).some(this.matchesContext,this);let t=e.split("/"),n=this.options.context,r=!(this.isOpen||n&&n.parent.type!=this.nodes[0].type),i=-(n?n.depth+1:0)+(r?0:1),o=(e,s)=>{for(;e>=0;e--){let l=t[e];if(""==l){if(e==t.length-1||0==e)continue;for(;s>=i;s--)if(o(e-1,s))return!0;return!1}{let e=s>0||0==s&&r?this.nodes[s].type:n&&s>=i?n.node(s-i).type:null;if(!e||e.name!=l&&!e.isInGroup(l))return!1;s--}}return!0};return o(t.length-1,this.open)}textblockFromContext(){let e=this.options.context;if(e)for(let t=e.depth;t>=0;t--){let n=e.node(t).contentMatchAt(e.indexAfter(t)).defaultType;if(n&&n.isTextblock&&n.defaultAttrs)return n}for(let t in this.parser.schema.nodes){let e=this.parser.schema.nodes[t];if(e.isTextblock&&e.defaultAttrs)return e}}}function Mt(e,t){return(e.matches||e.msMatchesSelector||e.webkitMatchesSelector||e.mozMatchesSelector).call(e,t)}function Ot(e){let t={};for(let n in e)t[n]=e[n];return t}function Ct(e,t){let n=t.schema.nodes;for(let r in n){let i=n[r];if(!i.allowsMarkType(e))continue;let o=[],s=e=>{o.push(e);for(let n=0;n{if(i.length||e.marks.length){let n=0,o=0;for(;n=0;r--){let i=this.serializeMark(e.marks[r],e.isInline,t);i&&((i.contentDOM||i.dom).appendChild(n),n=i.dom)}return n}serializeMark(e,t,n={}){let r=this.marks[e.type.name];return r&&Et(Tt(n),r(e,t),null,e.attrs)}static renderSpec(e,t,n=null,r){return Et(e,t,n,r)}static fromSchema(e){return e.cached.domSerializer||(e.cached.domSerializer=new Nt(this.nodesFromSchema(e),this.marksFromSchema(e)))}static nodesFromSchema(e){let t=Dt(e.nodes);return t.text||(t.text=e=>e.text),t}static marksFromSchema(e){return Dt(e.marks)}}function Dt(e){let t={};for(let n in e){let r=e[n].spec.toDOM;r&&(t[n]=r)}return t}function Tt(e){return e.document||window.document}const At=new WeakMap;function $t(e){let t=At.get(e);return void 0===t&&At.set(e,t=function(e){let t=null;function n(e){if(e&&"object"==typeof e)if(Array.isArray(e))if("string"==typeof e[0])t||(t=[]),t.push(e);else for(let t=0;t-1)throw new RangeError("Using an array from an attribute object as a DOM spec. This may be an attempted cross site scripting attack.");let s,l=o.indexOf(" ");l>0&&(n=o.slice(0,l),o=o.slice(l+1));let a=n?e.createElementNS(n,o):e.createElement(o),c=t[1],h=1;if(c&&"object"==typeof c&&null==c.nodeType&&!Array.isArray(c)){h=2;for(let e in c)if(null!=c[e]){let t=e.indexOf(" ");t>0?a.setAttributeNS(e.slice(0,t),e.slice(t+1),c[e]):a.setAttribute(e,c[e])}}for(let u=h;uh)throw new RangeError("Content hole must be the only child of its parent node");return{dom:a,contentDOM:a}}{let{dom:t,contentDOM:o}=Et(e,i,n,r);if(a.appendChild(t),o){if(s)throw new RangeError("Multiple content holes");s=o}}}return{dom:a,contentDOM:s}}const Pt=Math.pow(2,16);function Rt(e){return 65535&e}class It{constructor(e,t,n){this.pos=e,this.delInfo=t,this.recover=n}get deleted(){return(8&this.delInfo)>0}get deletedBefore(){return(5&this.delInfo)>0}get deletedAfter(){return(6&this.delInfo)>0}get deletedAcross(){return(4&this.delInfo)>0}}class zt{constructor(e,t=!1){if(this.ranges=e,this.inverted=t,!e.length&&zt.empty)return zt.empty}recover(e){let t=0,n=Rt(e);if(!this.inverted)for(let r=0;re)break;let a=this.ranges[s+i],c=this.ranges[s+o],h=l+a;if(e<=h){let i=l+r+((a?e==l?-1:e==h?1:t:t)<0?0:c);if(n)return i;let o=e==(t<0?l:h)?null:s/3+(e-l)*Pt,u=e==l?2:e==h?1:4;return(t<0?e!=l:e!=h)&&(u|=8),new It(i,u,o)}r+=c-a}return n?e+r:new It(e+r,0,null)}touches(e,t){let n=0,r=Rt(t),i=this.inverted?2:1,o=this.inverted?1:2;for(let s=0;se)break;let l=this.ranges[s+i];if(e<=t+l&&s==3*r)return!0;n+=this.ranges[s+o]-l}return!1}forEach(e){let t=this.inverted?2:1,n=this.inverted?1:2;for(let r=0,i=0;r=0;t--){let r=e.getMirror(t);this.appendMap(e.maps[t].invert(),null!=r&&r>t?n-r-1:void 0)}}invert(){let e=new _t;return e.appendMappingInverted(this),e}map(e,t=1){if(this.mirror)return this._map(e,t,!0);for(let n=this.from;ni&&te.isAtom&&t.type.allowsMarkType(this.mark.type)?e.mark(this.mark.addToSet(e.marks)):e),r),t.openStart,t.openEnd);return jt.fromReplace(e,this.from,this.to,i)}invert(){return new Wt(this.from,this.to,this.mark)}map(e){let t=e.mapResult(this.from,1),n=e.mapResult(this.to,-1);return t.deleted&&n.deleted||t.pos>=n.pos?null:new Lt(t.pos,n.pos,this.mark)}merge(e){return e instanceof Lt&&e.mark.eq(this.mark)&&this.from<=e.to&&this.to>=e.from?new Lt(Math.min(this.from,e.from),Math.max(this.to,e.to),this.mark):null}toJSON(){return{stepType:"addMark",mark:this.mark.toJSON(),from:this.from,to:this.to}}static fromJSON(e,t){if("number"!=typeof t.from||"number"!=typeof t.to)throw new RangeError("Invalid input for AddMarkStep.fromJSON");return new Lt(t.from,t.to,e.markFromJSON(t.mark))}}Vt.jsonID("addMark",Lt);class Wt extends Vt{constructor(e,t,n){super(),this.from=e,this.to=t,this.mark=n}apply(e){let t=e.slice(this.from,this.to),n=new Ee(Ft(t.content,(e=>e.mark(this.mark.removeFromSet(e.marks))),e),t.openStart,t.openEnd);return jt.fromReplace(e,this.from,this.to,n)}invert(){return new Lt(this.from,this.to,this.mark)}map(e){let t=e.mapResult(this.from,1),n=e.mapResult(this.to,-1);return t.deleted&&n.deleted||t.pos>=n.pos?null:new Wt(t.pos,n.pos,this.mark)}merge(e){return e instanceof Wt&&e.mark.eq(this.mark)&&this.from<=e.to&&this.to>=e.from?new Wt(Math.min(this.from,e.from),Math.max(this.to,e.to),this.mark):null}toJSON(){return{stepType:"removeMark",mark:this.mark.toJSON(),from:this.from,to:this.to}}static fromJSON(e,t){if("number"!=typeof t.from||"number"!=typeof t.to)throw new RangeError("Invalid input for RemoveMarkStep.fromJSON");return new Wt(t.from,t.to,e.markFromJSON(t.mark))}}Vt.jsonID("removeMark",Wt);class qt extends Vt{constructor(e,t){super(),this.pos=e,this.mark=t}apply(e){let t=e.nodeAt(this.pos);if(!t)return jt.fail("No node at mark step's position");let n=t.type.create(t.attrs,null,this.mark.addToSet(t.marks));return jt.fromReplace(e,this.pos,this.pos+1,new Ee(Ce.from(n),0,t.isLeaf?0:1))}invert(e){let t=e.nodeAt(this.pos);if(t){let e=this.mark.addToSet(t.marks);if(e.length==t.marks.length){for(let n=0;nn.pos?null:new Ht(t.pos,n.pos,r,i,this.slice,this.insert,this.structure)}toJSON(){let e={stepType:"replaceAround",from:this.from,to:this.to,gapFrom:this.gapFrom,gapTo:this.gapTo,insert:this.insert};return this.slice.size&&(e.slice=this.slice.toJSON()),this.structure&&(e.structure=!0),e}static fromJSON(e,t){if("number"!=typeof t.from||"number"!=typeof t.to||"number"!=typeof t.gapFrom||"number"!=typeof t.gapTo||"number"!=typeof t.insert)throw new RangeError("Invalid input for ReplaceAroundStep.fromJSON");return new Ht(t.from,t.to,t.gapFrom,t.gapTo,Ee.fromJSON(e,t.slice),t.insert,!!t.structure)}}function Yt(e,t,n){let r=e.resolve(t),i=n-t,o=r.depth;for(;i>0&&o>0&&r.indexAfter(o)==r.node(o).childCount;)o--,i--;if(i>0){let e=r.node(o).maybeChild(r.indexAfter(o));for(;i>0;){if(!e||e.isLeaf)return!0;e=e.firstChild,i--}}return!1}function Ut(e,t,n,r=n.contentMatch,i=!0){let o=e.doc.nodeAt(t),s=[],l=t+1;for(let a=0;a=0;a--)e.step(s[a])}function Gt(e,t,n){return(0==t||e.canReplace(t,e.childCount))&&(n==e.childCount||e.canReplace(0,n))}function Zt(e){let t=e.parent.content.cutByIndex(e.startIndex,e.endIndex);for(let n=e.depth;;--n){let r=e.$from.node(n),i=e.$from.index(n),o=e.$to.indexAfter(n);if(n{if(i.isText){let s,l=/\r?\n|\r/g;for(;s=l.exec(i.text);){let i=e.mapping.slice(r).map(n+1+o+s.index);e.replaceWith(i,i+1,t.type.schema.linebreakReplacement.create())}}}))}function tn(e,t,n,r){t.forEach(((i,o)=>{if(i.type==i.type.schema.linebreakReplacement){let i=e.mapping.slice(r).map(n+1+o);e.replaceWith(i,i+1,t.type.schema.text("\n"))}}))}function nn(e,t,n=1,r){let i=e.resolve(t),o=i.depth-n,s=r&&r[r.length-1]||i.parent;if(o<0||i.parent.type.spec.isolating||!i.parent.canReplace(i.index(),i.parent.childCount)||!s.type.validContent(i.parent.content.cutByIndex(i.index(),i.parent.childCount)))return!1;for(let c=i.depth-1,h=n-2;c>o;c--,h--){let e=i.node(c),t=i.index(c);if(e.type.spec.isolating)return!1;let n=e.content.cutByIndex(t,e.childCount),o=r&&r[h+1];o&&(n=n.replaceChild(0,o.type.create(o.attrs)));let s=r&&r[h]||e;if(!e.canReplace(t+1,e.childCount)||!s.type.validContent(n))return!1}let l=i.indexAfter(o),a=r&&r[0];return i.node(o).canReplaceWith(l,l,a?a.type:i.node(o+1).type)}function rn(e,t){let n=e.resolve(t),r=n.index();return i=n.nodeBefore,o=n.nodeAfter,!(!i||!o||i.isLeaf||!function(e,t){t.content.size||e.type.compatibleContent(t.type);let n=e.contentMatchAt(e.childCount),{linebreakReplacement:r}=e.type.schema;for(let i=0;i0;r--)this.placed=Ce.from(e.node(r).copy(this.placed))}get depth(){return this.frontier.length-1}fit(){for(;this.unplaced.size;){let e=this.findFittable();e?this.placeNodes(e):this.openMore()||this.dropNode()}let e=this.mustMoveInline(),t=this.placed.size-this.depth-this.$from.depth,n=this.$from,r=this.close(e<0?this.$to:n.doc.resolve(e));if(!r)return null;let i=this.placed,o=n.depth,s=r.depth;for(;o&&s&&1==i.childCount;)i=i.firstChild.content,o--,s--;let l=new Ee(i,o,s);return e>-1?new Ht(n.pos,e,this.$to.pos,this.$to.end(),l,t):l.size||n.pos!=this.$to.pos?new Kt(n.pos,r.pos,l):null}findFittable(){let e=this.unplaced.openStart;for(let t=this.unplaced.content,n=0,r=this.unplaced.openEnd;n1&&(r=0),i.type.spec.isolating&&r<=n){e=n;break}t=i.content}for(let t=1;t<=2;t++)for(let n=1==t?e:this.unplaced.openStart;n>=0;n--){let e,r=null;n?(r=hn(this.unplaced.content,n-1).firstChild,e=r.content):e=this.unplaced.content;let i=e.firstChild;for(let o=this.depth;o>=0;o--){let e,{type:s,match:l}=this.frontier[o],a=null;if(1==t&&(i?l.matchType(i.type)||(a=l.fillBefore(Ce.from(i),!1)):r&&s.compatibleContent(r.type)))return{sliceDepth:n,frontierDepth:o,parent:r,inject:a};if(2==t&&i&&(e=l.findWrapping(i.type)))return{sliceDepth:n,frontierDepth:o,parent:r,wrap:e};if(r&&l.matchType(r.type))break}}}openMore(){let{content:e,openStart:t,openEnd:n}=this.unplaced,r=hn(e,t);return!(!r.childCount||r.firstChild.isLeaf)&&(this.unplaced=new Ee(e,t+1,Math.max(n,r.size+t>=e.size-n?t+1:0)),!0)}dropNode(){let{content:e,openStart:t,openEnd:n}=this.unplaced,r=hn(e,t);if(r.childCount<=1&&t>0){let i=e.size-t<=t+r.size;this.unplaced=new Ee(an(e,t-1,1),t-1,i?t-1:n)}else this.unplaced=new Ee(an(e,t,1),t,n)}placeNodes({sliceDepth:e,frontierDepth:t,parent:n,inject:r,wrap:i}){for(;this.depth>t;)this.closeFrontierNode();if(i)for(let p=0;p1||0==l||e.content.size)&&(h=t,c.push(un(e.mark(u.allowedMarks(e.marks)),1==a?l:0,a==s.childCount?d:-1)))}let f=a==s.childCount;f||(d=-1),this.placed=cn(this.placed,t,Ce.from(c)),this.frontier[t].match=h,f&&d<0&&n&&n.type==this.frontier[this.depth].type&&this.frontier.length>1&&this.closeFrontierNode();for(let p=0,m=s;p1&&r==this.$to.end(--n);)++r;return r}findCloseLevel(e){e:for(let t=Math.min(this.depth,e.depth);t>=0;t--){let{match:n,type:r}=this.frontier[t],i=t=0;n--){let{match:t,type:r}=this.frontier[n],i=dn(e,n,r,t,!0);if(!i||i.childCount)continue e}return{depth:t,fit:o,move:i?e.doc.resolve(e.after(t+1)):e}}}}close(e){let t=this.findCloseLevel(e);if(!t)return null;for(;this.depth>t.depth;)this.closeFrontierNode();t.fit.childCount&&(this.placed=cn(this.placed,t.depth,t.fit)),e=t.move;for(let n=t.depth+1;n<=e.depth;n++){let t=e.node(n),r=t.type.contentMatch.fillBefore(t.content,!0,e.index(n));this.openFrontierNode(t.type,t.attrs,r)}return e}openFrontierNode(e,t=null,n){let r=this.frontier[this.depth];r.match=r.match.matchType(e),this.placed=cn(this.placed,this.depth,Ce.from(e.create(t,n))),this.frontier.push({type:e,match:e.contentMatch})}closeFrontierNode(){let e=this.frontier.pop().match.fillBefore(Ce.empty,!0);e.childCount&&(this.placed=cn(this.placed,this.frontier.length,e))}}function an(e,t,n){return 0==t?e.cutByIndex(n,e.childCount):e.replaceChild(0,e.firstChild.copy(an(e.firstChild.content,t-1,n)))}function cn(e,t,n){return 0==t?e.append(n):e.replaceChild(e.childCount-1,e.lastChild.copy(cn(e.lastChild.content,t-1,n)))}function hn(e,t){for(let n=0;n1&&(r=r.replaceChild(0,un(r.firstChild,t-1,1==r.childCount?n-1:0))),t>0&&(r=e.type.contentMatch.fillBefore(r).append(r),n<=0&&(r=r.append(e.type.contentMatch.matchFragment(r).fillBefore(Ce.empty,!0)))),e.copy(r)}function dn(e,t,n,r,i){let o=e.node(t),s=i?e.indexAfter(t):e.index(t);if(s==o.childCount&&!n.compatibleContent(o.type))return null;let l=r.fillBefore(o.content,!0,s);return l&&!function(e,t,n){for(let r=n;rr){let t=i.contentMatchAt(0),n=t.fillBefore(e).append(e);e=n.append(t.matchFragment(n).fillBefore(Ce.empty,!0))}return e}function pn(e,t){let n=[];for(let r=Math.min(e.depth,t.depth);r>=0;r--){let i=e.start(r);if(it.pos+(t.depth-r)||e.node(r).type.spec.isolating||t.node(r).type.spec.isolating)break;(i==t.start(r)||r==e.depth&&r==t.depth&&e.parent.inlineContent&&t.parent.inlineContent&&r&&t.start(r-1)==i-1)&&n.push(r)}return n}class mn extends Vt{constructor(e,t,n){super(),this.pos=e,this.attr=t,this.value=n}apply(e){let t=e.nodeAt(this.pos);if(!t)return jt.fail("No node at attribute step's position");let n=Object.create(null);for(let i in t.attrs)n[i]=t.attrs[i];n[this.attr]=this.value;let r=t.type.create(n,null,t.marks);return jt.fromReplace(e,this.pos,this.pos+1,new Ee(Ce.from(r),0,t.isLeaf?0:1))}getMap(){return zt.empty}invert(e){return new mn(this.pos,this.attr,e.nodeAt(this.pos).attrs[this.attr])}map(e){let t=e.mapResult(this.pos,1);return t.deletedAfter?null:new mn(t.pos,this.attr,this.value)}toJSON(){return{stepType:"attr",pos:this.pos,attr:this.attr,value:this.value}}static fromJSON(e,t){if("number"!=typeof t.pos||"string"!=typeof t.attr)throw new RangeError("Invalid input for AttrStep.fromJSON");return new mn(t.pos,t.attr,t.value)}}Vt.jsonID("attr",mn);class gn extends Vt{constructor(e,t){super(),this.attr=e,this.value=t}apply(e){let t=Object.create(null);for(let r in e.attrs)t[r]=e.attrs[r];t[this.attr]=this.value;let n=e.type.create(t,e.content,e.marks);return jt.ok(n)}getMap(){return zt.empty}invert(e){return new gn(this.attr,e.attrs[this.attr])}map(e){return this}toJSON(){return{stepType:"docAttr",attr:this.attr,value:this.value}}static fromJSON(e,t){if("string"!=typeof t.attr)throw new RangeError("Invalid input for DocAttrStep.fromJSON");return new gn(t.attr,t.value)}}Vt.jsonID("docAttr",gn);let yn=class extends Error{};yn=function e(t){let n=Error.call(this,t);return n.__proto__=e.prototype,n},(yn.prototype=Object.create(Error.prototype)).constructor=yn,yn.prototype.name="TransformError";class vn{constructor(e){this.doc=e,this.steps=[],this.docs=[],this.mapping=new _t}get before(){return this.docs.length?this.docs[0]:this.doc}step(e){let t=this.maybeStep(e);if(t.failed)throw new yn(t.failed);return this}maybeStep(e){let t=e.apply(this.doc);return t.failed||this.addStep(e,t.doc),t}get docChanged(){return this.steps.length>0}addStep(e,t){this.docs.push(this.doc),this.steps.push(e),this.mapping.appendMap(e.getMap()),this.doc=t}replace(e,t=e,n=Ee.empty){let r=on(this.doc,e,t,n);return r&&this.step(r),this}replaceWith(e,t,n){return this.replace(e,t,new Ee(Ce.from(n),0,0))}delete(e,t){return this.replace(e,t,Ee.empty)}insert(e,t){return this.replaceWith(e,e,t)}replaceRange(e,t,n){return function(e,t,n,r){if(!r.size)return e.deleteRange(t,n);let i=e.doc.resolve(t),o=e.doc.resolve(n);if(sn(i,o,r))return e.step(new Kt(t,n,r));let s=pn(i,e.doc.resolve(n));0==s[s.length-1]&&s.pop();let l=-(i.depth+1);s.unshift(l);for(let f=i.depth,p=i.pos-1;f>0;f--,p--){let e=i.node(f).type.spec;if(e.defining||e.definingAsContext||e.isolating)break;s.indexOf(f)>-1?l=f:i.before(f)==p&&s.splice(1,0,-f)}let a=s.indexOf(l),c=[],h=r.openStart;for(let f=r.content,p=0;;p++){let e=f.firstChild;if(c.push(e),p==r.openStart)break;f=e.content}for(let f=h-1;f>=0;f--){let e=c[f],t=(u=e.type).spec.defining||u.spec.definingForContent;if(t&&!e.sameMarkup(i.node(Math.abs(l)-1)))h=f;else if(t||!e.type.isTextblock)break}var u;for(let f=r.openStart;f>=0;f--){let t=(f+h+1)%(r.openStart+1),l=c[t];if(l)for(let c=0;c=0&&(e.replace(t,n,r),!(e.steps.length>d));f--){let e=s[f];e<0||(t=i.before(e),n=o.after(e))}}(this,e,t,n),this}replaceRangeWith(e,t,n){return function(e,t,n,r){if(!r.isInline&&t==n&&e.doc.resolve(t).parent.content.size){let i=function(e,t,n){let r=e.resolve(t);if(r.parent.canReplaceWith(r.index(),r.index(),n))return t;if(0==r.parentOffset)for(let i=r.depth-1;i>=0;i--){let e=r.index(i);if(r.node(i).canReplaceWith(e,e,n))return r.before(i+1);if(e>0)return null}if(r.parentOffset==r.parent.content.size)for(let i=r.depth-1;i>=0;i--){let e=r.indexAfter(i);if(r.node(i).canReplaceWith(e,e,n))return r.after(i+1);if(e0&&(n||r.node(t-1).canReplace(r.index(t-1),i.indexAfter(t-1))))return e.delete(r.before(t),i.after(t))}for(let s=1;s<=r.depth&&s<=i.depth;s++)if(t-r.start(s)==r.depth-s&&n>r.end(s)&&i.end(s)-n!=i.depth-s&&r.start(s-1)==i.start(s-1)&&r.node(s-1).canReplace(r.index(s-1),i.index(s-1)))return e.delete(r.before(s),n);e.delete(t,n)}(this,e,t),this}lift(e,t){return function(e,t,n){let{$from:r,$to:i,depth:o}=t,s=r.before(o+1),l=i.after(o+1),a=s,c=l,h=Ce.empty,u=0;for(let p=o,m=!1;p>n;p--)m||r.index(p)>0?(m=!0,h=Ce.from(r.node(p).copy(h)),u++):a--;let d=Ce.empty,f=0;for(let p=o,m=!1;p>n;p--)m||i.after(p+1)=0;s--){if(r.size){let e=n[s].type.contentMatch.matchFragment(r);if(!e||!e.validEnd)throw new RangeError("Wrapper type given to Transform.wrap does not form valid content of its parent wrapper")}r=Ce.from(n[s].type.create(n[s].attrs,r))}let i=t.start,o=t.end;e.step(new Ht(i,o,i,o,new Ee(r,0,0),n.length,!0))}(this,e,t),this}setBlockType(e,t=e,n,r=null){return function(e,t,n,r,i){if(!r.isTextblock)throw new RangeError("Type given to setBlockType should be a textblock");let o=e.steps.length;e.doc.nodesBetween(t,n,((t,n)=>{let s="function"==typeof i?i(t):i;if(t.isTextblock&&!t.hasMarkup(r,s)&&function(e,t,n){let r=e.resolve(t),i=r.index();return r.parent.canReplaceWith(i,i+1,n)}(e.doc,e.mapping.slice(o).map(n),r)){let i=null;if(r.schema.linebreakReplacement){let e="pre"==r.whitespace,t=!!r.contentMatch.matchType(r.schema.linebreakReplacement);e&&!t?i=!1:!e&&t&&(i=!0)}!1===i&&tn(e,t,n,o),Ut(e,e.mapping.slice(o).map(n,1),r,void 0,null===i);let l=e.mapping.slice(o),a=l.map(n,1),c=l.map(n+t.nodeSize,1);return e.step(new Ht(a,c,a+1,c-1,new Ee(Ce.from(r.create(s,null,t.marks)),0,0),1,!0)),!0===i&&en(e,t,n,o),!1}}))}(this,e,t,n,r),this}setNodeMarkup(e,t,n=null,r){return function(e,t,n,r,i){let o=e.doc.nodeAt(t);if(!o)throw new RangeError("No node at given position");n||(n=o.type);let s=n.create(r,null,i||o.marks);if(o.isLeaf)return e.replaceWith(t,t+o.nodeSize,s);if(!n.validContent(o.content))throw new RangeError("Invalid content for node type "+n.name);e.step(new Ht(t,t+o.nodeSize,t+1,t+o.nodeSize-1,new Ee(Ce.from(s),0,0),1,!0))}(this,e,t,n,r),this}setNodeAttribute(e,t,n){return this.step(new mn(e,t,n)),this}setDocAttribute(e,t){return this.step(new gn(e,t)),this}addNodeMark(e,t){return this.step(new qt(e,t)),this}removeNodeMark(e,t){if(!(t instanceof Ae)){let n=this.doc.nodeAt(e);if(!n)throw new RangeError("No node at position "+e);if(!(t=t.isInSet(n.marks)))return this}return this.step(new Jt(e,t)),this}split(e,t=1,n){return function(e,t,n=1,r){let i=e.doc.resolve(t),o=Ce.empty,s=Ce.empty;for(let l=i.depth,a=i.depth-n,c=n-1;l>a;l--,c--){o=Ce.from(i.node(l).copy(o));let e=r&&r[c];s=Ce.from(e?e.type.create(e.attrs,s):i.node(l).copy(s))}e.step(new Kt(t,t,new Ee(o.append(s),n,n),!0))}(this,e,t,n),this}addMark(e,t,n){return function(e,t,n,r){let i,o,s=[],l=[];e.doc.nodesBetween(t,n,((e,a,c)=>{if(!e.isInline)return;let h=e.marks;if(!r.isInSet(h)&&c.type.allowsMarkType(r.type)){let c=Math.max(a,t),u=Math.min(a+e.nodeSize,n),d=r.addToSet(h);for(let e=0;ee.step(t))),l.forEach((t=>e.step(t)))}(this,e,t,n),this}removeMark(e,t,n){return function(e,t,n,r){let i=[],o=0;e.doc.nodesBetween(t,n,((e,s)=>{if(!e.isInline)return;o++;let l=null;if(r instanceof pt){let t,n=e.marks;for(;t=r.isInSet(n);)(l||(l=[])).push(t),n=t.removeFromSet(n)}else r?r.isInSet(e.marks)&&(l=[r]):l=e.marks;if(l&&l.length){let r=Math.min(s+e.nodeSize,n);for(let e=0;ee.step(new Wt(t.from,t.to,t.style))))}(this,e,t,n),this}clearIncompatible(e,t,n){return Ut(this,e,t,n),this}}const wn=Object.create(null);class bn{constructor(e,t,n){this.$anchor=e,this.$head=t,this.ranges=n||[new xn(e.min(t),e.max(t))]}get anchor(){return this.$anchor.pos}get head(){return this.$head.pos}get from(){return this.$from.pos}get to(){return this.$to.pos}get $from(){return this.ranges[0].$from}get $to(){return this.ranges[0].$to}get empty(){let e=this.ranges;for(let t=0;t=0;i--){let r=t<0?An(e.node(0),e.node(i),e.before(i+1),e.index(i),t,n):An(e.node(0),e.node(i),e.after(i+1),e.index(i)+1,t,n);if(r)return r}return null}static near(e,t=1){return this.findFrom(e,t)||this.findFrom(e,-t)||new Dn(e.node(0))}static atStart(e){return An(e,e,0,0,1)||new Dn(e)}static atEnd(e){return An(e,e,e.content.size,e.childCount,-1)||new Dn(e)}static fromJSON(e,t){if(!t||!t.type)throw new RangeError("Invalid input for Selection.fromJSON");let n=wn[t.type];if(!n)throw new RangeError(`No selection type ${t.type} defined`);return n.fromJSON(e,t)}static jsonID(e,t){if(e in wn)throw new RangeError("Duplicate use of selection JSON ID "+e);return wn[e]=t,t.prototype.jsonID=e,t}getBookmark(){return Mn.between(this.$anchor,this.$head).getBookmark()}}bn.prototype.visible=!0;class xn{constructor(e,t){this.$from=e,this.$to=t}}let Sn=!1;function kn(e){Sn||e.parent.inlineContent||(Sn=!0,console.warn("TextSelection endpoint not pointing into a node with inline content ("+e.parent.type.name+")"))}class Mn extends bn{constructor(e,t=e){kn(e),kn(t),super(e,t)}get $cursor(){return this.$anchor.pos==this.$head.pos?this.$head:null}map(e,t){let n=e.resolve(t.map(this.head));if(!n.parent.inlineContent)return bn.near(n);let r=e.resolve(t.map(this.anchor));return new Mn(r.parent.inlineContent?r:n,n)}replace(e,t=Ee.empty){if(super.replace(e,t),t==Ee.empty){let t=this.$from.marksAcross(this.$to);t&&e.ensureMarks(t)}}eq(e){return e instanceof Mn&&e.anchor==this.anchor&&e.head==this.head}getBookmark(){return new On(this.anchor,this.head)}toJSON(){return{type:"text",anchor:this.anchor,head:this.head}}static fromJSON(e,t){if("number"!=typeof t.anchor||"number"!=typeof t.head)throw new RangeError("Invalid input for TextSelection.fromJSON");return new Mn(e.resolve(t.anchor),e.resolve(t.head))}static create(e,t,n=t){let r=e.resolve(t);return new this(r,n==t?r:e.resolve(n))}static between(e,t,n){let r=e.pos-t.pos;if(n&&!r||(n=r>=0?1:-1),!t.parent.inlineContent){let e=bn.findFrom(t,n,!0)||bn.findFrom(t,-n,!0);if(!e)return bn.near(t,n);t=e.$head}return e.parent.inlineContent||(0==r||(e=(bn.findFrom(e,-n,!0)||bn.findFrom(e,n,!0)).$anchor).posnew Dn(e)};function An(e,t,n,r,i,o=!1){if(t.inlineContent)return Mn.create(e,n);for(let s=r-(i>0?0:1);i>0?s=0;s+=i){let r=t.child(s);if(r.isAtom){if(!o&&Cn.isSelectable(r))return Cn.create(e,n-(i<0?r.nodeSize:0))}else{let t=An(e,r,n+i,i<0?r.childCount:0,i,o);if(t)return t}n+=r.nodeSize*i}return null}function $n(e,t,n){let r=e.steps.length-1;if(r{null==i&&(i=r)})),e.setSelection(bn.near(e.doc.resolve(i),n)))}class En extends vn{constructor(e){super(e.doc),this.curSelectionFor=0,this.updated=0,this.meta=Object.create(null),this.time=Date.now(),this.curSelection=e.selection,this.storedMarks=e.storedMarks}get selection(){return this.curSelectionFor0}setStoredMarks(e){return this.storedMarks=e,this.updated|=2,this}ensureMarks(e){return Ae.sameSet(this.storedMarks||this.selection.$from.marks(),e)||this.setStoredMarks(e),this}addStoredMark(e){return this.ensureMarks(e.addToSet(this.storedMarks||this.selection.$head.marks()))}removeStoredMark(e){return this.ensureMarks(e.removeFromSet(this.storedMarks||this.selection.$head.marks()))}get storedMarksSet(){return(2&this.updated)>0}addStep(e,t){super.addStep(e,t),this.updated=-3&this.updated,this.storedMarks=null}setTime(e){return this.time=e,this}replaceSelection(e){return this.selection.replace(this,e),this}replaceSelectionWith(e,t=!0){let n=this.selection;return t&&(e=e.mark(this.storedMarks||(n.empty?n.$from.marks():n.$from.marksAcross(n.$to)||Ae.none))),n.replaceWith(this,e),this}deleteSelection(){return this.selection.replace(this),this}insertText(e,t,n){let r=this.doc.type.schema;if(null==t)return e?this.replaceSelectionWith(r.text(e),!0):this.deleteSelection();{if(null==n&&(n=t),n=null==n?t:n,!e)return this.deleteRange(t,n);let i=this.storedMarks;if(!i){let e=this.doc.resolve(t);i=n==t?e.marks():e.marksAcross(this.doc.resolve(n))}return this.replaceRangeWith(t,n,r.text(e,i)),this.selection.empty||this.setSelection(bn.near(this.selection.$to)),this}}setMeta(e,t){return this.meta["string"==typeof e?e:e.key]=t,this}getMeta(e){return this.meta["string"==typeof e?e:e.key]}get isGeneric(){for(let e in this.meta)return!1;return!0}scrollIntoView(){return this.updated|=4,this}get scrolledIntoView(){return(4&this.updated)>0}}function Pn(e,t){return t&&e?e.bind(t):e}class Rn{constructor(e,t,n){this.name=e,this.init=Pn(t.init,n),this.apply=Pn(t.apply,n)}}const In=[new Rn("doc",{init:e=>e.doc||e.schema.topNodeType.createAndFill(),apply:e=>e.doc}),new Rn("selection",{init:(e,t)=>e.selection||bn.atStart(t.doc),apply:e=>e.selection}),new Rn("storedMarks",{init:e=>e.storedMarks||null,apply:(e,t,n,r)=>r.selection.$cursor?e.storedMarks:null}),new Rn("scrollToSelection",{init:()=>0,apply:(e,t)=>e.scrolledIntoView?t+1:t})];class zn{constructor(e,t){this.schema=e,this.plugins=[],this.pluginsByKey=Object.create(null),this.fields=In.slice(),t&&t.forEach((e=>{if(this.pluginsByKey[e.key])throw new RangeError("Adding different instances of a keyed plugin ("+e.key+")");this.plugins.push(e),this.pluginsByKey[e.key]=e,e.spec.state&&this.fields.push(new Rn(e.key,e.spec.state,e))}))}}class _n{constructor(e){this.config=e}get schema(){return this.config.schema}get plugins(){return this.config.plugins}apply(e){return this.applyTransaction(e).state}filterTransaction(e,t=-1){for(let n=0;ne.toJSON()))),e&&"object"==typeof e)for(let n in e){if("doc"==n||"selection"==n)throw new RangeError("The JSON fields `doc` and `selection` are reserved");let r=e[n],i=r.spec.state;i&&i.toJSON&&(t[n]=i.toJSON.call(r,this[r.key]))}return t}static fromJSON(e,t,n){if(!t)throw new RangeError("Invalid input for EditorState.fromJSON");if(!e.schema)throw new RangeError("Required config field 'schema' missing");let r=new zn(e.schema,e.plugins),i=new _n(r);return r.fields.forEach((r=>{if("doc"==r.name)i.doc=Ge.fromJSON(e.schema,t.doc);else if("selection"==r.name)i.selection=bn.fromJSON(i.doc,t.selection);else if("storedMarks"==r.name)t.storedMarks&&(i.storedMarks=t.storedMarks.map(e.schema.markFromJSON));else{if(n)for(let o in n){let s=n[o],l=s.spec.state;if(s.key==r.name&&l&&l.fromJSON&&Object.prototype.hasOwnProperty.call(t,o))return void(i[r.name]=l.fromJSON.call(s,e,t[o],i))}i[r.name]=r.init(e,i)}})),i}}function Bn(e,t,n){for(let r in e){let i=e[r];i instanceof Function?i=i.bind(t):"handleDOMEvents"==r&&(i=Bn(i,t,{})),n[r]=i}return n}class Vn{constructor(e){this.spec=e,this.props={},e.props&&Bn(e.props,this,this.props),this.key=e.key?e.key.key:Fn("plugin")}getState(e){return e[this.key]}}const jn=Object.create(null);function Fn(e){return e in jn?e+"$"+ ++jn[e]:(jn[e]=0,e+"$")}class Ln{constructor(e="key"){this.key=Fn(e)}get(e){return e.config.pluginsByKey[this.key]}getState(e){return e[this.key]}}const Wn=function(e){for(var t=0;;t++)if(!(e=e.previousSibling))return t},qn=function(e){let t=e.assignedSlot||e.parentNode;return t&&11==t.nodeType?t.host:t};let Jn=null;const Kn=function(e,t,n){let r=Jn||(Jn=document.createRange());return r.setEnd(e,null==n?e.nodeValue.length:n),r.setStart(e,t||0),r},Hn=function(e,t,n,r){return n&&(Un(e,t,n,r,-1)||Un(e,t,n,r,1))},Yn=/^(img|br|input|textarea|hr)$/i;function Un(e,t,n,r,i){for(;;){if(e==n&&t==r)return!0;if(t==(i<0?0:Gn(e))){let n=e.parentNode;if(!n||1!=n.nodeType||Zn(e)||Yn.test(e.nodeName)||"false"==e.contentEditable)return!1;t=Wn(e)+(i<0?0:1),e=n}else{if(1!=e.nodeType)return!1;if("false"==(e=e.childNodes[t+(i<0?-1:0)]).contentEditable)return!1;t=i<0?Gn(e):0}}}function Gn(e){return 3==e.nodeType?e.nodeValue.length:e.childNodes.length}function Zn(e){let t;for(let n=e;n&&!(t=n.pmViewDesc);n=n.parentNode);return t&&t.node&&t.node.isBlock&&(t.dom==e||t.contentDOM==e)}const Xn=function(e){return e.focusNode&&Hn(e.focusNode,e.focusOffset,e.anchorNode,e.anchorOffset)};function Qn(e,t){let n=document.createEvent("Event");return n.initEvent("keydown",!0,!0),n.keyCode=e,n.key=n.code=t,n}const er="undefined"!=typeof navigator?navigator:null,tr="undefined"!=typeof document?document:null,nr=er&&er.userAgent||"",rr=/Edge\/(\d+)/.exec(nr),ir=/MSIE \d/.exec(nr),or=/Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(nr),sr=!!(ir||or||rr),lr=ir?document.documentMode:or?+or[1]:rr?+rr[1]:0,ar=!sr&&/gecko\/(\d+)/i.test(nr);ar&&(/Firefox\/(\d+)/.exec(nr)||[0,0])[1];const cr=!sr&&/Chrome\/(\d+)/.exec(nr),hr=!!cr,ur=cr?+cr[1]:0,dr=!sr&&!!er&&/Apple Computer/.test(er.vendor),fr=dr&&(/Mobile\/\w+/.test(nr)||!!er&&er.maxTouchPoints>2),pr=fr||!!er&&/Mac/.test(er.platform),mr=!!er&&/Win/.test(er.platform),gr=/Android \d/.test(nr),yr=!!tr&&"webkitFontSmoothing"in tr.documentElement.style,vr=yr?+(/\bAppleWebKit\/(\d+)/.exec(navigator.userAgent)||[0,0])[1]:0;function wr(e){let t=e.defaultView&&e.defaultView.visualViewport;return t?{left:0,right:t.width,top:0,bottom:t.height}:{left:0,right:e.documentElement.clientWidth,top:0,bottom:e.documentElement.clientHeight}}function br(e,t){return"number"==typeof e?e:e[t]}function xr(e){let t=e.getBoundingClientRect(),n=t.width/e.offsetWidth||1,r=t.height/e.offsetHeight||1;return{left:t.left,right:t.left+e.clientWidth*n,top:t.top,bottom:t.top+e.clientHeight*r}}function Sr(e,t,n){let r=e.someProp("scrollThreshold")||0,i=e.someProp("scrollMargin")||5,o=e.dom.ownerDocument;for(let s=n||e.dom;s;){if(1!=s.nodeType){s=qn(s);continue}let e=s,n=e==o.body,l=n?wr(o):xr(e),a=0,c=0;if(t.topl.bottom-br(r,"bottom")&&(c=t.bottom-t.top>l.bottom-l.top?t.top+br(i,"top")-l.top:t.bottom-l.bottom+br(i,"bottom")),t.leftl.right-br(r,"right")&&(a=t.right-l.right+br(i,"right")),a||c)if(n)o.defaultView.scrollBy(a,c);else{let n=e.scrollLeft,r=e.scrollTop;c&&(e.scrollTop+=c),a&&(e.scrollLeft+=a);let i=e.scrollLeft-n,o=e.scrollTop-r;t={left:t.left-i,top:t.top-o,right:t.right-i,bottom:t.bottom-o}}let h=n?"fixed":getComputedStyle(s).position;if(/^(fixed|sticky)$/.test(h))break;s="absolute"==h?s.offsetParent:qn(s)}}function kr(e){let t=[],n=e.ownerDocument;for(let r=e;r&&(t.push({dom:r,top:r.scrollTop,left:r.scrollLeft}),e!=n);r=qn(r));return t}function Mr(e,t){for(let n=0;n=c){a=Math.max(f.bottom,a),c=Math.min(f.top,c);let e=f.left>t.left?f.left-t.left:f.right=(f.left+f.right)/2?1:0));continue}}else f.top>t.top&&!i&&f.left<=t.left&&f.right>=t.left&&(i=h,o={left:Math.max(f.left,Math.min(f.right,t.left)),top:f.top});!n&&(t.left>=f.right&&t.top>=f.top||t.left>=f.left&&t.top>=f.bottom)&&(l=u+1)}}return!n&&i&&(n=i,r=o,s=0),n&&3==n.nodeType?function(e,t){let n=e.nodeValue.length,r=document.createRange();for(let i=0;i=(n.left+n.right)/2?1:0)}}return{node:e,offset:0}}(n,r):!n||s&&1==n.nodeType?{node:e,offset:l}:Cr(n,r)}function Nr(e,t){return e.left>=t.left-1&&e.left<=t.right+1&&e.top>=t.top-1&&e.top<=t.bottom+1}function Dr(e,t,n){let r=e.childNodes.length;if(r&&n.topt.top&&i++}let r;yr&&i&&1==n.nodeType&&1==(r=n.childNodes[i-1]).nodeType&&"false"==r.contentEditable&&r.getBoundingClientRect().top>=t.top&&i--,n==e.dom&&i==n.childNodes.length-1&&1==n.lastChild.nodeType&&t.top>n.lastChild.getBoundingClientRect().bottom?s=e.state.doc.content.size:0!=i&&1==n.nodeType&&"BR"==n.childNodes[i-1].nodeName||(s=function(e,t,n,r){let i=-1;for(let o=t,s=!1;o!=e.dom;){let t,n=e.docView.nearestDesc(o,!0);if(!n)return null;if(1==n.dom.nodeType&&(n.node.isBlock&&n.parent||!n.contentDOM)&&((t=n.dom.getBoundingClientRect()).width||t.height)&&(n.node.isBlock&&n.parent&&(!s&&t.left>r.left||t.top>r.top?i=n.posBefore:(!s&&t.right-1?i:e.docView.posFromDOM(t,n,-1)}(e,n,i,t))}null==s&&(s=function(e,t,n){let{node:r,offset:i}=Cr(t,n),o=-1;if(1==r.nodeType&&!r.firstChild){let e=r.getBoundingClientRect();o=e.left!=e.right&&n.left>(e.left+e.right)/2?1:-1}return e.docView.posFromDOM(r,i,o)}(e,l,t));let a=e.docView.nearestDesc(l,!0);return{pos:s,inside:a?a.posAtStart-a.border:-1}}function Ar(e){return e.top=0&&i==r.nodeValue.length?(e--,o=1):n<0?e--:t++,Rr($r(Kn(r,e,t),o),o<0)}{let e=$r(Kn(r,i,i),n);if(ar&&i&&/\s/.test(r.nodeValue[i-1])&&i=0)}if(null==o&&i&&(n<0||i==Gn(r))){let e=r.childNodes[i-1],t=3==e.nodeType?Kn(e,Gn(e)-(s?0:1)):1!=e.nodeType||"BR"==e.nodeName&&e.nextSibling?null:e;if(t)return Rr($r(t,1),!1)}if(null==o&&i=0)}function Rr(e,t){if(0==e.width)return e;let n=t?e.left:e.right;return{top:e.top,bottom:e.bottom,left:n,right:n}}function Ir(e,t){if(0==e.height)return e;let n=t?e.top:e.bottom;return{top:n,bottom:n,left:e.left,right:e.right}}function zr(e,t,n){let r=e.state,i=e.root.activeElement;r!=t&&e.updateState(t),i!=e.dom&&e.focus();try{return n()}finally{r!=t&&e.updateState(r),i!=e.dom&&i&&i.focus()}}const _r=/[\u0590-\u08ac]/;let Br=null,Vr=null,jr=!1;function Fr(e,t,n){return Br==t&&Vr==n?jr:(Br=t,Vr=n,jr="up"==n||"down"==n?function(e,t,n){let r=t.selection,i="up"==n?r.$from:r.$to;return zr(e,t,(()=>{let{node:t}=e.docView.domFromPos(i.pos,"up"==n?-1:1);for(;;){let n=e.docView.nearestDesc(t,!0);if(!n)break;if(n.node.isBlock){t=n.contentDOM||n.dom;break}t=n.dom.parentNode}let r=Pr(e,i.pos,1);for(let e=t.firstChild;e;e=e.nextSibling){let t;if(1==e.nodeType)t=e.getClientRects();else{if(3!=e.nodeType)continue;t=Kn(e,0,e.nodeValue.length).getClientRects()}for(let e=0;ei.top+1&&("up"==n?r.top-i.top>2*(i.bottom-r.top):i.bottom-r.bottom>2*(r.bottom-i.top)))return!1}}return!0}))}(e,t,n):function(e,t,n){let{$head:r}=t.selection;if(!r.parent.isTextblock)return!1;let i=r.parentOffset,o=!i,s=i==r.parent.content.size,l=e.domSelection();return l?_r.test(r.parent.textContent)&&l.modify?zr(e,t,(()=>{let{focusNode:t,focusOffset:i,anchorNode:o,anchorOffset:s}=e.domSelectionRange(),a=l.caretBidiLevel;l.modify("move",n,"character");let c=r.depth?e.docView.domAfterPos(r.before()):e.dom,{focusNode:h,focusOffset:u}=e.domSelectionRange(),d=h&&!c.contains(1==h.nodeType?h:h.parentNode)||t==h&&i==u;try{l.collapse(o,s),t&&(t!=o||i!=s)&&l.extend&&l.extend(t,i)}catch(f){}return null!=a&&(l.caretBidiLevel=a),d})):"left"==n||"backward"==n?o:s:r.pos==r.start()||r.pos==r.end()}(e,t,n))}class Lr{constructor(e,t,n,r){this.parent=e,this.children=t,this.dom=n,this.contentDOM=r,this.dirty=0,n.pmViewDesc=this}matchesWidget(e){return!1}matchesMark(e){return!1}matchesNode(e,t,n){return!1}matchesHack(e){return!1}parseRule(){return null}stopEvent(e){return!1}get size(){let e=0;for(let t=0;tWn(this.contentDOM);else if(this.contentDOM&&this.contentDOM!=this.dom&&this.dom.contains(this.contentDOM))r=2&e.compareDocumentPosition(this.contentDOM);else if(this.dom.firstChild){if(0==t)for(let t=e;;t=t.parentNode){if(t==this.dom){r=!1;break}if(t.previousSibling)break}if(null==r&&t==e.childNodes.length)for(let t=e;;t=t.parentNode){if(t==this.dom){r=!0;break}if(t.nextSibling)break}}return(null==r?n>0:r)?this.posAtEnd:this.posAtStart}nearestDesc(e,t=!1){for(let n=!0,r=e;r;r=r.parentNode){let i,o=this.getDesc(r);if(o&&(!t||o.node)){if(!n||!(i=o.nodeDOM)||(1==i.nodeType?i.contains(1==e.nodeType?e:e.parentNode):i==e))return o;n=!1}}}getDesc(e){let t=e.pmViewDesc;for(let n=t;n;n=n.parent)if(n==this)return t}posFromDOM(e,t,n){for(let r=e;r;r=r.parentNode){let i=this.getDesc(r);if(i)return i.localPosFromDOM(e,t,n)}return-1}descAt(e){for(let t=0,n=0;te||t instanceof Ur){r=e-i;break}i=o}if(r)return this.children[n].domFromPos(r-this.children[n].border,t);for(let i;n&&!(i=this.children[n-1]).size&&i instanceof Wr&&i.side>=0;n--);if(t<=0){let e,r=!0;for(;e=n?this.children[n-1]:null,e&&e.dom.parentNode!=this.contentDOM;n--,r=!1);return e&&t&&r&&!e.border&&!e.domAtom?e.domFromPos(e.size,t):{node:this.contentDOM,offset:e?Wn(e.dom)+1:0}}{let e,r=!0;for(;e=n=i&&t<=l-n.border&&n.node&&n.contentDOM&&this.contentDOM.contains(n.contentDOM))return n.parseRange(e,t,i);e=o;for(let t=s;t>0;t--){let n=this.children[t-1];if(n.size&&n.dom.parentNode==this.contentDOM&&!n.emptyChildAt(1)){r=Wn(n.dom)+1;break}e-=n.size}-1==r&&(r=0)}if(r>-1&&(l>t||s==this.children.length-1)){t=l;for(let e=s+1;ep&&ot){let e=s;s=l,l=e}let n=document.createRange();n.setEnd(l.node,l.offset),n.setStart(s.node,s.offset),a.removeAllRanges(),a.addRange(n)}}ignoreMutation(e){return!this.contentDOM&&"selection"!=e.type}get contentLost(){return this.contentDOM&&this.contentDOM!=this.dom&&!this.dom.contains(this.contentDOM)}markDirty(e,t){for(let n=0,r=0;r=n:en){let r=n+i.border,s=o-i.border;if(e>=r&&t<=s)return this.dirty=e==n||t==o?2:1,void(e!=r||t!=s||!i.contentLost&&i.dom.parentNode==this.contentDOM?i.markDirty(e-r,t-r):i.dirty=3);i.dirty=i.dom!=i.contentDOM||i.dom.parentNode!=this.contentDOM||i.children.length?3:2}n=o}this.dirty=2}markParentsDirty(){let e=1;for(let t=this.parent;t;t=t.parent,e++){let n=1==e?2:1;t.dirtyi?i.parent?i.parent.posBeforeChild(i):void 0:r))),!t.type.spec.raw){if(1!=o.nodeType){let e=document.createElement("span");e.appendChild(o),o=e}o.contentEditable="false",o.classList.add("ProseMirror-widget")}super(e,[],o,null),this.widget=t,this.widget=t,i=this}matchesWidget(e){return 0==this.dirty&&e.type.eq(this.widget.type)}parseRule(){return{ignore:!0}}stopEvent(e){let t=this.widget.spec.stopEvent;return!!t&&t(e)}ignoreMutation(e){return"selection"!=e.type||this.widget.spec.ignoreSelection}destroy(){this.widget.type.destroy(this.dom),super.destroy()}get domAtom(){return!0}get side(){return this.widget.type.side}}class qr extends Lr{constructor(e,t,n,r){super(e,[],t,null),this.textDOM=n,this.text=r}get size(){return this.text.length}localPosFromDOM(e,t){return e!=this.textDOM?this.posAtStart+(t?this.size:0):this.posAtStart+t}domFromPos(e){return{node:this.textDOM,offset:e}}ignoreMutation(e){return"characterData"===e.type&&e.target.nodeValue==e.oldValue}}class Jr extends Lr{constructor(e,t,n,r,i){super(e,[],n,r),this.mark=t,this.spec=i}static create(e,t,n,r){let i=r.nodeViews[t.type.name],o=i&&i(t,r,n);return o&&o.dom||(o=Nt.renderSpec(document,t.type.spec.toDOM(t,n),null,t.attrs)),new Jr(e,t,o.dom,o.contentDOM||o.dom,o)}parseRule(){return 3&this.dirty||this.mark.type.spec.reparseInView?null:{mark:this.mark.type.name,attrs:this.mark.attrs,contentElement:this.contentDOM}}matchesMark(e){return 3!=this.dirty&&this.mark.eq(e)}markDirty(e,t){if(super.markDirty(e,t),0!=this.dirty){let e=this.parent;for(;!e.node;)e=e.parent;e.dirty0&&(i=ai(i,0,e,n));for(let s=0;ss?s.parent?s.parent.posBeforeChild(s):void 0:o),n,r),c=a&&a.dom,h=a&&a.contentDOM;if(t.isText)if(c){if(3!=c.nodeType)throw new RangeError("Text must be rendered as a DOM text node")}else c=document.createTextNode(t.text);else if(!c){let e=Nt.renderSpec(document,t.type.spec.toDOM(t),null,t.attrs);({dom:c,contentDOM:h}=e)}h||t.isText||"BR"==c.nodeName||(c.hasAttribute("contenteditable")||(c.contentEditable="false"),t.type.spec.draggable&&(c.draggable=!0));let u=c;return c=ri(c,n,t),a?s=new Gr(e,t,n,r,c,h||null,u,a,i,o+1):t.isText?new Yr(e,t,n,r,c,u,i):new Kr(e,t,n,r,c,h||null,u,i,o+1)}parseRule(){if(this.node.type.spec.reparseInView)return null;let e={node:this.node.type.name,attrs:this.node.attrs};if("pre"==this.node.type.whitespace&&(e.preserveWhitespace="full"),this.contentDOM)if(this.contentLost){for(let t=this.children.length-1;t>=0;t--){let n=this.children[t];if(this.dom.contains(n.dom.parentNode)){e.contentElement=n.dom.parentNode;break}}e.contentElement||(e.getContent=()=>Ce.empty)}else e.contentElement=this.contentDOM;else e.getContent=()=>this.node.content;return e}matchesNode(e,t,n){return 0==this.dirty&&e.eq(this.node)&&ii(t,this.outerDeco)&&n.eq(this.innerDeco)}get size(){return this.node.nodeSize}get border(){return this.node.isLeaf?0:1}updateChildren(e,t){let n=this.node.inlineContent,r=t,i=e.composing?this.localCompositionInfo(e,t):null,o=i&&i.pos>-1?i:null,s=i&&i.pos<0,l=new si(this,o&&o.node,e);!function(e,t,n,r){let i=t.locals(e),o=0;if(0==i.length){for(let n=0;no;)l.push(i[s++]);let p=o+d.nodeSize;if(d.isText){let e=p;s!e.inline)):l.slice(),t.forChild(o,d),f),o=p}}(this.node,this.innerDeco,((t,i,o)=>{t.spec.marks?l.syncToMarks(t.spec.marks,n,e):t.type.side>=0&&!o&&l.syncToMarks(i==this.node.childCount?Ae.none:this.node.child(i).marks,n,e),l.placeWidget(t,e,r)}),((t,o,a,c)=>{let h;l.syncToMarks(t.marks,n,e),l.findNodeMatch(t,o,a,c)||s&&e.state.selection.from>r&&e.state.selection.to-1&&l.updateNodeAt(t,o,a,h,e)||l.updateNextNode(t,o,a,e,c,r)||l.addNode(t,o,a,e,r),r+=t.nodeSize})),l.syncToMarks([],n,e),this.node.isTextblock&&l.addTextblockHacks(),l.destroyRest(),(l.changed||2==this.dirty)&&(o&&this.protectLocalComposition(e,o),Zr(this.contentDOM,this.children,e),fr&&function(e){if("UL"==e.nodeName||"OL"==e.nodeName){let t=e.style.cssText;e.style.cssText=t+"; list-style: square !important",window.getComputedStyle(e).listStyle,e.style.cssText=t}}(this.dom))}localCompositionInfo(e,t){let{from:n,to:r}=e.state.selection;if(!(e.state.selection instanceof Mn)||nt+this.node.content.size)return null;let i=e.input.compositionNode;if(!i||!this.dom.contains(i.parentNode))return null;if(this.node.inlineContent){let e=i.nodeValue,o=function(e,t,n,r){for(let i=0,o=0;i=n){if(o>=r&&a.slice(r-t.length-l,r-l)==t)return r-t.length;let e=l=0&&e+t.length+l>=n)return l+e;if(n==r&&a.length>=r+t.length-l&&a.slice(r-l,r-l+t.length)==t)return r}}return-1}(this.node.content,e,n-t,r-t);return o<0?null:{node:i,pos:o,text:e}}return{node:i,pos:-1,text:""}}protectLocalComposition(e,{node:t,pos:n,text:r}){if(this.getDesc(t))return;let i=t;for(;i.parentNode!=this.contentDOM;i=i.parentNode){for(;i.previousSibling;)i.parentNode.removeChild(i.previousSibling);for(;i.nextSibling;)i.parentNode.removeChild(i.nextSibling);i.pmViewDesc&&(i.pmViewDesc=void 0)}let o=new qr(this,i,t,r);e.input.compositionNodes.push(o),this.children=ai(this.children,n,n+r.length,e,o)}update(e,t,n,r){return!(3==this.dirty||!e.sameMarkup(this.node))&&(this.updateInner(e,t,n,r),!0)}updateInner(e,t,n,r){this.updateOuterDeco(t),this.node=e,this.innerDeco=n,this.contentDOM&&this.updateChildren(r,this.posAtStart),this.dirty=0}updateOuterDeco(e){if(ii(e,this.outerDeco))return;let t=1!=this.nodeDOM.nodeType,n=this.dom;this.dom=ti(this.dom,this.nodeDOM,ei(this.outerDeco,this.node,t),ei(e,this.node,t)),this.dom!=n&&(n.pmViewDesc=void 0,this.dom.pmViewDesc=this),this.outerDeco=e}selectNode(){1==this.nodeDOM.nodeType&&this.nodeDOM.classList.add("ProseMirror-selectednode"),!this.contentDOM&&this.node.type.spec.draggable||(this.dom.draggable=!0)}deselectNode(){1==this.nodeDOM.nodeType&&(this.nodeDOM.classList.remove("ProseMirror-selectednode"),!this.contentDOM&&this.node.type.spec.draggable||this.dom.removeAttribute("draggable"))}get domAtom(){return this.node.isAtom}}function Hr(e,t,n,r,i){ri(r,t,e);let o=new Kr(void 0,e,t,n,r,r,r,i,0);return o.contentDOM&&o.updateChildren(i,0),o}class Yr extends Kr{constructor(e,t,n,r,i,o,s){super(e,t,n,r,i,null,o,s,0)}parseRule(){let e=this.nodeDOM.parentNode;for(;e&&e!=this.dom&&!e.pmIsDeco;)e=e.parentNode;return{skip:e||!0}}update(e,t,n,r){return!(3==this.dirty||0!=this.dirty&&!this.inParent()||!e.sameMarkup(this.node))&&(this.updateOuterDeco(t),0==this.dirty&&e.text==this.node.text||e.text==this.nodeDOM.nodeValue||(this.nodeDOM.nodeValue=e.text,r.trackWrites==this.nodeDOM&&(r.trackWrites=null)),this.node=e,this.dirty=0,!0)}inParent(){let e=this.parent.contentDOM;for(let t=this.nodeDOM;t;t=t.parentNode)if(t==e)return!0;return!1}domFromPos(e){return{node:this.nodeDOM,offset:e}}localPosFromDOM(e,t,n){return e==this.nodeDOM?this.posAtStart+Math.min(t,this.node.text.length):super.localPosFromDOM(e,t,n)}ignoreMutation(e){return"characterData"!=e.type&&"selection"!=e.type}slice(e,t,n){let r=this.node.cut(e,t),i=document.createTextNode(r.text);return new Yr(this.parent,r,this.outerDeco,this.innerDeco,i,i,n)}markDirty(e,t){super.markDirty(e,t),this.dom==this.nodeDOM||0!=e&&t!=this.nodeDOM.nodeValue.length||(this.dirty=3)}get domAtom(){return!1}isText(e){return this.node.text==e}}class Ur extends Lr{parseRule(){return{ignore:!0}}matchesHack(e){return 0==this.dirty&&this.dom.nodeName==e}get domAtom(){return!0}get ignoreForCoords(){return"IMG"==this.dom.nodeName}}class Gr extends Kr{constructor(e,t,n,r,i,o,s,l,a,c){super(e,t,n,r,i,o,s,a,c),this.spec=l}update(e,t,n,r){if(3==this.dirty)return!1;if(this.spec.update&&(this.node.type==e.type||this.spec.multiType)){let i=this.spec.update(e,t,n);return i&&this.updateInner(e,t,n,r),i}return!(!this.contentDOM&&!e.isLeaf)&&super.update(e,t,n,r)}selectNode(){this.spec.selectNode?this.spec.selectNode():super.selectNode()}deselectNode(){this.spec.deselectNode?this.spec.deselectNode():super.deselectNode()}setSelection(e,t,n,r){this.spec.setSelection?this.spec.setSelection(e,t,n.root):super.setSelection(e,t,n,r)}destroy(){this.spec.destroy&&this.spec.destroy(),super.destroy()}stopEvent(e){return!!this.spec.stopEvent&&this.spec.stopEvent(e)}ignoreMutation(e){return this.spec.ignoreMutation?this.spec.ignoreMutation(e):super.ignoreMutation(e)}}function Zr(e,t,n){let r=e.firstChild,i=!1;for(let o=0;o0;){let l;for(;;)if(r){let e=n.children[r-1];if(!(e instanceof Jr)){l=e,r--;break}n=e,r=e.children.length}else{if(n==t)break e;r=n.parent.children.indexOf(n),n=n.parent}let a=l.node;if(a){if(a!=e.child(i-1))break;--i,o.set(l,i),s.push(l)}}return{index:i,matched:o,matches:s.reverse()}}(e.node.content,e)}destroyBetween(e,t){if(e!=t){for(let n=e;n>1,o=Math.min(i,e.length);for(;r-1)r>this.index&&(this.changed=!0,this.destroyBetween(this.index,r)),this.top=this.top.children[this.index];else{let r=Jr.create(this.top,e[i],t,n);this.top.children.splice(this.index,0,r),this.top=r,this.changed=!0}this.index=0,i++}}findNodeMatch(e,t,n,r){let i,o=-1;if(r>=this.preMatch.index&&(i=this.preMatch.matches[r-this.preMatch.index]).parent==this.top&&i.matchesNode(e,t,n))o=this.top.children.indexOf(i,this.index);else for(let s=this.index,l=Math.min(this.top.children.length,s+5);s=n||h<=t?o.push(a):(cn&&o.push(a.slice(n-c,a.size,r)))}return o}function ci(e,t=null){let n=e.domSelectionRange(),r=e.state.doc;if(!n.focusNode)return null;let i=e.docView.nearestDesc(n.focusNode),o=i&&0==i.size,s=e.docView.posFromDOM(n.focusNode,n.focusOffset,1);if(s<0)return null;let l,a,c=r.resolve(s);if(Xn(n)){for(l=s;i&&!i.node;)i=i.parent;let e=i.node;if(i&&e.isAtom&&Cn.isSelectable(e)&&i.parent&&(!e.isInline||!function(e,t,n){for(let r=0==t,i=t==Gn(e);r||i;){if(e==n)return!0;let t=Wn(e);if(!(e=e.parentNode))return!1;r=r&&0==t,i=i&&t==Gn(e)}}(n.focusNode,n.focusOffset,i.dom))){let e=i.posBefore;a=new Cn(s==e?c:r.resolve(e))}}else{if(n instanceof e.dom.ownerDocument.defaultView.Selection&&n.rangeCount>1){let t=s,i=s;for(let r=0;r{n.anchorNode==r&&n.anchorOffset==i||(t.removeEventListener("selectionchange",e.input.hideSelectionGuard),setTimeout((()=>{hi(e)&&!e.state.selection.visible||e.dom.classList.remove("ProseMirror-hideselection")}),20))})}(e))}e.domObserver.setCurSelection(),e.domObserver.connectSelection()}}const di=dr||hr&&ur<63;function fi(e,t){let{node:n,offset:r}=e.docView.domFromPos(t,0),i=rr(e,t,n)))||Mn.between(t,n,r)}function wi(e){return!(e.editable&&!e.hasFocus())&&bi(e)}function bi(e){let t=e.domSelectionRange();if(!t.anchorNode)return!1;try{return e.dom.contains(3==t.anchorNode.nodeType?t.anchorNode.parentNode:t.anchorNode)&&(e.editable||e.dom.contains(3==t.focusNode.nodeType?t.focusNode.parentNode:t.focusNode))}catch(n){return!1}}function xi(e,t){let{$anchor:n,$head:r}=e.selection,i=t>0?n.max(r):n.min(r),o=i.parent.inlineContent?i.depth?e.doc.resolve(t>0?i.after():i.before()):null:i;return o&&bn.findFrom(o,t)}function Si(e,t){return e.dispatch(e.state.tr.setSelection(t).scrollIntoView()),!0}function ki(e,t,n){let r=e.state.selection;if(!(r instanceof Mn)){if(r instanceof Cn&&r.node.isInline)return Si(e,new Mn(t>0?r.$to:r.$from));{let n=xi(e.state,t);return!!n&&Si(e,n)}}if(n.indexOf("s")>-1){let{$head:n}=r,i=n.textOffset?null:t<0?n.nodeBefore:n.nodeAfter;if(!i||i.isText||!i.isLeaf)return!1;let o=e.state.doc.resolve(n.pos+i.nodeSize*(t<0?-1:1));return Si(e,new Mn(r.$anchor,o))}if(!r.empty)return!1;if(e.endOfTextblock(t>0?"forward":"backward")){let n=xi(e.state,t);return!!(n&&n instanceof Cn)&&Si(e,n)}if(!(pr&&n.indexOf("m")>-1)){let n,i=r.$head,o=i.textOffset?null:t<0?i.nodeBefore:i.nodeAfter;if(!o||o.isText)return!1;let s=t<0?i.pos-o.nodeSize:i.pos;return!!(o.isAtom||(n=e.docView.descAt(s))&&!n.contentDOM)&&(Cn.isSelectable(o)?Si(e,new Cn(t<0?e.state.doc.resolve(i.pos-o.nodeSize):i)):!!yr&&Si(e,new Mn(e.state.doc.resolve(t<0?s:s+o.nodeSize))))}}function Mi(e){return 3==e.nodeType?e.nodeValue.length:e.childNodes.length}function Oi(e,t){let n=e.pmViewDesc;return n&&0==n.size&&(t<0||e.nextSibling||"BR"!=e.nodeName)}function Ci(e,t){return t<0?function(e){let t=e.domSelectionRange(),n=t.focusNode,r=t.focusOffset;if(!n)return;let i,o,s=!1;ar&&1==n.nodeType&&r0){if(1!=n.nodeType)break;{let e=n.childNodes[r-1];if(Oi(e,-1))i=n,o=--r;else{if(3!=e.nodeType)break;n=e,r=n.nodeValue.length}}}else{if(Ni(n))break;{let t=n.previousSibling;for(;t&&Oi(t,-1);)i=n.parentNode,o=Wn(t),t=t.previousSibling;if(t)n=t,r=Mi(n);else{if(n=n.parentNode,n==e.dom)break;r=0}}}s?Di(e,n,r):i&&Di(e,i,o)}(e):function(e){let t=e.domSelectionRange(),n=t.focusNode,r=t.focusOffset;if(!n)return;let i,o,s=Mi(n);for(;;)if(r{e.state==i&&ui(e)}),50)}function Ti(e,t){let n=e.state.doc.resolve(t);if(!hr&&!mr&&n.parent.inlineContent){let r=e.coordsAtPos(t);if(t>n.start()){let n=e.coordsAtPos(t-1),i=(n.top+n.bottom)/2;if(i>r.top&&i1)return n.leftr.top&&i1)return n.left>r.left?"ltr":"rtl"}}return"rtl"==getComputedStyle(e.dom).direction?"rtl":"ltr"}function Ai(e,t,n){let r=e.state.selection;if(r instanceof Mn&&!r.empty||n.indexOf("s")>-1)return!1;if(pr&&n.indexOf("m")>-1)return!1;let{$from:i,$to:o}=r;if(!i.parent.inlineContent||e.endOfTextblock(t<0?"up":"down")){let n=xi(e.state,t);if(n&&n instanceof Cn)return Si(e,n)}if(!i.parent.inlineContent){let n=t<0?i:o,s=r instanceof Dn?bn.near(n,t):bn.findFrom(n,t);return!!s&&Si(e,s)}return!1}function $i(e,t){if(!(e.state.selection instanceof Mn))return!0;let{$head:n,$anchor:r,empty:i}=e.state.selection;if(!n.sameParent(r))return!0;if(!i)return!1;if(e.endOfTextblock(t>0?"forward":"backward"))return!0;let o=!n.textOffset&&(t<0?n.nodeBefore:n.nodeAfter);if(o&&!o.isText){let r=e.state.tr;return t<0?r.delete(n.pos-o.nodeSize,n.pos):r.delete(n.pos,n.pos+o.nodeSize),e.dispatch(r),!0}return!1}function Ei(e,t,n){e.domObserver.stop(),t.contentEditable=n,e.domObserver.start()}function Pi(e,t){let n=t.keyCode,r=function(e){let t="";return e.ctrlKey&&(t+="c"),e.metaKey&&(t+="m"),e.altKey&&(t+="a"),e.shiftKey&&(t+="s"),t}(t);if(8==n||pr&&72==n&&"c"==r)return $i(e,-1)||Ci(e,-1);if(46==n&&!t.shiftKey||pr&&68==n&&"c"==r)return $i(e,1)||Ci(e,1);if(13==n||27==n)return!0;if(37==n||pr&&66==n&&"c"==r){let t=37==n?"ltr"==Ti(e,e.state.selection.from)?-1:1:-1;return ki(e,t,r)||Ci(e,t)}if(39==n||pr&&70==n&&"c"==r){let t=39==n?"ltr"==Ti(e,e.state.selection.from)?1:-1:1;return ki(e,t,r)||Ci(e,t)}return 38==n||pr&&80==n&&"c"==r?Ai(e,-1,r)||Ci(e,-1):40==n||pr&&78==n&&"c"==r?function(e){if(!dr||e.state.selection.$head.parentOffset>0)return!1;let{focusNode:t,focusOffset:n}=e.domSelectionRange();if(t&&1==t.nodeType&&0==n&&t.firstChild&&"false"==t.firstChild.contentEditable){let n=t.firstChild;Ei(e,n,"true"),setTimeout((()=>Ei(e,n,"false")),20)}return!1}(e)||Ai(e,1,r)||Ci(e,1):r==(pr?"m":"c")&&(66==n||73==n||89==n||90==n)}function Ri(e,t){e.someProp("transformCopied",(n=>{t=n(t,e)}));let n=[],{content:r,openStart:i,openEnd:o}=t;for(;i>1&&o>1&&1==r.childCount&&1==r.firstChild.childCount;){i--,o--;let e=r.firstChild;n.push(e.type.name,e.attrs!=e.type.defaultAttrs?e.attrs:null),r=e.content}let s=e.someProp("clipboardSerializer")||Nt.fromSchema(e.state.schema),l=qi(),a=l.createElement("div");a.appendChild(s.serializeFragment(r,{document:l}));let c,h=a.firstChild,u=0;for(;h&&1==h.nodeType&&(c=Li[h.nodeName.toLowerCase()]);){for(let e=c.length-1;e>=0;e--){let t=l.createElement(c[e]);for(;a.firstChild;)t.appendChild(a.firstChild);a.appendChild(t),u++}h=a.firstChild}return h&&1==h.nodeType&&h.setAttribute("data-pm-slice",`${i} ${o}${u?` -${u}`:""} ${JSON.stringify(n)}`),{dom:a,text:e.someProp("clipboardTextSerializer",(n=>n(t,e)))||t.content.textBetween(0,t.content.size,"\n\n"),slice:t}}function Ii(e,t,n,r,i){let o,s,l=i.parent.type.spec.code;if(!n&&!t)return null;let a=t&&(r||l||!n);if(a){if(e.someProp("transformPastedText",(n=>{t=n(t,l||r,e)})),l)return t?new Ee(Ce.from(e.state.schema.text(t.replace(/\r\n?/g,"\n"))),0,0):Ee.empty;let n=e.someProp("clipboardTextParser",(n=>n(t,i,r,e)));if(n)s=n;else{let n=i.marks(),{schema:r}=e.state,s=Nt.fromSchema(r);o=document.createElement("div"),t.split(/(?:\r\n?|\n)+/).forEach((e=>{let t=o.appendChild(document.createElement("p"));e&&t.appendChild(s.serializeNode(r.text(e,n)))}))}}else e.someProp("transformPastedHTML",(t=>{n=t(n,e)})),o=function(e){let t=/^(\s*]*>)*/.exec(e);t&&(e=e.slice(t[0].length));let n,r=qi().createElement("div"),i=/<([a-z][^>\s]+)/i.exec(e);(n=i&&Li[i[1].toLowerCase()])&&(e=n.map((e=>"<"+e+">")).join("")+e+n.map((e=>"")).reverse().join(""));if(r.innerHTML=function(e){let t=window.trustedTypes;if(!t)return e;Ji||(Ji=t.createPolicy("ProseMirrorClipboard",{createHTML:e=>e}));return Ji.createHTML(e)}(e),n)for(let o=0;o0;u--){let e=o.firstChild;for(;e&&1!=e.nodeType;)e=e.nextSibling;if(!e)break;o=e}if(!s){let t=e.someProp("clipboardParser")||e.someProp("domParser")||yt.fromSchema(e.state.schema);s=t.parseSlice(o,{preserveWhitespace:!(!a&&!h),context:i,ruleFromNode:e=>"BR"!=e.nodeName||e.nextSibling||!e.parentNode||zi.test(e.parentNode.nodeName)?null:{ignore:!0}})}if(h)s=function(e,t){if(!e.size)return e;let n,r=e.content.firstChild.type.schema;try{n=JSON.parse(t)}catch(l){return e}let{content:i,openStart:o,openEnd:s}=e;for(let a=n.length-2;a>=0;a-=2){let e=r.nodes[n[a]];if(!e||e.hasRequiredAttrs())break;i=Ce.from(e.create(n[a+1],i)),o++,s++}return new Ee(i,o,s)}(Fi(s,+h[1],+h[2]),h[4]);else if(s=Ee.maxOpen(function(e,t){if(e.childCount<2)return e;for(let n=t.depth;n>=0;n--){let r,i=t.node(n).contentMatchAt(t.index(n)),o=[];if(e.forEach((e=>{if(!o)return;let t,n=i.findWrapping(e.type);if(!n)return o=null;if(t=o.length&&r.length&&Bi(n,r,e,o[o.length-1],0))o[o.length-1]=t;else{o.length&&(o[o.length-1]=Vi(o[o.length-1],r.length));let t=_i(e,n);o.push(t),i=i.matchType(t.type),r=n}})),o)return Ce.from(o)}return e}(s.content,i),!0),s.openStart||s.openEnd){let e=0,t=0;for(let n=s.content.firstChild;e{s=t(s,e)})),s}const zi=/^(a|abbr|acronym|b|cite|code|del|em|i|ins|kbd|label|output|q|ruby|s|samp|span|strong|sub|sup|time|u|tt|var)$/i;function _i(e,t,n=0){for(let r=t.length-1;r>=n;r--)e=t[r].create(null,Ce.from(e));return e}function Bi(e,t,n,r,i){if(i1&&(o=0),i=n&&(l=t<0?s.contentMatchAt(0).fillBefore(l,o<=i).append(l):l.append(s.contentMatchAt(s.childCount).fillBefore(Ce.empty,!0))),e.replaceChild(t<0?0:e.childCount-1,s.copy(l))}function Fi(e,t,n){return t{for(let n in t)e.input.eventHandlers[n]||e.dom.addEventListener(n,e.input.eventHandlers[n]=t=>Xi(e,t))}))}function Xi(e,t){return e.someProp("handleDOMEvents",(n=>{let r=n[t.type];return!!r&&(r(e,t)||t.defaultPrevented)}))}function Qi(e,t){if(!t.bubbles)return!0;if(t.defaultPrevented)return!1;for(let n=t.target;n!=e.dom;n=n.parentNode)if(!n||11==n.nodeType||n.pmViewDesc&&n.pmViewDesc.stopEvent(t))return!1;return!0}function eo(e){return{left:e.clientX,top:e.clientY}}function to(e,t,n,r,i){if(-1==r)return!1;let o=e.state.doc.resolve(r);for(let s=o.depth+1;s>0;s--)if(e.someProp(t,(t=>s>o.depth?t(e,n,o.nodeAfter,o.before(s),i,!0):t(e,n,o.node(s),o.before(s),i,!1))))return!0;return!1}function no(e,t,n){if(e.focused||e.focus(),e.state.selection.eq(t))return;let r=e.state.tr.setSelection(t);r.setMeta("pointer",!0),e.dispatch(r)}function ro(e,t,n,r,i){return to(e,"handleClickOn",t,n,r)||e.someProp("handleClick",(n=>n(e,t,r)))||(i?function(e,t){if(-1==t)return!1;let n,r,i=e.state.selection;i instanceof Cn&&(n=i.node);let o=e.state.doc.resolve(t);for(let s=o.depth+1;s>0;s--){let e=s>o.depth?o.nodeAfter:o.node(s);if(Cn.isSelectable(e)){r=n&&i.$from.depth>0&&s>=i.$from.depth&&o.before(i.$from.depth+1)==i.$from.pos?o.before(i.$from.depth):o.before(s);break}}return null!=r&&(no(e,Cn.create(e.state.doc,r)),!0)}(e,n):function(e,t){if(-1==t)return!1;let n=e.state.doc.resolve(t),r=n.nodeAfter;return!!(r&&r.isAtom&&Cn.isSelectable(r))&&(no(e,new Cn(n)),!0)}(e,n))}function io(e,t,n,r){return to(e,"handleDoubleClickOn",t,n,r)||e.someProp("handleDoubleClick",(n=>n(e,t,r)))}function oo(e,t,n,r){return to(e,"handleTripleClickOn",t,n,r)||e.someProp("handleTripleClick",(n=>n(e,t,r)))||function(e,t,n){if(0!=n.button)return!1;let r=e.state.doc;if(-1==t)return!!r.inlineContent&&(no(e,Mn.create(r,0,r.content.size)),!0);let i=r.resolve(t);for(let o=i.depth+1;o>0;o--){let t=o>i.depth?i.nodeAfter:i.node(o),n=i.before(o);if(t.inlineContent)no(e,Mn.create(r,n+1,n+1+t.content.size));else{if(!Cn.isSelectable(t))continue;no(e,Cn.create(r,n))}return!0}}(e,n,r)}function so(e){return mo(e)}Hi.keydown=(e,t)=>{let n=t;if(e.input.shiftKey=16==n.keyCode||n.shiftKey,!co(e,n)&&(e.input.lastKeyCode=n.keyCode,e.input.lastKeyCodeTime=Date.now(),!gr||!hr||13!=n.keyCode))if(229!=n.keyCode&&e.domObserver.forceFlush(),!fr||13!=n.keyCode||n.ctrlKey||n.altKey||n.metaKey)e.someProp("handleKeyDown",(t=>t(e,n)))||Pi(e,n)?n.preventDefault():Gi(e,"key");else{let t=Date.now();e.input.lastIOSEnter=t,e.input.lastIOSEnterFallbackTimeout=setTimeout((()=>{e.input.lastIOSEnter==t&&(e.someProp("handleKeyDown",(t=>t(e,Qn(13,"Enter")))),e.input.lastIOSEnter=0)}),200)}},Hi.keyup=(e,t)=>{16==t.keyCode&&(e.input.shiftKey=!1)},Hi.keypress=(e,t)=>{let n=t;if(co(e,n)||!n.charCode||n.ctrlKey&&!n.altKey||pr&&n.metaKey)return;if(e.someProp("handleKeyPress",(t=>t(e,n))))return void n.preventDefault();let r=e.state.selection;if(!(r instanceof Mn&&r.$from.sameParent(r.$to))){let t=String.fromCharCode(n.charCode);/[\r\n]/.test(t)||e.someProp("handleTextInput",(n=>n(e,r.$from.pos,r.$to.pos,t)))||e.dispatch(e.state.tr.insertText(t).scrollIntoView()),n.preventDefault()}};const lo=pr?"metaKey":"ctrlKey";Ki.mousedown=(e,t)=>{let n=t;e.input.shiftKey=n.shiftKey;let r=so(e),i=Date.now(),o="singleClick";i-e.input.lastClick.time<500&&function(e,t){let n=t.x-e.clientX,r=t.y-e.clientY;return n*n+r*r<100}(n,e.input.lastClick)&&!n[lo]&&("singleClick"==e.input.lastClick.type?o="doubleClick":"doubleClick"==e.input.lastClick.type&&(o="tripleClick")),e.input.lastClick={time:i,x:n.clientX,y:n.clientY,type:o};let s=e.posAtCoords(eo(n));s&&("singleClick"==o?(e.input.mouseDown&&e.input.mouseDown.done(),e.input.mouseDown=new ao(e,s,n,!!r)):("doubleClick"==o?io:oo)(e,s.pos,s.inside,n)?n.preventDefault():Gi(e,"pointer"))};class ao{constructor(e,t,n,r){let i,o;if(this.view=e,this.pos=t,this.event=n,this.flushed=r,this.delayedSelectionSync=!1,this.mightDrag=null,this.startDoc=e.state.doc,this.selectNode=!!n[lo],this.allowDefault=n.shiftKey,t.inside>-1)i=e.state.doc.nodeAt(t.inside),o=t.inside;else{let n=e.state.doc.resolve(t.pos);i=n.parent,o=n.depth?n.before():0}const s=r?null:n.target,l=s?e.docView.nearestDesc(s,!0):null;this.target=l&&1==l.dom.nodeType?l.dom:null;let{selection:a}=e.state;(0==n.button&&i.type.spec.draggable&&!1!==i.type.spec.selectable||a instanceof Cn&&a.from<=o&&a.to>o)&&(this.mightDrag={node:i,pos:o,addAttr:!(!this.target||this.target.draggable),setUneditable:!(!this.target||!ar||this.target.hasAttribute("contentEditable"))}),this.target&&this.mightDrag&&(this.mightDrag.addAttr||this.mightDrag.setUneditable)&&(this.view.domObserver.stop(),this.mightDrag.addAttr&&(this.target.draggable=!0),this.mightDrag.setUneditable&&setTimeout((()=>{this.view.input.mouseDown==this&&this.target.setAttribute("contentEditable","false")}),20),this.view.domObserver.start()),e.root.addEventListener("mouseup",this.up=this.up.bind(this)),e.root.addEventListener("mousemove",this.move=this.move.bind(this)),Gi(e,"pointer")}done(){this.view.root.removeEventListener("mouseup",this.up),this.view.root.removeEventListener("mousemove",this.move),this.mightDrag&&this.target&&(this.view.domObserver.stop(),this.mightDrag.addAttr&&this.target.removeAttribute("draggable"),this.mightDrag.setUneditable&&this.target.removeAttribute("contentEditable"),this.view.domObserver.start()),this.delayedSelectionSync&&setTimeout((()=>ui(this.view))),this.view.input.mouseDown=null}up(e){if(this.done(),!this.view.dom.contains(e.target))return;let t=this.pos;this.view.state.doc!=this.startDoc&&(t=this.view.posAtCoords(eo(e))),this.updateAllowDefault(e),this.allowDefault||!t?Gi(this.view,"pointer"):ro(this.view,t.pos,t.inside,e,this.selectNode)?e.preventDefault():0==e.button&&(this.flushed||dr&&this.mightDrag&&!this.mightDrag.node.isAtom||hr&&!this.view.state.selection.visible&&Math.min(Math.abs(t.pos-this.view.state.selection.from),Math.abs(t.pos-this.view.state.selection.to))<=2)?(no(this.view,bn.near(this.view.state.doc.resolve(t.pos))),e.preventDefault()):Gi(this.view,"pointer")}move(e){this.updateAllowDefault(e),Gi(this.view,"pointer"),0==e.buttons&&this.done()}updateAllowDefault(e){!this.allowDefault&&(Math.abs(this.event.x-e.clientX)>4||Math.abs(this.event.y-e.clientY)>4)&&(this.allowDefault=!0)}}function co(e,t){return!!e.composing||!!(dr&&Math.abs(t.timeStamp-e.input.compositionEndedAt)<500)&&(e.input.compositionEndedAt=-2e8,!0)}Ki.touchstart=e=>{e.input.lastTouch=Date.now(),so(e),Gi(e,"pointer")},Ki.touchmove=e=>{e.input.lastTouch=Date.now(),Gi(e,"pointer")},Ki.contextmenu=e=>so(e);const ho=gr?5e3:-1;function uo(e,t){clearTimeout(e.input.composingTimeout),t>-1&&(e.input.composingTimeout=setTimeout((()=>mo(e)),t))}function fo(e){for(e.composing&&(e.input.composing=!1,e.input.compositionEndedAt=function(){let e=document.createEvent("Event");return e.initEvent("event",!0,!0),e.timeStamp}());e.input.compositionNodes.length>0;)e.input.compositionNodes.pop().markParentsDirty()}function po(e){let t=e.domSelectionRange();if(!t.focusNode)return null;let n=function(e,t){for(;;){if(3==e.nodeType&&t)return e;if(1==e.nodeType&&t>0){if("false"==e.contentEditable)return null;t=Gn(e=e.childNodes[t-1])}else{if(!e.parentNode||Zn(e))return null;t=Wn(e),e=e.parentNode}}}(t.focusNode,t.focusOffset),r=function(e,t){for(;;){if(3==e.nodeType&&t=0)){if(e.domObserver.forceFlush(),fo(e),t||e.docView&&e.docView.dirty){let n=ci(e);return n&&!n.eq(e.state.selection)?e.dispatch(e.state.tr.setSelection(n)):!e.markCursor&&!t||e.state.selection.empty?e.updateState(e.state):e.dispatch(e.state.tr.deleteSelection()),!0}return!1}}Hi.compositionstart=Hi.compositionupdate=e=>{if(!e.composing){e.domObserver.flush();let{state:t}=e,n=t.selection.$to;if(t.selection instanceof Mn&&(t.storedMarks||!n.textOffset&&n.parentOffset&&n.nodeBefore.marks.some((e=>!1===e.type.spec.inclusive))))e.markCursor=e.state.storedMarks||n.marks(),mo(e,!0),e.markCursor=null;else if(mo(e,!t.selection.empty),ar&&t.selection.empty&&n.parentOffset&&!n.textOffset&&n.nodeBefore.marks.length){let t=e.domSelectionRange();for(let n=t.focusNode,r=t.focusOffset;n&&1==n.nodeType&&0!=r;){let t=r<0?n.lastChild:n.childNodes[r-1];if(!t)break;if(3==t.nodeType){let n=e.domSelection();n&&n.collapse(t,t.nodeValue.length);break}n=t,r=-1}}e.input.composing=!0}uo(e,ho)},Hi.compositionend=(e,t)=>{e.composing&&(e.input.composing=!1,e.input.compositionEndedAt=t.timeStamp,e.input.compositionPendingChanges=e.domObserver.pendingRecords().length?e.input.compositionID:0,e.input.compositionNode=null,e.input.compositionPendingChanges&&Promise.resolve().then((()=>e.domObserver.flush())),e.input.compositionID++,uo(e,20))};const go=sr&&lr<15||fr&&vr<604;function yo(e,t,n,r,i){let o=Ii(e,t,n,r,e.state.selection.$from);if(e.someProp("handlePaste",(t=>t(e,i,o||Ee.empty))))return!0;if(!o)return!1;let s=function(e){return 0==e.openStart&&0==e.openEnd&&1==e.content.childCount?e.content.firstChild:null}(o),l=s?e.state.tr.replaceSelectionWith(s,r):e.state.tr.replaceSelection(o);return e.dispatch(l.scrollIntoView().setMeta("paste",!0).setMeta("uiEvent","paste")),!0}function vo(e){let t=e.getData("text/plain")||e.getData("Text");if(t)return t;let n=e.getData("text/uri-list");return n?n.replace(/\r?\n/g," "):""}Ki.copy=Hi.cut=(e,t)=>{let n=t,r=e.state.selection,i="cut"==n.type;if(r.empty)return;let o=go?null:n.clipboardData,s=r.content(),{dom:l,text:a}=Ri(e,s);o?(n.preventDefault(),o.clearData(),o.setData("text/html",l.innerHTML),o.setData("text/plain",a)):function(e,t){if(!e.dom.parentNode)return;let n=e.dom.parentNode.appendChild(document.createElement("div"));n.appendChild(t),n.style.cssText="position: fixed; left: -10000px; top: 10px";let r=getSelection(),i=document.createRange();i.selectNodeContents(t),e.dom.blur(),r.removeAllRanges(),r.addRange(i),setTimeout((()=>{n.parentNode&&n.parentNode.removeChild(n),e.focus()}),50)}(e,l),i&&e.dispatch(e.state.tr.deleteSelection().scrollIntoView().setMeta("uiEvent","cut"))},Hi.paste=(e,t)=>{let n=t;if(e.composing&&!gr)return;let r=go?null:n.clipboardData,i=e.input.shiftKey&&45!=e.input.lastKeyCode;r&&yo(e,vo(r),r.getData("text/html"),i,n)?n.preventDefault():function(e,t){if(!e.dom.parentNode)return;let n=e.input.shiftKey||e.state.selection.$from.parent.type.spec.code,r=e.dom.parentNode.appendChild(document.createElement(n?"textarea":"div"));n||(r.contentEditable="true"),r.style.cssText="position: fixed; left: -10000px; top: 10px",r.focus();let i=e.input.shiftKey&&45!=e.input.lastKeyCode;setTimeout((()=>{e.focus(),r.parentNode&&r.parentNode.removeChild(r),n?yo(e,r.value,null,i,t):yo(e,r.textContent,r.innerHTML,i,t)}),50)}(e,n)};class wo{constructor(e,t,n){this.slice=e,this.move=t,this.node=n}}const bo=pr?"altKey":"ctrlKey";Ki.dragstart=(e,t)=>{let n=t,r=e.input.mouseDown;if(r&&r.done(),!n.dataTransfer)return;let i,o=e.state.selection,s=o.empty?null:e.posAtCoords(eo(n));if(s&&s.pos>=o.from&&s.pos<=(o instanceof Cn?o.to-1:o.to));else if(r&&r.mightDrag)i=Cn.create(e.state.doc,r.mightDrag.pos);else if(n.target&&1==n.target.nodeType){let t=e.docView.nearestDesc(n.target,!0);t&&t.node.type.spec.draggable&&t!=e.docView&&(i=Cn.create(e.state.doc,t.posBefore))}let l=(i||e.state.selection).content(),{dom:a,text:c,slice:h}=Ri(e,l);(!n.dataTransfer.files.length||!hr||ur>120)&&n.dataTransfer.clearData(),n.dataTransfer.setData(go?"Text":"text/html",a.innerHTML),n.dataTransfer.effectAllowed="copyMove",go||n.dataTransfer.setData("text/plain",c),e.dragging=new wo(h,!n[bo],i)},Ki.dragend=e=>{let t=e.dragging;window.setTimeout((()=>{e.dragging==t&&(e.dragging=null)}),50)},Hi.dragover=Hi.dragenter=(e,t)=>t.preventDefault(),Hi.drop=(e,t)=>{let n=t,r=e.dragging;if(e.dragging=null,!n.dataTransfer)return;let i=e.posAtCoords(eo(n));if(!i)return;let o=e.state.doc.resolve(i.pos),s=r&&r.slice;s?e.someProp("transformPasted",(t=>{s=t(s,e)})):s=Ii(e,vo(n.dataTransfer),go?null:n.dataTransfer.getData("text/html"),!1,o);let l=!(!r||n[bo]);if(e.someProp("handleDrop",(t=>t(e,n,s||Ee.empty,l))))return void n.preventDefault();if(!s)return;n.preventDefault();let a=s?function(e,t,n){let r=e.resolve(t);if(!n.content.size)return t;let i=n.content;for(let o=0;o=0;e--){let t=e==r.depth?0:r.pos<=(r.start(e+1)+r.end(e+1))/2?-1:1,n=r.index(e)+(t>0?1:0),s=r.node(e),l=!1;if(1==o)l=s.canReplace(n,n,i);else{let e=s.contentMatchAt(n).findWrapping(i.firstChild.type);l=e&&s.canReplaceWith(n,n,e[0])}if(l)return 0==t?r.pos:t<0?r.before(e+1):r.after(e+1)}return null}(e.state.doc,o.pos,s):o.pos;null==a&&(a=o.pos);let c=e.state.tr;if(l){let{node:e}=r;e?e.replace(c):c.deleteSelection()}let h=c.mapping.map(a),u=0==s.openStart&&0==s.openEnd&&1==s.content.childCount,d=c.doc;if(u?c.replaceRangeWith(h,h,s.content.firstChild):c.replaceRange(h,h,s),c.doc.eq(d))return;let f=c.doc.resolve(h);if(u&&Cn.isSelectable(s.content.firstChild)&&f.nodeAfter&&f.nodeAfter.sameMarkup(s.content.firstChild))c.setSelection(new Cn(f));else{let t=c.mapping.map(a);c.mapping.maps[c.mapping.maps.length-1].forEach(((e,n,r,i)=>t=i)),c.setSelection(vi(e,f,c.doc.resolve(t)))}e.focus(),e.dispatch(c.setMeta("uiEvent","drop"))},Ki.focus=e=>{e.input.lastFocus=Date.now(),e.focused||(e.domObserver.stop(),e.dom.classList.add("ProseMirror-focused"),e.domObserver.start(),e.focused=!0,setTimeout((()=>{e.docView&&e.hasFocus()&&!e.domObserver.currentSelection.eq(e.domSelectionRange())&&ui(e)}),20))},Ki.blur=(e,t)=>{let n=t;e.focused&&(e.domObserver.stop(),e.dom.classList.remove("ProseMirror-focused"),e.domObserver.start(),n.relatedTarget&&e.dom.contains(n.relatedTarget)&&e.domObserver.currentSelection.clear(),e.focused=!1)},Ki.beforeinput=(e,t)=>{if(hr&&gr&&"deleteContentBackward"==t.inputType){e.domObserver.flushSoon();let{domChangeCount:t}=e.input;setTimeout((()=>{if(e.input.domChangeCount!=t)return;if(e.dom.blur(),e.focus(),e.someProp("handleKeyDown",(t=>t(e,Qn(8,"Backspace")))))return;let{$cursor:n}=e.state.selection;n&&n.pos>0&&e.dispatch(e.state.tr.delete(n.pos-1,n.pos).scrollIntoView())}),50)}};for(let os in Hi)Ki[os]=Hi[os];function xo(e,t){if(e==t)return!0;for(let n in e)if(e[n]!==t[n])return!1;for(let n in t)if(!(n in e))return!1;return!0}class So{constructor(e,t){this.toDOM=e,this.spec=t||No,this.side=this.spec.side||0}map(e,t,n,r){let{pos:i,deleted:o}=e.mapResult(t.from+r,this.side<0?-1:1);return o?null:new Oo(i-n,i-n,this)}valid(){return!0}eq(e){return this==e||e instanceof So&&(this.spec.key&&this.spec.key==e.spec.key||this.toDOM==e.toDOM&&xo(this.spec,e.spec))}destroy(e){this.spec.destroy&&this.spec.destroy(e)}}class ko{constructor(e,t){this.attrs=e,this.spec=t||No}map(e,t,n,r){let i=e.map(t.from+r,this.spec.inclusiveStart?-1:1)-n,o=e.map(t.to+r,this.spec.inclusiveEnd?1:-1)-n;return i>=o?null:new Oo(i,o,this)}valid(e,t){return t.from=e&&(!i||i(s.spec))&&n.push(s.copy(s.from+r,s.to+r))}for(let o=0;oe){let s=this.children[o]+1;this.children[o+2].findInner(e-s,t-s,n,r+s,i)}}map(e,t,n){return this==To||0==e.maps.length?this:this.mapInner(e,t,0,0,n||No)}mapInner(e,t,n,r,i){let o;for(let s=0;s{let o=i-r-(n-t);for(let s=0;sr+h-e)continue;let i=l[s]+h-e;n>=i?l[s+1]=t<=i?-2:-1:t>=h&&o&&(l[s]+=o,l[s+1]+=o)}e+=o})),h=n.maps[c].map(h,-1)}let a=!1;for(let c=0;c=r.content.size){a=!0;continue}let u=n.map(e[c+1]+o,-1)-i,{index:d,offset:f}=r.content.findIndex(h),p=r.maybeChild(d);if(p&&f==h&&f+p.nodeSize==u){let r=l[c+2].mapInner(n,p,t+1,e[c]+o+1,s);r!=To?(l[c]=h,l[c+1]=u,l[c+2]=r):(l[c+1]=-2,a=!0)}else a=!0}if(a){let a=function(e,t,n,r,i,o,s){function l(e,t){for(let o=0;o{let s,l=o+n;if(s=Eo(t,e,l)){for(r||(r=this.children.slice());io&&t.to=e){this.children[s]==e&&(n=this.children[s+2]);break}let i=e+1,o=i+t.content.size;for(let s=0;si&&e.type instanceof ko){let t=Math.max(i,e.from)-i,n=Math.min(o,e.to)-i;tn.map(e,t,No)));return Ao.from(n)}forChild(e,t){if(t.isLeaf)return Do.empty;let n=[];for(let r=0;re instanceof Do))?e:e.reduce(((e,t)=>e.concat(t instanceof Do?t:t.members)),[]))}}forEachSet(e){for(let t=0;tn&&o.to{let l=Eo(e,t,s+n);if(l){o=!0;let e=Ro(l,t,n+s+1,r);e!=To&&i.push(s,s+t.nodeSize,e)}}));let s=$o(o?Po(e):e,-n).sort(Io);for(let l=0;l0;)t++;e.splice(t,0,n)}function Bo(e){let t=[];return e.someProp("decorations",(n=>{let r=n(e.state);r&&r!=To&&t.push(r)})),e.cursorWrapper&&t.push(Do.create(e.state.doc,[e.cursorWrapper.deco])),Ao.from(t)}const Vo={childList:!0,characterData:!0,characterDataOldValue:!0,attributes:!0,attributeOldValue:!0,subtree:!0},jo=sr&&lr<=11;class Fo{constructor(){this.anchorNode=null,this.anchorOffset=0,this.focusNode=null,this.focusOffset=0}set(e){this.anchorNode=e.anchorNode,this.anchorOffset=e.anchorOffset,this.focusNode=e.focusNode,this.focusOffset=e.focusOffset}clear(){this.anchorNode=this.focusNode=null}eq(e){return e.anchorNode==this.anchorNode&&e.anchorOffset==this.anchorOffset&&e.focusNode==this.focusNode&&e.focusOffset==this.focusOffset}}class Lo{constructor(e,t){this.view=e,this.handleDOMChange=t,this.queue=[],this.flushingSoon=-1,this.observer=null,this.currentSelection=new Fo,this.onCharData=null,this.suppressingSelectionUpdates=!1,this.lastChangedTextNode=null,this.observer=window.MutationObserver&&new window.MutationObserver((e=>{for(let t=0;t"childList"==e.type&&e.removedNodes.length||"characterData"==e.type&&e.oldValue.length>e.target.nodeValue.length))?this.flushSoon():this.flush()})),jo&&(this.onCharData=e=>{this.queue.push({target:e.target,type:"characterData",oldValue:e.prevValue}),this.flushSoon()}),this.onSelectionChange=this.onSelectionChange.bind(this)}flushSoon(){this.flushingSoon<0&&(this.flushingSoon=window.setTimeout((()=>{this.flushingSoon=-1,this.flush()}),20))}forceFlush(){this.flushingSoon>-1&&(window.clearTimeout(this.flushingSoon),this.flushingSoon=-1,this.flush())}start(){this.observer&&(this.observer.takeRecords(),this.observer.observe(this.view.dom,Vo)),this.onCharData&&this.view.dom.addEventListener("DOMCharacterDataModified",this.onCharData),this.connectSelection()}stop(){if(this.observer){let e=this.observer.takeRecords();if(e.length){for(let t=0;tthis.flush()),20)}this.observer.disconnect()}this.onCharData&&this.view.dom.removeEventListener("DOMCharacterDataModified",this.onCharData),this.disconnectSelection()}connectSelection(){this.view.dom.ownerDocument.addEventListener("selectionchange",this.onSelectionChange)}disconnectSelection(){this.view.dom.ownerDocument.removeEventListener("selectionchange",this.onSelectionChange)}suppressSelectionUpdates(){this.suppressingSelectionUpdates=!0,setTimeout((()=>this.suppressingSelectionUpdates=!1),50)}onSelectionChange(){if(wi(this.view)){if(this.suppressingSelectionUpdates)return ui(this.view);if(sr&&lr<=11&&!this.view.state.selection.empty){let e=this.view.domSelectionRange();if(e.focusNode&&Hn(e.focusNode,e.focusOffset,e.anchorNode,e.anchorOffset))return this.flushSoon()}this.flush()}}setCurSelection(){this.currentSelection.set(this.view.domSelectionRange())}ignoreSelectionChange(e){if(!e.focusNode)return!0;let t,n=new Set;for(let i=e.focusNode;i;i=qn(i))n.add(i);for(let i=e.anchorNode;i;i=qn(i))if(n.has(i)){t=i;break}let r=t&&this.view.docView.nearestDesc(t);return r&&r.ignoreMutation({type:"selection",target:3==t.nodeType?t.parentNode:t})?(this.setCurSelection(),!0):void 0}pendingRecords(){if(this.observer)for(let e of this.observer.takeRecords())this.queue.push(e);return this.queue}flush(){let{view:e}=this;if(!e.docView||this.flushingSoon>-1)return;let t=this.pendingRecords();t.length&&(this.queue=[]);let n=e.domSelectionRange(),r=!this.suppressingSelectionUpdates&&!this.currentSelection.eq(n)&&wi(e)&&!this.ignoreSelectionChange(n),i=-1,o=-1,s=!1,l=[];if(e.editable)for(let c=0;c"BR"==e.nodeName));if(2==t.length){let[e,n]=t;e.parentNode&&e.parentNode.parentNode==n.parentNode?n.remove():e.remove()}else{let{focusNode:n}=this.currentSelection;for(let r of t){let t=r.parentNode;!t||"LI"!=t.nodeName||n&&Ko(e,n)==t||r.remove()}}}let a=null;i<0&&r&&e.input.lastFocus>Date.now()-200&&Math.max(e.input.lastTouch,e.input.lastClick.time)-1||r)&&(i>-1&&(e.docView.markDirty(i,o),function(e){if(Wo.has(e))return;if(Wo.set(e,null),-1!==["normal","nowrap","pre-line"].indexOf(getComputedStyle(e.dom).whiteSpace)){if(e.requiresGeckoHackNode=ar,qo)return;console.warn("ProseMirror expects the CSS white-space property to be set, preferably to 'pre-wrap'. It is recommended to load style/prosemirror.css from the prosemirror-view package."),qo=!0}}(e)),this.handleDOMChange(i,o,s,l),e.docView&&e.docView.dirty?e.updateState(e.state):this.currentSelection.eq(n)||ui(e),this.currentSelection.set(n))}registerMutation(e,t){if(t.indexOf(e.target)>-1)return null;let n=this.view.docView.nearestDesc(e.target);if("attributes"==e.type&&(n==this.view.docView||"contenteditable"==e.attributeName||"style"==e.attributeName&&!e.oldValue&&!e.target.getAttribute("style")))return null;if(!n||n.ignoreMutation(e))return null;if("childList"==e.type){for(let n=0;nDate.now()-50?e.input.lastSelectionOrigin:null,n=ci(e,t);if(n&&!e.state.selection.eq(n)){if(hr&&gr&&13===e.input.lastKeyCode&&Date.now()-100t(e,Qn(13,"Enter")))))return;let r=e.state.tr.setSelection(n);"pointer"==t?r.setMeta("pointer",!0):"key"==t&&r.scrollIntoView(),o&&r.setMeta("composition",o),e.dispatch(r)}return}let s=e.state.doc.resolve(t),l=s.sharedDepth(n);t=s.before(l+1),n=e.state.doc.resolve(n).after(l+1);let a,c,h=e.state.selection,u=function(e,t,n){let r,{node:i,fromOffset:o,toOffset:s,from:l,to:a}=e.docView.parseRange(t,n),c=e.domSelectionRange(),h=c.anchorNode;if(h&&e.dom.contains(1==h.nodeType?h:h.parentNode)&&(r=[{node:h,offset:c.anchorOffset}],Xn(c)||r.push({node:c.focusNode,offset:c.focusOffset})),hr&&8===e.input.lastKeyCode)for(let g=s;g>o;g--){let e=i.childNodes[g-1],t=e.pmViewDesc;if("BR"==e.nodeName&&!t){s=g;break}if(!t||t.size)break}let u=e.state.doc,d=e.someProp("domParser")||yt.fromSchema(e.state.schema),f=u.resolve(l),p=null,m=d.parse(i,{topNode:f.parent,topMatch:f.parent.contentMatchAt(f.index()),topOpen:!0,from:o,to:s,preserveWhitespace:"pre"!=f.parent.type.whitespace||"full",findPositions:r,ruleFromNode:Ho,context:f});if(r&&null!=r[0].pos){let e=r[0].pos,t=r[1]&&r[1].pos;null==t&&(t=e),p={anchor:e+l,head:t+l}}return{doc:m,sel:p,from:l,to:a}}(e,t,n),d=e.state.doc,f=d.slice(u.from,u.to);8===e.input.lastKeyCode&&Date.now()-100=s?o-r:0;o-=e,o&&o=l?o-r:0;o-=t,o&&oDate.now()-225||gr)&&i.some((e=>1==e.nodeType&&!Yo.test(e.nodeName)))&&(!p||p.endA>=p.endB)&&e.someProp("handleKeyDown",(t=>t(e,Qn(13,"Enter")))))return void(e.input.lastIOSEnter=0);if(!p){if(!(r&&h instanceof Mn&&!h.empty&&h.$head.sameParent(h.$anchor))||e.composing||u.sel&&u.sel.anchor!=u.sel.head){if(u.sel){let t=Go(e,e.state.doc,u.sel);if(t&&!t.eq(e.state.selection)){let n=e.state.tr.setSelection(t);o&&n.setMeta("composition",o),e.dispatch(n)}}return}p={start:h.from,endA:h.to,endB:h.to}}e.state.selection.frome.state.selection.from&&p.start<=e.state.selection.from+2&&e.state.selection.from>=u.from?p.start=e.state.selection.from:p.endA=e.state.selection.to-2&&e.state.selection.to<=u.to&&(p.endB+=e.state.selection.to-p.endA,p.endA=e.state.selection.to)),sr&&lr<=11&&p.endB==p.start+1&&p.endA==p.start&&p.start>u.from&&"  "==u.doc.textBetween(p.start-u.from-1,p.start-u.from+1)&&(p.start--,p.endA--,p.endB--);let m,g=u.doc.resolveNoCache(p.start-u.from),y=u.doc.resolveNoCache(p.endB-u.from),v=d.resolve(p.start),w=g.sameParent(y)&&g.parent.inlineContent&&v.end()>=p.endA;if((fr&&e.input.lastIOSEnter>Date.now()-225&&(!w||i.some((e=>"DIV"==e.nodeName||"P"==e.nodeName)))||!w&&g.post(e,Qn(13,"Enter")))))return void(e.input.lastIOSEnter=0);if(e.state.selection.anchor>p.start&&function(e,t,n,r,i){if(n-t<=i.pos-r.pos||Zo(r,!0,!1)n||Zo(s,!0,!1)t(e,Qn(8,"Backspace")))))return void(gr&&hr&&e.domObserver.suppressSelectionUpdates());hr&&p.endB==p.start&&(e.input.lastChromeDelete=Date.now()),gr&&!w&&g.start()!=y.start()&&0==y.parentOffset&&g.depth==y.depth&&u.sel&&u.sel.anchor==u.sel.head&&u.sel.head==p.endA&&(p.endB-=2,y=u.doc.resolveNoCache(p.endB-u.from),setTimeout((()=>{e.someProp("handleKeyDown",(function(t){return t(e,Qn(13,"Enter"))}))}),20));let b,x,S,k=p.start,M=p.endA;if(w)if(g.pos==y.pos)sr&&lr<=11&&0==g.parentOffset&&(e.domObserver.suppressSelectionUpdates(),setTimeout((()=>ui(e)),20)),b=e.state.tr.delete(k,M),x=d.resolve(p.start).marksAcross(d.resolve(p.endA));else if(p.endA==p.endB&&(S=function(e,t){let n,r,i,o=e.firstChild.marks,s=t.firstChild.marks,l=o,a=s;for(let h=0;he.mark(r.addToSet(e.marks));else{if(0!=l.length||1!=a.length)return null;r=a[0],n="remove",i=e=>e.mark(r.removeFromSet(e.marks))}let c=[];for(let h=0;hn(e,k,M,t))))return;b=e.state.tr.insertText(t,k,M)}if(b||(b=e.state.tr.replace(k,M,u.doc.slice(p.start-u.from,p.endB-u.from))),u.sel){let t=Go(e,b.doc,u.sel);t&&!(hr&&e.composing&&t.empty&&(p.start!=p.endB||e.input.lastChromeDeletet.content.size?null:vi(e,t.resolve(n.anchor),t.resolve(n.head))}function Zo(e,t,n){let r=e.depth,i=t?e.end():e.pos;for(;r>0&&(t||e.indexAfter(r)==e.node(r).childCount);)r--,i++,t=!1;if(n){let t=e.node(r).maybeChild(e.indexAfter(r));for(;t&&!t.isLeaf;)t=t.firstChild,i++}return i}function Xo(e){if(2!=e.length)return!1;let t=e.charCodeAt(0),n=e.charCodeAt(1);return t>=56320&&t<=57343&&n>=55296&&n<=56319}class Qo{constructor(e,t){this._root=null,this.focused=!1,this.trackWrites=null,this.mounted=!1,this.markCursor=null,this.cursorWrapper=null,this.lastSelectedViewDesc=void 0,this.input=new Ui,this.prevDirectPlugins=[],this.pluginViews=[],this.requiresGeckoHackNode=!1,this.dragging=null,this._props=t,this.state=t.state,this.directPlugins=t.plugins||[],this.directPlugins.forEach(is),this.dispatch=this.dispatch.bind(this),this.dom=e&&e.mount||document.createElement("div"),e&&(e.appendChild?e.appendChild(this.dom):"function"==typeof e?e(this.dom):e.mount&&(this.mounted=!0)),this.editable=ns(this),ts(this),this.nodeViews=rs(this),this.docView=Hr(this.state.doc,es(this),Bo(this),this.dom,this),this.domObserver=new Lo(this,((e,t,n,r)=>Uo(this,e,t,n,r))),this.domObserver.start(),function(e){for(let t in Ki){let n=Ki[t];e.dom.addEventListener(t,e.input.eventHandlers[t]=t=>{!Qi(e,t)||Xi(e,t)||!e.editable&&t.type in Hi||n(e,t)},Yi[t]?{passive:!0}:void 0)}dr&&e.dom.addEventListener("input",(()=>null)),Zi(e)}(this),this.updatePluginViews()}get composing(){return this.input.composing}get props(){if(this._props.state!=this.state){let e=this._props;this._props={};for(let t in e)this._props[t]=e[t];this._props.state=this.state}return this._props}update(e){e.handleDOMEvents!=this._props.handleDOMEvents&&Zi(this);let t=this._props;this._props=e,e.plugins&&(e.plugins.forEach(is),this.directPlugins=e.plugins),this.updateStateInner(e.state,t)}setProps(e){let t={};for(let n in this._props)t[n]=this._props[n];t.state=this.state;for(let n in e)t[n]=e[n];this.update(t)}updateState(e){this.updateStateInner(e,this._props)}updateStateInner(e,t){var n;let r=this.state,i=!1,o=!1;e.storedMarks&&this.composing&&(fo(this),o=!0),this.state=e;let s=r.plugins!=e.plugins||this._props.plugins!=t.plugins;if(s||this._props.plugins!=t.plugins||this._props.nodeViews!=t.nodeViews){let e=rs(this);(function(e,t){let n=0,r=0;for(let i in e){if(e[i]!=t[i])return!0;n++}for(let i in t)r++;return n!=r})(e,this.nodeViews)&&(this.nodeViews=e,i=!0)}(s||t.handleDOMEvents!=this._props.handleDOMEvents)&&Zi(this),this.editable=ns(this),ts(this);let l=Bo(this),a=es(this),c=r.plugins==e.plugins||r.doc.eq(e.doc)?e.scrollToSelection>r.scrollToSelection?"to selection":"preserve":"reset",h=i||!this.docView.matchesNode(e.doc,a,l);!h&&e.selection.eq(r.selection)||(o=!0);let u="preserve"==c&&o&&null==this.dom.style.overflowAnchor&&function(e){let t,n,r=e.dom.getBoundingClientRect(),i=Math.max(0,r.top);for(let o=(r.left+r.right)/2,s=i+1;s=i-20){t=r,n=l.top;break}}return{refDOM:t,refTop:n,stack:kr(e.dom)}}(this);if(o){this.domObserver.stop();let t=h&&(sr||hr)&&!this.composing&&!r.selection.empty&&!e.selection.empty&&function(e,t){let n=Math.min(e.$anchor.sharedDepth(e.head),t.$anchor.sharedDepth(t.head));return e.$anchor.start(n)!=t.$anchor.start(n)}(r.selection,e.selection);if(h){let n=hr?this.trackWrites=this.domSelectionRange().focusNode:null;this.composing&&(this.input.compositionNode=po(this)),!i&&this.docView.update(e.doc,a,l,this)||(this.docView.updateOuterDeco(a),this.docView.destroy(),this.docView=Hr(e.doc,a,l,this.dom,this)),n&&!this.trackWrites&&(t=!0)}t||!(this.input.mouseDown&&this.domObserver.currentSelection.eq(this.domSelectionRange())&&function(e){let t=e.docView.domFromPos(e.state.selection.anchor,0),n=e.domSelectionRange();return Hn(t.node,t.offset,n.anchorNode,n.anchorOffset)}(this))?ui(this,t):(gi(this,e.selection),this.domObserver.setCurSelection()),this.domObserver.start()}this.updatePluginViews(r),(null===(n=this.dragging)||void 0===n?void 0:n.node)&&!r.doc.eq(e.doc)&&this.updateDraggedNode(this.dragging,r),"reset"==c?this.dom.scrollTop=0:"to selection"==c?this.scrollToSelection():u&&function({refDOM:e,refTop:t,stack:n}){let r=e?e.getBoundingClientRect().top:0;Mr(n,0==r?0:r-t)}(u)}scrollToSelection(){let e=this.domSelectionRange().focusNode;if(e&&this.dom.contains(1==e.nodeType?e:e.parentNode))if(this.someProp("handleScrollToSelection",(e=>e(this))));else if(this.state.selection instanceof Cn){let t=this.docView.domAfterPos(this.state.selection.from);1==t.nodeType&&Sr(this,t.getBoundingClientRect(),e)}else Sr(this,this.coordsAtPos(this.state.selection.head,1),e);else;}destroyPluginViews(){let e;for(;e=this.pluginViews.pop();)e.destroy&&e.destroy()}updatePluginViews(e){if(e&&e.plugins==this.state.plugins&&this.directPlugins==this.prevDirectPlugins)for(let t=0;t0&&this.state.doc.nodeAt(e))==n.node&&(r=e)}this.dragging=new wo(e.slice,e.move,r<0?void 0:Cn.create(this.state.doc,r))}someProp(e,t){let n,r=this._props&&this._props[e];if(null!=r&&(n=t?t(r):r))return n;for(let o=0;ot.ownerDocument.getSelection()),this._root=t;return e||document}updateRoot(){this._root=null}posAtCoords(e){return Tr(this,e)}coordsAtPos(e,t=1){return Pr(this,e,t)}domAtPos(e,t=0){return this.docView.domFromPos(e,t)}nodeDOM(e){let t=this.docView.descAt(e);return t?t.nodeDOM:null}posAtDOM(e,t,n=-1){let r=this.docView.posFromDOM(e,t,n);if(null==r)throw new RangeError("DOM position not inside the editor");return r}endOfTextblock(e,t){return Fr(this,t||this.state,e)}pasteHTML(e,t){return yo(this,"",e,!1,t||new ClipboardEvent("paste"))}pasteText(e,t){return yo(this,e,null,!0,t||new ClipboardEvent("paste"))}serializeForClipboard(e){return Ri(this,e)}destroy(){this.docView&&(!function(e){e.domObserver.stop();for(let t in e.input.eventHandlers)e.dom.removeEventListener(t,e.input.eventHandlers[t]);clearTimeout(e.input.composingTimeout),clearTimeout(e.input.lastIOSEnterFallbackTimeout)}(this),this.destroyPluginViews(),this.mounted?(this.docView.update(this.state.doc,[],Bo(this),this),this.dom.textContent=""):this.dom.parentNode&&this.dom.parentNode.removeChild(this.dom),this.docView.destroy(),this.docView=null,Jn=null)}get isDestroyed(){return null==this.docView}dispatchEvent(e){return function(e,t){Xi(e,t)||!Ki[t.type]||!e.editable&&t.type in Hi||Ki[t.type](e,t)}(this,e)}dispatch(e){let t=this._props.dispatchTransaction;t?t.call(this,e):this.updateState(this.state.apply(e))}domSelectionRange(){let e=this.domSelection();return e?dr&&11===this.root.nodeType&&function(e){let t=e.activeElement;for(;t&&t.shadowRoot;)t=t.shadowRoot.activeElement;return t}(this.dom.ownerDocument)==this.dom&&function(e,t){if(t.getComposedRanges){let n=t.getComposedRanges(e.root)[0];if(n)return Jo(e,n)}let n;function r(e){e.preventDefault(),e.stopImmediatePropagation(),n=e.getTargetRanges()[0]}return e.dom.addEventListener("beforeinput",r,!0),document.execCommand("indent"),e.dom.removeEventListener("beforeinput",r,!0),n?Jo(e,n):null}(this,e)||e:{focusNode:null,focusOffset:0,anchorNode:null,anchorOffset:0}}domSelection(){return this.root.getSelection()}}function es(e){let t=Object.create(null);return t.class="ProseMirror",t.contenteditable=String(e.editable),e.someProp("attributes",(n=>{if("function"==typeof n&&(n=n(e.state)),n)for(let e in n)"class"==e?t.class+=" "+n[e]:"style"==e?t.style=(t.style?t.style+";":"")+n[e]:t[e]||"contenteditable"==e||"nodeName"==e||(t[e]=String(n[e]))})),t.translate||(t.translate="no"),[Oo.node(0,e.state.doc.content.size,t)]}function ts(e){if(e.markCursor){let t=document.createElement("img");t.className="ProseMirror-separator",t.setAttribute("mark-placeholder","true"),t.setAttribute("alt",""),e.cursorWrapper={dom:t,deco:Oo.widget(e.state.selection.from,t,{raw:!0,marks:e.markCursor})}}else e.cursorWrapper=null}function ns(e){return!e.someProp("editable",(t=>!1===t(e.state)))}function rs(e){let t=Object.create(null);function n(e){for(let n in e)Object.prototype.hasOwnProperty.call(t,n)||(t[n]=e[n])}return e.someProp("nodeViews",n),e.someProp("markViews",n),t}function is(e){if(e.spec.state||e.spec.filterTransaction||e.spec.appendTransaction)throw new RangeError("Plugins passed directly to the view must not have a state component")}for(var ss={8:"Backspace",9:"Tab",10:"Enter",12:"NumLock",13:"Enter",16:"Shift",17:"Control",18:"Alt",20:"CapsLock",27:"Escape",32:" ",33:"PageUp",34:"PageDown",35:"End",36:"Home",37:"ArrowLeft",38:"ArrowUp",39:"ArrowRight",40:"ArrowDown",44:"PrintScreen",45:"Insert",46:"Delete",59:";",61:"=",91:"Meta",92:"Meta",106:"*",107:"+",108:",",109:"-",110:".",111:"/",144:"NumLock",145:"ScrollLock",160:"Shift",161:"Shift",162:"Control",163:"Control",164:"Alt",165:"Alt",173:"-",186:";",187:"=",188:",",189:"-",190:".",191:"/",192:"`",219:"[",220:"\\",221:"]",222:"'"},ls={48:")",49:"!",50:"@",51:"#",52:"$",53:"%",54:"^",55:"&",56:"*",57:"(",59:":",61:"+",173:"_",186:":",187:"+",188:"<",189:"_",190:">",191:"?",192:"~",219:"{",220:"|",221:"}",222:'"'},as="undefined"!=typeof navigator&&/Mac/.test(navigator.platform),cs="undefined"!=typeof navigator&&/MSIE \d|Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(navigator.userAgent),hs=0;hs<10;hs++)ss[48+hs]=ss[96+hs]=String(hs);for(hs=1;hs<=24;hs++)ss[hs+111]="F"+hs;for(hs=65;hs<=90;hs++)ss[hs]=String.fromCharCode(hs+32),ls[hs]=String.fromCharCode(hs);for(var us in ss)ls.hasOwnProperty(us)||(ls[us]=ss[us]);const ds="undefined"!=typeof navigator&&/Mac|iP(hone|[oa]d)/.test(navigator.platform);function fs(e){let t,n,r,i,o=e.split(/-(?!$)/),s=o[o.length-1];"Space"==s&&(s=" ");for(let l=0;l127)&&(r=ss[n.keyCode])&&r!=i){let i=t[ps(r,n)];if(i&&i(e.state,e.dispatch,e))return!0}}return!1}}const ys=(e,t)=>!e.selection.empty&&(t&&t(e.tr.deleteSelection().scrollIntoView()),!0);function vs(e,t,n=!1){for(let r=e;r;r="start"==t?r.firstChild:r.lastChild){if(r.isTextblock)return!0;if(n&&1!=r.childCount)return!1}return!1}function ws(e){if(!e.parent.type.spec.isolating)for(let t=e.depth-1;t>=0;t--){if(e.index(t)>0)return e.doc.resolve(e.before(t+1));if(e.node(t).type.spec.isolating)break}return null}function bs(e){if(!e.parent.type.spec.isolating)for(let t=e.depth-1;t>=0;t--){let n=e.node(t);if(e.index(t)+1{let{$from:n,$to:r}=e.selection,i=n.blockRange(r),o=i&&Zt(i);return null!=o&&(t&&t(e.tr.lift(i,o).scrollIntoView()),!0)};function Ss(e){for(let t=0;t{let{$head:n,$anchor:r}=e.selection;if(!n.parent.type.spec.code||!n.sameParent(r))return!1;let i=n.node(-1),o=n.indexAfter(-1),s=Ss(i.contentMatchAt(o));if(!s||!i.canReplaceWith(o,o,s))return!1;if(t){let r=n.after(),i=e.tr.replaceWith(r,r,s.createAndFill());i.setSelection(bn.near(i.doc.resolve(r),1)),t(i.scrollIntoView())}return!0};const Ms=(e,t)=>{let{$from:n,$to:r}=e.selection;if(e.selection instanceof Cn&&e.selection.node.isBlock)return!(!n.parentOffset||!nn(e.doc,n.pos)||(t&&t(e.tr.split(n.pos).scrollIntoView()),0));if(!n.depth)return!1;let i,o,s=[],l=!1,a=!1;for(let d=n.depth;;d--){if(n.node(d).isBlock){l=n.end(d)==n.pos+(n.depth-d),a=n.start(d)==n.pos-(n.depth-d),o=Ss(n.node(d-1).contentMatchAt(n.indexAfter(d-1))),s.unshift(l&&o?{type:o}:null),i=d;break}if(1==d)return!1;s.unshift(null)}let c=e.tr;(e.selection instanceof Mn||e.selection instanceof Dn)&&c.deleteSelection();let h=c.mapping.map(n.pos),u=nn(c.doc,h,s.length,s);if(u||(s[0]=o?{type:o}:null,u=nn(c.doc,h,s.length,s)),c.split(h,s.length,s),!l&&a&&n.node(i).type!=o){let e=c.mapping.map(n.before(i)),t=c.doc.resolve(e);o&&n.node(i-1).canReplaceWith(t.index(),t.index()+1,o)&&c.setNodeMarkup(c.mapping.map(n.before(i)),o)}return t&&t(c.scrollIntoView()),!0};function Os(e,t,n,r){let i,o,s=t.nodeBefore,l=t.nodeAfter,a=s.type.spec.isolating||l.type.spec.isolating;if(!a&&function(e,t,n){let r=t.nodeBefore,i=t.nodeAfter,o=t.index();return!(!(r&&i&&r.type.compatibleContent(i.type))||(!r.content.size&&t.parent.canReplace(o-1,o)?(n&&n(e.tr.delete(t.pos-r.nodeSize,t.pos).scrollIntoView()),0):!t.parent.canReplace(o,o+1)||!i.isTextblock&&!rn(e.doc,t.pos)||(n&&n(e.tr.join(t.pos).scrollIntoView()),0)))}(e,t,n))return!0;let c=!a&&t.parent.canReplace(t.index(),t.index()+1);if(c&&(i=(o=s.contentMatchAt(s.childCount)).findWrapping(l.type))&&o.matchType(i[0]||l.type).validEnd){if(n){let r=t.pos+l.nodeSize,o=Ce.empty;for(let e=i.length-1;e>=0;e--)o=Ce.from(i[e].create(null,o));o=Ce.from(s.copy(o));let a=e.tr.step(new Ht(t.pos-1,r,t.pos,r,new Ee(o,1,0),i.length,!0)),c=a.doc.resolve(r+2*i.length);c.nodeAfter&&c.nodeAfter.type==s.type&&rn(a.doc,c.pos)&&a.join(c.pos),n(a.scrollIntoView())}return!0}let h=l.type.spec.isolating||r>0&&a?null:bn.findFrom(t,1),u=h&&h.$from.blockRange(h.$to),d=u&&Zt(u);if(null!=d&&d>=t.depth)return n&&n(e.tr.lift(u,d).scrollIntoView()),!0;if(c&&vs(l,"start",!0)&&vs(s,"end")){let r=s,i=[];for(;i.push(r),!r.isTextblock;)r=r.lastChild;let o=l,a=1;for(;!o.isTextblock;o=o.firstChild)a++;if(r.canReplace(r.childCount,r.childCount,o.content)){if(n){let r=Ce.empty;for(let e=i.length-1;e>=0;e--)r=Ce.from(i[e].copy(r));n(e.tr.step(new Ht(t.pos-i.length,t.pos+l.nodeSize,t.pos+a,t.pos+l.nodeSize-a,new Ee(r,i.length,0),0,!0)).scrollIntoView())}return!0}}return!1}function Cs(e){return function(t,n){let r=t.selection,i=e<0?r.$from:r.$to,o=i.depth;for(;i.node(o).isInline;){if(!o)return!1;o--}return!!i.node(o).isTextblock&&(n&&n(t.tr.setSelection(Mn.create(t.doc,e<0?i.start(o):i.end(o)))),!0)}}const Ns=Cs(-1),Ds=Cs(1);function Ts(e,t=null){return function(n,r){let{$from:i,$to:o}=n.selection,s=i.blockRange(o),l=s&&Xt(s,e,t);return!!l&&(r&&r(n.tr.wrap(s,l).scrollIntoView()),!0)}}function As(e,t=null){return function(n,r){let i=!1;for(let o=0;o{if(i)return!1;if(r.isTextblock&&!r.hasMarkup(e,t))if(r.type==e)i=!0;else{let t=n.doc.resolve(o),r=t.index();i=t.parent.canReplaceWith(r,r+1,e)}}))}if(!i)return!1;if(r){let i=n.tr;for(let r=0;r{if(l||!r&&e.isAtom&&e.isInline&&t>=o.pos&&t+e.nodeSize<=s.pos)return!1;l=e.inlineContent&&e.type.allowsMarkType(n)})),l)return!0}return!1}(n.doc,c,e,i))return!1;if(s)if(a)e.isInSet(n.storedMarks||a.marks())?s(n.tr.removeStoredMark(e)):s(n.tr.addStoredMark(e.create(t)));else{let l,a=n.tr;i||(c=function(e){let t=[];for(let n=0;n{if(e.isAtom&&e.content.size&&e.isInline&&n>=r.pos&&n+e.nodeSize<=i.pos)return n+1>r.pos&&t.push(new xn(r,r.doc.resolve(n+1))),r=r.doc.resolve(n+1+e.content.size),!1})),r.posn.doc.rangeHasMark(t.$from.pos,t.$to.pos,e))):!c.every((t=>{let n=!1;return a.doc.nodesBetween(t.$from.pos,t.$to.pos,((r,i,o)=>{if(n)return!1;n=!e.isInSet(r.marks)&&!!o&&o.type.allowsMarkType(e)&&!(r.isText&&/^\s*$/.test(r.textBetween(Math.max(0,t.$from.pos-i),Math.min(r.nodeSize,t.$to.pos-i))))})),!n}));for(let n=0;n{let r=function(e,t){let{$cursor:n}=e.selection;return!n||(t?!t.endOfTextblock("backward",e):n.parentOffset>0)?null:n}(e,n);if(!r)return!1;let i=ws(r);if(!i){let n=r.blockRange(),i=n&&Zt(n);return null!=i&&(t&&t(e.tr.lift(n,i).scrollIntoView()),!0)}let o=i.nodeBefore;if(Os(e,i,t,-1))return!0;if(0==r.parent.content.size&&(vs(o,"end")||Cn.isSelectable(o)))for(let s=r.depth;;s--){let n=on(e.doc,r.before(s),r.after(s),Ee.empty);if(n&&n.slice.size1)break}return!(!o.isAtom||i.depth!=r.depth-1)&&(t&&t(e.tr.delete(i.pos-o.nodeSize,i.pos).scrollIntoView()),!0)}),((e,t,n)=>{let{$head:r,empty:i}=e.selection,o=r;if(!i)return!1;if(r.parent.isTextblock){if(n?!n.endOfTextblock("backward",e):r.parentOffset>0)return!1;o=ws(r)}let s=o&&o.nodeBefore;return!(!s||!Cn.isSelectable(s))&&(t&&t(e.tr.setSelection(Cn.create(e.doc,o.pos-s.nodeSize)).scrollIntoView()),!0)})),Rs=Es(ys,((e,t,n)=>{let r=function(e,t){let{$cursor:n}=e.selection;return!n||(t?!t.endOfTextblock("forward",e):n.parentOffset{let{$head:r,empty:i}=e.selection,o=r;if(!i)return!1;if(r.parent.isTextblock){if(n?!n.endOfTextblock("forward",e):r.parentOffset{let{$head:n,$anchor:r}=e.selection;return!(!n.parent.type.spec.code||!n.sameParent(r))&&(t&&t(e.tr.insertText("\n").scrollIntoView()),!0)}),((e,t)=>{let n=e.selection,{$from:r,$to:i}=n;if(n instanceof Dn||r.parent.inlineContent||i.parent.inlineContent)return!1;let o=Ss(i.parent.contentMatchAt(i.indexAfter()));if(!o||!o.isTextblock)return!1;if(t){let n=(!r.parentOffset&&i.index(){let{$cursor:n}=e.selection;if(!n||n.parent.content.size)return!1;if(n.depth>1&&n.after()!=n.end(-1)){let r=n.before();if(nn(e.doc,r))return t&&t(e.tr.split(r).scrollIntoView()),!0}let r=n.blockRange(),i=r&&Zt(r);return null!=i&&(t&&t(e.tr.lift(r,i).scrollIntoView()),!0)}),Ms),"Mod-Enter":ks,Backspace:Ps,"Mod-Backspace":Ps,"Shift-Backspace":Ps,Delete:Rs,"Mod-Delete":Rs,"Mod-a":(e,t)=>(t&&t(e.tr.setSelection(new Dn(e.doc))),!0)},zs={"Ctrl-h":Is.Backspace,"Alt-Backspace":Is["Mod-Backspace"],"Ctrl-d":Is.Delete,"Ctrl-Alt-Backspace":Is["Mod-Delete"],"Alt-Delete":Is["Mod-Delete"],"Alt-d":Is["Mod-Delete"],"Ctrl-a":Ns,"Ctrl-e":Ds};for(let os in Is)zs[os]=Is[os];const _s=("undefined"!=typeof navigator?/Mac|iP(hone|[oa]d)/.test(navigator.platform):!("undefined"==typeof os||!os.platform)&&"darwin"==os.platform())?zs:Is;class Bs{constructor(e,t,n={}){var r;this.match=e,this.match=e,this.handler="string"==typeof t?(r=t,function(e,t,n,i){let o=r;if(t[1]){let e=t[0].lastIndexOf(t[1]);o+=t[0].slice(e+t[1].length);let r=(n+=e)-i;r>0&&(o=t[0].slice(e-r,e)+o,n=i)}return e.tr.insertText(o,n,i)}):t,this.undoable=!1!==n.undoable,this.inCode=n.inCode||!1}}function Vs({rules:e}){let t=new Vn({state:{init:()=>null,apply(e,t){let n=e.getMeta(this);return n||(e.selectionSet||e.docChanged?null:t)}},props:{handleTextInput:(n,r,i,o)=>js(n,r,i,o,e,t),handleDOMEvents:{compositionend:n=>{setTimeout((()=>{let{$cursor:r}=n.state.selection;r&&js(n,r.pos,r.pos,"",e,t)}))}}},isInputRules:!0});return t}function js(e,t,n,r,i,o){if(e.composing)return!1;let s=e.state,l=s.doc.resolve(t),a=l.parent.textBetween(Math.max(0,l.parentOffset-500),l.parentOffset,null,"")+r;for(let c=0;c{let n=e.plugins;for(let r=0;r=0;e--)n.step(r.steps[e].invert(r.docs[e]));if(i.text){let t=n.doc.resolve(i.from).marks();n.replaceWith(i.from,i.to,e.schema.text(i.text,t))}else n.delete(i.from,i.to);t(n)}return!0}}return!1};function Ls(e,t,n=null,r){return new Bs(e,((e,i,o,s)=>{let l=n instanceof Function?n(i):n,a=e.tr.delete(o,s),c=a.doc.resolve(o).blockRange(),h=c&&Xt(c,t,l);if(!h)return null;a.wrap(c,h);let u=a.doc.resolve(o-1).nodeBefore;return u&&u.type==t&&rn(a.doc,o-1)&&(!r||r(i,u))&&a.join(o-1),a}))}function Ws(e,t,n=null){return new Bs(e,((e,r,i,o)=>{let s=e.doc.resolve(i),l=n instanceof Function?n(r):n;return s.node(-1).canReplaceWith(s.index(-1),s.indexAfter(-1),t)?e.tr.delete(i,o).setBlockType(i,i,t,l):null}))}new Bs(/--$/,"—"),new Bs(/\.\.\.$/,"…"),new Bs(/(?:^|[\s\{\[\(\<'"\u2018\u201C])(")$/,"“"),new Bs(/"$/,"”"),new Bs(/(?:^|[\s\{\[\(\<'"\u2018\u201C])(')$/,"‘"),new Bs(/'$/,"’");const qs=["ol",0],Js=["ul",0],Ks=["li",0],Hs={attrs:{order:{default:1,validate:"number"}},parseDOM:[{tag:"ol",getAttrs:e=>({order:e.hasAttribute("start")?+e.getAttribute("start"):1})}],toDOM:e=>1==e.attrs.order?qs:["ol",{start:e.attrs.order},0]},Ys={parseDOM:[{tag:"ul"}],toDOM:()=>Js},Us={parseDOM:[{tag:"li"}],toDOM:()=>Ks,defining:!0};function Gs(e,t){let n={};for(let r in e)n[r]=e[r];for(let r in t)n[r]=t[r];return n}function Zs(e,t,n){return e.append({ordered_list:Gs(Hs,{content:"list_item+",group:n}),bullet_list:Gs(Ys,{content:"list_item+",group:n}),list_item:Gs(Us,{content:t})})}function Xs(e,t=null){return function(n,r){let{$from:i,$to:o}=n.selection,s=i.blockRange(o);if(!s)return!1;let l=r?n.tr:null;return!!function(e,t,n,r=null){let i=!1,o=t,s=t.$from.doc;if(t.depth>=2&&t.$from.node(t.depth-1).type.compatibleContent(n)&&0==t.startIndex){if(0==t.$from.index(t.depth-1))return!1;let e=s.resolve(t.start-2);o=new Ye(e,e,t.depth),t.endIndex=0;h--)o=Ce.from(n[h].type.create(n[h].attrs,o));e.step(new Ht(t.start-(r?2:0),t.end,t.start,t.end,new Ee(o,0,0),n.length,!0));let s=0;for(let h=0;h=i.depth-3;e--)t=Ce.from(i.node(e).copy(t));let s=i.indexAfter(-1){if(c>-1)return!1;e.isTextblock&&0==e.content.size&&(c=t+1)})),c>-1&&a.setSelection(bn.near(a.doc.resolve(c))),r(a.scrollIntoView())}return!0}let a=o.pos==i.end()?l.contentMatchAt(0).defaultType:null,c=n.tr.delete(i.pos,o.pos),h=a?[t?{type:e,attrs:t}:null,{type:a}]:void 0;return!!nn(c.doc,i.pos,2,h)&&(r&&r(c.split(i.pos,2,h).scrollIntoView()),!0)}}function el(e){return function(t,n){let{$from:r,$to:i}=t.selection,o=r.blockRange(i,(t=>t.childCount>0&&t.firstChild.type==e));return!!o&&(!n||(r.node(o.depth-1).type==e?function(e,t,n,r){let i=e.tr,o=r.end,s=r.$to.end(r.depth);om;p--)f-=i.child(p).nodeSize,r.delete(f-1,f+1);let o=r.doc.resolve(n.start),s=o.nodeAfter;if(r.mapping.map(n.end)!=n.start+o.nodeAfter.nodeSize)return!1;let l=0==n.startIndex,a=n.endIndex==i.childCount,c=o.node(-1),h=o.index(-1);if(!c.canReplace(h+(l?0:1),h+1,s.content.append(a?Ce.empty:Ce.from(i))))return!1;let u=o.pos,d=u+s.nodeSize;return r.step(new Ht(u-(l?1:0),d+(a?1:0),u+1,d-1,new Ee((l?Ce.empty:Ce.from(i.copy(Ce.empty))).append(a?Ce.empty:Ce.from(i.copy(Ce.empty))),l?0:1,a?0:1),l?0:1)),t(r.scrollIntoView()),!0}(t,n,o)))}}function tl(e){return function(t,n){let{$from:r,$to:i}=t.selection,o=r.blockRange(i,(t=>t.childCount>0&&t.firstChild.type==e));if(!o)return!1;let s=o.startIndex;if(0==s)return!1;let l=o.parent,a=l.child(s-1);if(a.type!=e)return!1;if(n){let r=a.lastChild&&a.lastChild.type==l.type,i=Ce.from(r?e.create():null),s=new Ee(Ce.from(e.create(null,Ce.from(l.type.create(null,i)))),r?3:1,0),c=o.start,h=o.end;n(t.tr.step(new Ht(c-(r?3:1),h,c,h,s,1,!0)).scrollIntoView())}return!0}}var nl=200,rl=function(){};rl.prototype.append=function(e){return e.length?(e=rl.from(e),!this.length&&e||e.length=t?rl.empty:this.sliceInner(Math.max(0,e),Math.min(this.length,t))},rl.prototype.get=function(e){if(!(e<0||e>=this.length))return this.getInner(e)},rl.prototype.forEach=function(e,t,n){void 0===t&&(t=0),void 0===n&&(n=this.length),t<=n?this.forEachInner(e,t,n,0):this.forEachInvertedInner(e,t,n,0)},rl.prototype.map=function(e,t,n){void 0===t&&(t=0),void 0===n&&(n=this.length);var r=[];return this.forEach((function(t,n){return r.push(e(t,n))}),t,n),r},rl.from=function(e){return e instanceof rl?e:e&&e.length?new il(e):rl.empty};var il=function(e){function t(t){e.call(this),this.values=t}e&&(t.__proto__=e),t.prototype=Object.create(e&&e.prototype),t.prototype.constructor=t;var n={length:{configurable:!0},depth:{configurable:!0}};return t.prototype.flatten=function(){return this.values},t.prototype.sliceInner=function(e,n){return 0==e&&n==this.length?this:new t(this.values.slice(e,n))},t.prototype.getInner=function(e){return this.values[e]},t.prototype.forEachInner=function(e,t,n,r){for(var i=t;i=n;i--)if(!1===e(this.values[i],r+i))return!1},t.prototype.leafAppend=function(e){if(this.length+e.length<=nl)return new t(this.values.concat(e.flatten()))},t.prototype.leafPrepend=function(e){if(this.length+e.length<=nl)return new t(e.flatten().concat(this.values))},n.length.get=function(){return this.values.length},n.depth.get=function(){return 0},Object.defineProperties(t.prototype,n),t}(rl);rl.empty=new il([]);var ol=function(e){function t(t,n){e.call(this),this.left=t,this.right=n,this.length=t.length+n.length,this.depth=Math.max(t.depth,n.depth)+1}return e&&(t.__proto__=e),t.prototype=Object.create(e&&e.prototype),t.prototype.constructor=t,t.prototype.flatten=function(){return this.left.flatten().concat(this.right.flatten())},t.prototype.getInner=function(e){return ei&&!1===this.right.forEachInner(e,Math.max(t-i,0),Math.min(this.length,n)-i,r+i))&&void 0)},t.prototype.forEachInvertedInner=function(e,t,n,r){var i=this.left.length;return!(t>i&&!1===this.right.forEachInvertedInner(e,t-i,Math.max(n,i)-i,r+i))&&(!(n=n?this.right.slice(e-n,t-n):this.left.slice(e,n).append(this.right.slice(0,t-n))},t.prototype.leafAppend=function(e){var n=this.right.leafAppend(e);if(n)return new t(this.left,n)},t.prototype.leafPrepend=function(e){var n=this.left.leafPrepend(e);if(n)return new t(n,this.right)},t.prototype.appendInner=function(e){return this.left.depth>=Math.max(this.right.depth,e.depth)+1?new t(this.left,new t(this.right,e)):new t(this,e)},t}(rl);class sl{constructor(e,t){this.items=e,this.eventCount=t}popEvent(e,t){if(0==this.eventCount)return null;let n,r,i=this.items.length;for(;;i--){if(this.items.get(i-1).selection){--i;break}}t&&(n=this.remapping(i,this.items.length),r=n.maps.length);let o,s,l=e.tr,a=[],c=[];return this.items.forEach(((e,t)=>{if(!e.step)return n||(n=this.remapping(i,t+1),r=n.maps.length),r--,void c.push(e);if(n){c.push(new ll(e.map));let t,i=e.step.map(n.slice(r));i&&l.maybeStep(i).doc&&(t=l.mapping.maps[l.mapping.maps.length-1],a.push(new ll(t,void 0,void 0,a.length+c.length))),r--,t&&n.appendMap(t,r)}else l.maybeStep(e.step);return e.selection?(o=n?e.selection.map(n.slice(r)):e.selection,s=new sl(this.items.slice(0,i).append(c.reverse().concat(a)),this.eventCount-1),!1):void 0}),this.items.length,0),{remaining:s,transform:l,selection:o}}addTransform(e,t,n,r){let i=[],o=this.eventCount,s=this.items,l=!r&&s.length?s.get(s.length-1):null;for(let c=0;ccl&&(s=function(e,t){let n;return e.forEach(((e,r)=>{if(e.selection&&0==t--)return n=r,!1})),e.slice(n)}(s,a),o-=a),new sl(s.append(i),o)}remapping(e,t){let n=new _t;return this.items.forEach(((t,r)=>{let i=null!=t.mirrorOffset&&r-t.mirrorOffset>=e?n.maps.length-t.mirrorOffset:void 0;n.appendMap(t.map,i)}),e,t),n}addMaps(e){return 0==this.eventCount?this:new sl(this.items.append(e.map((e=>new ll(e)))),this.eventCount)}rebased(e,t){if(!this.eventCount)return this;let n=[],r=Math.max(0,this.items.length-t),i=e.mapping,o=e.steps.length,s=this.eventCount;this.items.forEach((e=>{e.selection&&s--}),r);let l=t;this.items.forEach((t=>{let r=i.getMirror(--l);if(null==r)return;o=Math.min(o,r);let a=i.maps[r];if(t.step){let o=e.steps[r].invert(e.docs[r]),c=t.selection&&t.selection.map(i.slice(l+1,r));c&&s++,n.push(new ll(a,o,c))}else n.push(new ll(a))}),r);let a=[];for(let u=t;u500&&(h=h.compress(this.items.length-n.length)),h}emptyItemCount(){let e=0;return this.items.forEach((t=>{t.step||e++})),e}compress(e=this.items.length){let t=this.remapping(0,e),n=t.maps.length,r=[],i=0;return this.items.forEach(((o,s)=>{if(s>=e)r.push(o),o.selection&&i++;else if(o.step){let e=o.step.map(t.slice(n)),s=e&&e.getMap();if(n--,s&&t.appendMap(s,n),e){let l=o.selection&&o.selection.map(t.slice(n));l&&i++;let a,c=new ll(s.invert(),e,l),h=r.length-1;(a=r.length&&r[h].merge(c))?r[h]=a:r.push(c)}}else o.map&&n--}),this.items.length,0),new sl(rl.from(r.reverse()),i)}}sl.empty=new sl(rl.empty,0);class ll{constructor(e,t,n,r){this.map=e,this.step=t,this.selection=n,this.mirrorOffset=r}merge(e){if(this.step&&e.step&&!e.selection){let t=e.step.merge(this.step);if(t)return new ll(t.getMap().invert(),t,this.selection)}}}class al{constructor(e,t,n,r,i){this.done=e,this.undone=t,this.prevRanges=n,this.prevTime=r,this.prevComposition=i}}const cl=20;function hl(e){let t=[];for(let n=e.length-1;n>=0&&0==t.length;n--)e[n].forEach(((e,n,r,i)=>t.push(r,i)));return t}function ul(e,t){if(!e)return null;let n=[];for(let r=0;rnew al(sl.empty,sl.empty,null,0,-1),apply:(t,n,r)=>function(e,t,n,r){let i,o=n.getMeta(ml);if(o)return o.historyState;n.getMeta(gl)&&(e=new al(e.done,e.undone,null,0,-1));let s=n.getMeta("appendedTransaction");if(0==n.steps.length)return e;if(s&&s.getMeta(ml))return s.getMeta(ml).redo?new al(e.done.addTransform(n,void 0,r,pl(t)),e.undone,hl(n.mapping.maps),e.prevTime,e.prevComposition):new al(e.done,e.undone.addTransform(n,void 0,r,pl(t)),null,e.prevTime,e.prevComposition);if(!1===n.getMeta("addToHistory")||s&&!1===s.getMeta("addToHistory"))return(i=n.getMeta("rebased"))?new al(e.done.rebased(n,i),e.undone.rebased(n,i),ul(e.prevRanges,n.mapping),e.prevTime,e.prevComposition):new al(e.done.addMaps(n.mapping.maps),e.undone.addMaps(n.mapping.maps),ul(e.prevRanges,n.mapping),e.prevTime,e.prevComposition);{let i=n.getMeta("composition"),o=0==e.prevTime||!s&&e.prevComposition!=i&&(e.prevTime<(n.time||0)-r.newGroupDelay||!function(e,t){if(!t)return!1;if(!e.docChanged)return!0;let n=!1;return e.mapping.maps[0].forEach(((e,r)=>{for(let i=0;i=t[i]&&(n=!0)})),n}(n,e.prevRanges)),l=s?ul(e.prevRanges,n.mapping):hl(n.mapping.maps);return new al(e.done.addTransform(n,o?t.selection.getBookmark():void 0,r,pl(t)),sl.empty,l,n.time,null==i?e.prevComposition:i)}}(n,r,t,e)},config:e,props:{handleDOMEvents:{beforeinput(e,t){let n=t.inputType,r="historyUndo"==n?wl:"historyRedo"==n?bl:null;return!!r&&(t.preventDefault(),r(e.state,e.dispatch))}}}})}function vl(e,t){return(n,r)=>{let i=ml.getState(n);if(!i||0==(e?i.undone:i.done).eventCount)return!1;if(r){let o=function(e,t,n){let r=pl(t),i=ml.get(t).spec.config,o=(n?e.undone:e.done).popEvent(t,r);if(!o)return null;let s=o.selection.resolve(o.transform.doc),l=(n?e.done:e.undone).addTransform(o.transform,t.selection.getBookmark(),i,r),a=new al(n?l:o.remaining,n?o.remaining:l,null,0,-1);return o.transform.setSelection(s).setMeta(ml,{redo:n,historyState:a})}(i,n,e);o&&r(t?o.scrollIntoView():o)}return!0}}const wl=vl(!1,!0),bl=vl(!0,!0);function xl(e){let t=ml.getState(e);return t?t.done.eventCount:0}function Sl(e){let t=ml.getState(e);return t?t.undone.eventCount:0} +/*! + * portal-vue © Thorsten Lünborg, 2019 + * + * Version: 2.1.7 + * + * LICENCE: MIT + * + * https://github.com/linusborg/portal-vue + * + */function kl(e){return(kl="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function Ml(e){return function(e){if(Array.isArray(e)){for(var t=0,n=new Array(e.length);t1&&void 0!==arguments[1]&&arguments[1],n=e.to,r=e.from;if(n&&(r||!1!==t)&&this.transports[n])if(t)this.transports[n]=[];else{var i=this.$_getTransportIndex(e);if(i>=0){var o=this.transports[n].slice(0);o.splice(i,1),this.transports[n]=o}}},registerTarget:function(e,t,n){Ol&&(this.trackInstances&&!n&&this.targets[e]&&console.warn("[portal-vue]: Target ".concat(e," already exists")),this.$set(this.targets,e,Object.freeze([t])))},unregisterTarget:function(e){this.$delete(this.targets,e)},registerSource:function(e,t,n){Ol&&(this.trackInstances&&!n&&this.sources[e]&&console.warn("[portal-vue]: source ".concat(e," already exists")),this.$set(this.sources,e,Object.freeze([t])))},unregisterSource:function(e){this.$delete(this.sources,e)},hasTarget:function(e){return!(!this.targets[e]||!this.targets[e][0])},hasSource:function(e){return!(!this.sources[e]||!this.sources[e][0])},hasContentFor:function(e){return!!this.transports[e]&&!!this.transports[e].length},$_getTransportIndex:function(e){var t=e.to,n=e.from;for(var r in this.transports[t])if(this.transports[t][r].from===n)return+r;return-1}}}),$l=new Al(Nl),El=1,Pl=Vue.extend({name:"portal",props:{disabled:{type:Boolean},name:{type:String,default:function(){return String(El++)}},order:{type:Number,default:0},slim:{type:Boolean},slotProps:{type:Object,default:function(){return{}}},tag:{type:String,default:"DIV"},to:{type:String,default:function(){return String(Math.round(1e7*Math.random()))}}},created:function(){var e=this;this.$nextTick((function(){$l.registerSource(e.name,e)}))},mounted:function(){this.disabled||this.sendUpdate()},updated:function(){this.disabled?this.clear():this.sendUpdate()},beforeDestroy:function(){$l.unregisterSource(this.name),this.clear()},watch:{to:function(e,t){t&&t!==e&&this.clear(t),this.sendUpdate()}},methods:{clear:function(e){var t={from:this.name,to:e||this.to};$l.close(t)},normalizeSlots:function(){return this.$scopedSlots.default?[this.$scopedSlots.default]:this.$slots.default},normalizeOwnChildren:function(e){return"function"==typeof e?e(this.slotProps):e},sendUpdate:function(){var e=this.normalizeSlots();if(e){var t={from:this.name,to:this.to,passengers:Ml(e),order:this.order};$l.open(t)}else this.clear()}},render:function(e){var t=this.$slots.default||this.$scopedSlots.default||[],n=this.tag;return t&&this.disabled?t.length<=1&&this.slim?this.normalizeOwnChildren(t)[0]:e(n,[this.normalizeOwnChildren(t)]):this.slim?e():e(n,{class:{"v-portal":!0},style:{display:"none"},key:"v-portal-placeholder"})}}),Rl=Vue.extend({name:"portalTarget",props:{multiple:{type:Boolean,default:!1},name:{type:String,required:!0},slim:{type:Boolean,default:!1},slotProps:{type:Object,default:function(){return{}}},tag:{type:String,default:"div"},transition:{type:[String,Object,Function]}},data:function(){return{transports:$l.transports,firstRender:!0}},created:function(){var e=this;this.$nextTick((function(){$l.registerTarget(e.name,e)}))},watch:{ownTransports:function(){this.$emit("change",this.children().length>0)},name:function(e,t){$l.unregisterTarget(t),$l.registerTarget(e,this)}},mounted:function(){var e=this;this.transition&&this.$nextTick((function(){e.firstRender=!1}))},beforeDestroy:function(){$l.unregisterTarget(this.name)},computed:{ownTransports:function(){var e=this.transports[this.name]||[];return this.multiple?e:0===e.length?[]:[e[e.length-1]]},passengers:function(){return function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return e.reduce((function(e,n){var r=n.passengers[0],i="function"==typeof r?r(t):n.passengers;return e.concat(i)}),[])}(this.ownTransports,this.slotProps)}},methods:{children:function(){return 0!==this.passengers.length?this.passengers:this.$scopedSlots.default?this.$scopedSlots.default(this.slotProps):this.$slots.default||[]},noWrapper:function(){var e=this.slim&&!this.transition;return e&&this.children().length>1&&console.warn("[portal-vue]: PortalTarget with `slim` option received more than one child element."),e}},render:function(e){var t=this.noWrapper(),n=this.children(),r=this.transition||this.tag;return t?n[0]:this.slim&&!r?e():e(r,{props:{tag:this.transition&&this.tag?this.tag:void 0},class:{"vue-portal-target":!0}},n)}}),Il=0,zl=["disabled","name","order","slim","slotProps","tag","to"],_l=["multiple","transition"],Bl=Vue.extend({name:"MountingPortal",inheritAttrs:!1,props:{append:{type:[Boolean,String]},bail:{type:Boolean},mountTo:{type:String,required:!0},disabled:{type:Boolean},name:{type:String,default:function(){return"mounted_"+String(Il++)}},order:{type:Number,default:0},slim:{type:Boolean},slotProps:{type:Object,default:function(){return{}}},tag:{type:String,default:"DIV"},to:{type:String,default:function(){return String(Math.round(1e7*Math.random()))}},multiple:{type:Boolean,default:!1},targetSlim:{type:Boolean},targetSlotProps:{type:Object,default:function(){return{}}},targetTag:{type:String,default:"div"},transition:{type:[String,Object,Function]}},created:function(){if("undefined"!=typeof document){var e=document.querySelector(this.mountTo);if(e){var t=this.$props;if($l.targets[t.name])t.bail?console.warn("[portal-vue]: Target ".concat(t.name," is already mounted.\n Aborting because 'bail: true' is set")):this.portalTarget=$l.targets[t.name];else{var n=t.append;if(n){var r="string"==typeof n?n:"DIV",i=document.createElement(r);e.appendChild(i),e=i}var o=Cl(this.$props,_l);o.slim=this.targetSlim,o.tag=this.targetTag,o.slotProps=this.targetSlotProps,o.name=this.to,this.portalTarget=new Rl({el:e,parent:this.$parent||this,propsData:o})}}else console.error("[portal-vue]: Mount Point '".concat(this.mountTo,"' not found in document"))}},beforeDestroy:function(){var e=this.portalTarget;if(this.append){var t=e.$el;t.parentNode.removeChild(t)}e.$destroy()},render:function(e){if(!this.portalTarget)return console.warn("[portal-vue] Target wasn't mounted"),e();if(!this.$scopedSlots.manual){var t=Cl(this.$props,zl);return e(Pl,{props:t,attrs:this.$attrs,on:this.$listeners,scopedSlots:this.$scopedSlots},this.$slots.default)}var n=this.$scopedSlots.manual({to:this.to});return Array.isArray(n)&&(n=n[0]),n||e()}});var Vl={install:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};e.component(t.portalName||"Portal",Pl),e.component(t.portalTargetName||"PortalTarget",Rl),e.component(t.MountingPortalName||"MountingPortal",Bl)}},jl=new Map;function Fl(e){var t=jl.get(e);t&&t.destroy()}function Ll(e){var t=jl.get(e);t&&t.update()}var Wl=null;"undefined"==typeof window?((Wl=function(e){return e}).destroy=function(e){return e},Wl.update=function(e){return e}):((Wl=function(e,t){return e&&Array.prototype.forEach.call(e.length?e:[e],(function(e){return function(e){if(e&&e.nodeName&&"TEXTAREA"===e.nodeName&&!jl.has(e)){var t,n=null,r=window.getComputedStyle(e),i=(t=e.value,function(){s({testForHeightReduction:""===t||!e.value.startsWith(t),restoreTextAlign:null}),t=e.value}),o=function(t){e.removeEventListener("autosize:destroy",o),e.removeEventListener("autosize:update",l),e.removeEventListener("input",i),window.removeEventListener("resize",l),Object.keys(t).forEach((function(n){return e.style[n]=t[n]})),jl.delete(e)}.bind(e,{height:e.style.height,resize:e.style.resize,textAlign:e.style.textAlign,overflowY:e.style.overflowY,overflowX:e.style.overflowX,wordWrap:e.style.wordWrap});e.addEventListener("autosize:destroy",o),e.addEventListener("autosize:update",l),e.addEventListener("input",i),window.addEventListener("resize",l),e.style.overflowX="hidden",e.style.wordWrap="break-word",jl.set(e,{destroy:o,update:l}),l()}function s(t){var i,o,l=t.restoreTextAlign,a=void 0===l?null:l,c=t.testForHeightReduction,h=void 0===c||c,u=r.overflowY;if(0!==e.scrollHeight&&("vertical"===r.resize?e.style.resize="none":"both"===r.resize&&(e.style.resize="horizontal"),h&&(i=function(e){for(var t=[];e&&e.parentNode&&e.parentNode instanceof Element;)e.parentNode.scrollTop&&t.push([e.parentNode,e.parentNode.scrollTop]),e=e.parentNode;return function(){return t.forEach((function(e){var t=e[0],n=e[1];t.style.scrollBehavior="auto",t.scrollTop=n,t.style.scrollBehavior=null}))}}(e),e.style.height=""),o="content-box"===r.boxSizing?e.scrollHeight-(parseFloat(r.paddingTop)+parseFloat(r.paddingBottom)):e.scrollHeight+parseFloat(r.borderTopWidth)+parseFloat(r.borderBottomWidth),"none"!==r.maxHeight&&o>parseFloat(r.maxHeight)?("hidden"===r.overflowY&&(e.style.overflow="scroll"),o=parseFloat(r.maxHeight)):"hidden"!==r.overflowY&&(e.style.overflow="hidden"),e.style.height=o+"px",a&&(e.style.textAlign=a),i&&i(),n!==o&&(e.dispatchEvent(new Event("autosize:resized",{bubbles:!0})),n=o),u!==r.overflow&&!a)){var d=r.textAlign;"hidden"===r.overflow&&(e.style.textAlign="start"===d?"end":"start"),s({restoreTextAlign:d,testForHeightReduction:!0})}}function l(){s({testForHeightReduction:!0,restoreTextAlign:null})}}(e)})),e}).destroy=function(e){return e&&Array.prototype.forEach.call(e.length?e:[e],Fl),e},Wl.update=function(e){return e&&Array.prototype.forEach.call(e.length?e:[e],Ll),e});var ql,Jl=Wl,Kl={exports:{}};var Hl=(ql||(ql=1,Kl.exports=function(){var e=1e3,t=6e4,n=36e5,r="millisecond",i="second",o="minute",s="hour",l="day",a="week",c="month",h="quarter",u="year",d="date",f="Invalid Date",p=/^(\d{4})[-/]?(\d{1,2})?[-/]?(\d{0,2})[Tt\s]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?[.:]?(\d+)?$/,m=/\[([^\]]+)]|Y{1,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g,g={name:"en",weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),ordinal:function(e){var t=["th","st","nd","rd"],n=e%100;return"["+e+(t[(n-20)%10]||t[n]||t[0])+"]"}},y=function(e,t,n){var r=String(e);return!r||r.length>=t?e:""+Array(t+1-r.length).join(n)+e},v={s:y,z:function(e){var t=-e.utcOffset(),n=Math.abs(t),r=Math.floor(n/60),i=n%60;return(t<=0?"+":"-")+y(r,2,"0")+":"+y(i,2,"0")},m:function e(t,n){if(t.date()1)return e(s[0])}else{var l=t.name;b[l]=t,i=l}return!r&&i&&(w=i),i||!r&&w},M=function(e,t){if(S(e))return e.clone();var n="object"==typeof t?t:{};return n.date=e,n.args=arguments,new C(n)},O=v;O.l=k,O.i=S,O.w=function(e,t){return M(e,{locale:t.$L,utc:t.$u,x:t.$x,$offset:t.$offset})};var C=function(){function g(e){this.$L=k(e.locale,null,!0),this.parse(e),this.$x=this.$x||e.x||{},this[x]=!0}var y=g.prototype;return y.parse=function(e){this.$d=function(e){var t=e.date,n=e.utc;if(null===t)return new Date(NaN);if(O.u(t))return new Date;if(t instanceof Date)return new Date(t);if("string"==typeof t&&!/Z$/i.test(t)){var r=t.match(p);if(r){var i=r[2]-1||0,o=(r[7]||"0").substring(0,3);return n?new Date(Date.UTC(r[1],i,r[3]||1,r[4]||0,r[5]||0,r[6]||0,o)):new Date(r[1],i,r[3]||1,r[4]||0,r[5]||0,r[6]||0,o)}}return new Date(t)}(e),this.init()},y.init=function(){var e=this.$d;this.$y=e.getFullYear(),this.$M=e.getMonth(),this.$D=e.getDate(),this.$W=e.getDay(),this.$H=e.getHours(),this.$m=e.getMinutes(),this.$s=e.getSeconds(),this.$ms=e.getMilliseconds()},y.$utils=function(){return O},y.isValid=function(){return!(this.$d.toString()===f)},y.isSame=function(e,t){var n=M(e);return this.startOf(t)<=n&&n<=this.endOf(t)},y.isAfter=function(e,t){return M(e)68?1900:2e3)},a=function(e){return function(t){this[e]=+t}},c=[/[+-]\d\d:?(\d\d)?|Z/,function(e){(this.zone||(this.zone={})).offset=function(e){if(!e)return 0;if("Z"===e)return 0;var t=e.match(/([+-]|\d\d)/g),n=60*t[1]+(+t[2]||0);return 0===n?0:"+"===t[0]?-n:n}(e)}],h=function(e){var t=s[e];return t&&(t.indexOf?t:t.s.concat(t.f))},u=function(e,t){var n,r=s.meridiem;if(r){for(var i=1;i<=24;i+=1)if(e.indexOf(r(i,0,t))>-1){n=i>12;break}}else n=e===(t?"pm":"PM");return n},d={A:[o,function(e){this.afternoon=u(e,!1)}],a:[o,function(e){this.afternoon=u(e,!0)}],Q:[n,function(e){this.month=3*(e-1)+1}],S:[n,function(e){this.milliseconds=100*+e}],SS:[r,function(e){this.milliseconds=10*+e}],SSS:[/\d{3}/,function(e){this.milliseconds=+e}],s:[i,a("seconds")],ss:[i,a("seconds")],m:[i,a("minutes")],mm:[i,a("minutes")],H:[i,a("hours")],h:[i,a("hours")],HH:[i,a("hours")],hh:[i,a("hours")],D:[i,a("day")],DD:[r,a("day")],Do:[o,function(e){var t=s.ordinal,n=e.match(/\d+/);if(this.day=n[0],t)for(var r=1;r<=31;r+=1)t(r).replace(/\[|\]/g,"")===e&&(this.day=r)}],w:[i,a("week")],ww:[r,a("week")],M:[i,a("month")],MM:[r,a("month")],MMM:[o,function(e){var t=h("months"),n=(h("monthsShort")||t.map((function(e){return e.slice(0,3)}))).indexOf(e)+1;if(n<1)throw new Error;this.month=n%12||n}],MMMM:[o,function(e){var t=h("months").indexOf(e)+1;if(t<1)throw new Error;this.month=t%12||t}],Y:[/[+-]?\d+/,a("year")],YY:[r,function(e){this.year=l(e)}],YYYY:[/\d{4}/,a("year")],Z:c,ZZ:c};function f(n){var r,i;r=n,i=s&&s.formats;for(var o=(n=r.replace(/(\[[^\]]+])|(LTS?|l{1,4}|L{1,4})/g,(function(t,n,r){var o=r&&r.toUpperCase();return n||i[r]||e[r]||i[o].replace(/(\[[^\]]+])|(MMMM|MM|DD|dddd)/g,(function(e,t,n){return t||n.slice(1)}))}))).match(t),l=o.length,a=0;a-1)return new Date(("X"===t?1e3:1)*e);var i=f(t)(e),o=i.year,s=i.month,l=i.day,a=i.hours,c=i.minutes,h=i.seconds,u=i.milliseconds,d=i.zone,p=i.week,m=new Date,g=l||(o||s?1:m.getDate()),y=o||m.getFullYear(),v=0;o&&!s||(v=s>0?s-1:m.getMonth());var w,b=a||0,x=c||0,S=h||0,k=u||0;return d?new Date(Date.UTC(y,v,g,b,x,S,k+60*d.offset*1e3)):n?new Date(Date.UTC(y,v,g,b,x,S,k)):(w=new Date(y,v,g,b,x,S,k),p&&(w=r(w).week(p).toDate()),w)}catch(M){return new Date("")}}(t,l,r,n),this.init(),u&&!0!==u&&(this.$L=this.locale(u).$L),h&&t!=this.format(l)&&(this.$d=new Date("")),s={}}else if(l instanceof Array)for(var d=l.length,p=1;p<=d;p+=1){o[1]=l[p-1];var m=n.apply(this,o);if(m.isValid()){this.$d=m.$d,this.$L=m.$L,this.init();break}p===d&&(this.$d=new Date(""))}else i.call(this,e)}}}()),Gl.exports);const Xl=e(Zl);function Ql(e){return{all:e=e||new Map,on:function(t,n){var r=e.get(t);r?r.push(n):e.set(t,[n])},off:function(t,n){var r=e.get(t);r&&(n?r.splice(r.indexOf(n)>>>0,1):e.set(t,[]))},emit:function(t,n){var r=e.get(t);r&&r.slice().map((function(e){e(n)})),(r=e.get("*"))&&r.slice().map((function(e){e(t,n)}))}}} +/*! + * vuex v3.6.2 + * (c) 2021 Evan You + * @license MIT + */var ea=("undefined"!=typeof window?window:"undefined"!=typeof global?global:{}).__VUE_DEVTOOLS_GLOBAL_HOOK__;function ta(e,t){if(void 0===t&&(t=[]),null===e||"object"!=typeof e)return e;var n,r=(n=function(t){return t.original===e},t.filter(n)[0]);if(r)return r.copy;var i=Array.isArray(e)?[]:{};return t.push({original:e,copy:i}),Object.keys(e).forEach((function(n){i[n]=ta(e[n],t)})),i}function na(e,t){Object.keys(e).forEach((function(n){return t(e[n],n)}))}function ra(e){return null!==e&&"object"==typeof e}var ia=function(e,t){this.runtime=t,this._children=Object.create(null),this._rawModule=e;var n=e.state;this.state=("function"==typeof n?n():n)||{}},oa={namespaced:{configurable:!0}};oa.namespaced.get=function(){return!!this._rawModule.namespaced},ia.prototype.addChild=function(e,t){this._children[e]=t},ia.prototype.removeChild=function(e){delete this._children[e]},ia.prototype.getChild=function(e){return this._children[e]},ia.prototype.hasChild=function(e){return e in this._children},ia.prototype.update=function(e){this._rawModule.namespaced=e.namespaced,e.actions&&(this._rawModule.actions=e.actions),e.mutations&&(this._rawModule.mutations=e.mutations),e.getters&&(this._rawModule.getters=e.getters)},ia.prototype.forEachChild=function(e){na(this._children,e)},ia.prototype.forEachGetter=function(e){this._rawModule.getters&&na(this._rawModule.getters,e)},ia.prototype.forEachAction=function(e){this._rawModule.actions&&na(this._rawModule.actions,e)},ia.prototype.forEachMutation=function(e){this._rawModule.mutations&&na(this._rawModule.mutations,e)},Object.defineProperties(ia.prototype,oa);var sa,la=function(e){this.register([],e,!1)};function aa(e,t,n){if(t.update(n),n.modules)for(var r in n.modules){if(!t.getChild(r))return;aa(e.concat(r),t.getChild(r),n.modules[r])}}la.prototype.get=function(e){return e.reduce((function(e,t){return e.getChild(t)}),this.root)},la.prototype.getNamespace=function(e){var t=this.root;return e.reduce((function(e,n){return e+((t=t.getChild(n)).namespaced?n+"/":"")}),"")},la.prototype.update=function(e){aa([],this.root,e)},la.prototype.register=function(e,t,n){var r=this;void 0===n&&(n=!0);var i=new ia(t,n);0===e.length?this.root=i:this.get(e.slice(0,-1)).addChild(e[e.length-1],i);t.modules&&na(t.modules,(function(t,i){r.register(e.concat(i),t,n)}))},la.prototype.unregister=function(e){var t=this.get(e.slice(0,-1)),n=e[e.length-1],r=t.getChild(n);r&&r.runtime&&t.removeChild(n)},la.prototype.isRegistered=function(e){var t=this.get(e.slice(0,-1)),n=e[e.length-1];return!!t&&t.hasChild(n)};var ca=function(e){var t=this;void 0===e&&(e={}),!sa&&"undefined"!=typeof window&&window.Vue&&ya(window.Vue);var n=e.plugins;void 0===n&&(n=[]);var r=e.strict;void 0===r&&(r=!1),this._committing=!1,this._actions=Object.create(null),this._actionSubscribers=[],this._mutations=Object.create(null),this._wrappedGetters=Object.create(null),this._modules=new la(e),this._modulesNamespaceMap=Object.create(null),this._subscribers=[],this._watcherVM=new sa,this._makeLocalGettersCache=Object.create(null);var i=this,o=this.dispatch,s=this.commit;this.dispatch=function(e,t){return o.call(i,e,t)},this.commit=function(e,t,n){return s.call(i,e,t,n)},this.strict=r;var l=this._modules.root.state;pa(this,l,[],this._modules.root),fa(this,l),n.forEach((function(e){return e(t)})),(void 0!==e.devtools?e.devtools:sa.config.devtools)&&function(e){ea&&(e._devtoolHook=ea,ea.emit("vuex:init",e),ea.on("vuex:travel-to-state",(function(t){e.replaceState(t)})),e.subscribe((function(e,t){ea.emit("vuex:mutation",e,t)}),{prepend:!0}),e.subscribeAction((function(e,t){ea.emit("vuex:action",e,t)}),{prepend:!0}))}(this)},ha={state:{configurable:!0}};function ua(e,t,n){return t.indexOf(e)<0&&(n&&n.prepend?t.unshift(e):t.push(e)),function(){var n=t.indexOf(e);n>-1&&t.splice(n,1)}}function da(e,t){e._actions=Object.create(null),e._mutations=Object.create(null),e._wrappedGetters=Object.create(null),e._modulesNamespaceMap=Object.create(null);var n=e.state;pa(e,n,[],e._modules.root,!0),fa(e,n,t)}function fa(e,t,n){var r=e._vm;e.getters={},e._makeLocalGettersCache=Object.create(null);var i=e._wrappedGetters,o={};na(i,(function(t,n){o[n]=function(e,t){return function(){return e(t)}}(t,e),Object.defineProperty(e.getters,n,{get:function(){return e._vm[n]},enumerable:!0})}));var s=sa.config.silent;sa.config.silent=!0,e._vm=new sa({data:{$$state:t},computed:o}),sa.config.silent=s,e.strict&&function(e){e._vm.$watch((function(){return this._data.$$state}),(function(){}),{deep:!0,sync:!0})}(e),r&&(n&&e._withCommit((function(){r._data.$$state=null})),sa.nextTick((function(){return r.$destroy()})))}function pa(e,t,n,r,i){var o=!n.length,s=e._modules.getNamespace(n);if(r.namespaced&&(e._modulesNamespaceMap[s],e._modulesNamespaceMap[s]=r),!o&&!i){var l=ma(t,n.slice(0,-1)),a=n[n.length-1];e._withCommit((function(){sa.set(l,a,r.state)}))}var c=r.context=function(e,t,n){var r=""===t,i={dispatch:r?e.dispatch:function(n,r,i){var o=ga(n,r,i),s=o.payload,l=o.options,a=o.type;return l&&l.root||(a=t+a),e.dispatch(a,s)},commit:r?e.commit:function(n,r,i){var o=ga(n,r,i),s=o.payload,l=o.options,a=o.type;l&&l.root||(a=t+a),e.commit(a,s,l)}};return Object.defineProperties(i,{getters:{get:r?function(){return e.getters}:function(){return function(e,t){if(!e._makeLocalGettersCache[t]){var n={},r=t.length;Object.keys(e.getters).forEach((function(i){if(i.slice(0,r)===t){var o=i.slice(r);Object.defineProperty(n,o,{get:function(){return e.getters[i]},enumerable:!0})}})),e._makeLocalGettersCache[t]=n}return e._makeLocalGettersCache[t]}(e,t)}},state:{get:function(){return ma(e.state,n)}}}),i}(e,s,n);r.forEachMutation((function(t,n){!function(e,t,n,r){var i=e._mutations[t]||(e._mutations[t]=[]);i.push((function(t){n.call(e,r.state,t)}))}(e,s+n,t,c)})),r.forEachAction((function(t,n){var r=t.root?n:s+n,i=t.handler||t;!function(e,t,n,r){var i=e._actions[t]||(e._actions[t]=[]);i.push((function(t){var i,o=n.call(e,{dispatch:r.dispatch,commit:r.commit,getters:r.getters,state:r.state,rootGetters:e.getters,rootState:e.state},t);return(i=o)&&"function"==typeof i.then||(o=Promise.resolve(o)),e._devtoolHook?o.catch((function(t){throw e._devtoolHook.emit("vuex:error",t),t})):o}))}(e,r,i,c)})),r.forEachGetter((function(t,n){!function(e,t,n,r){if(e._wrappedGetters[t])return;e._wrappedGetters[t]=function(e){return n(r.state,r.getters,e.state,e.getters)}}(e,s+n,t,c)})),r.forEachChild((function(r,o){pa(e,t,n.concat(o),r,i)}))}function ma(e,t){return t.reduce((function(e,t){return e[t]}),e)}function ga(e,t,n){return ra(e)&&e.type&&(n=t,t=e,e=e.type),{type:e,payload:t,options:n}}function ya(e){sa&&e===sa||function(e){if(Number(e.version.split(".")[0])>=2)e.mixin({beforeCreate:n});else{var t=e.prototype._init;e.prototype._init=function(e){void 0===e&&(e={}),e.init=e.init?[n].concat(e.init):n,t.call(this,e)}}function n(){var e=this.$options;e.store?this.$store="function"==typeof e.store?e.store():e.store:e.parent&&e.parent.$store&&(this.$store=e.parent.$store)}}(sa=e)}ha.state.get=function(){return this._vm._data.$$state},ha.state.set=function(e){},ca.prototype.commit=function(e,t,n){var r=this,i=ga(e,t,n),o=i.type,s=i.payload,l={type:o,payload:s},a=this._mutations[o];a&&(this._withCommit((function(){a.forEach((function(e){e(s)}))})),this._subscribers.slice().forEach((function(e){return e(l,r.state)})))},ca.prototype.dispatch=function(e,t){var n=this,r=ga(e,t),i=r.type,o=r.payload,s={type:i,payload:o},l=this._actions[i];if(l){try{this._actionSubscribers.slice().filter((function(e){return e.before})).forEach((function(e){return e.before(s,n.state)}))}catch(c){}var a=l.length>1?Promise.all(l.map((function(e){return e(o)}))):l[0](o);return new Promise((function(e,t){a.then((function(t){try{n._actionSubscribers.filter((function(e){return e.after})).forEach((function(e){return e.after(s,n.state)}))}catch(c){}e(t)}),(function(e){try{n._actionSubscribers.filter((function(e){return e.error})).forEach((function(t){return t.error(s,n.state,e)}))}catch(c){}t(e)}))}))}},ca.prototype.subscribe=function(e,t){return ua(e,this._subscribers,t)},ca.prototype.subscribeAction=function(e,t){return ua("function"==typeof e?{before:e}:e,this._actionSubscribers,t)},ca.prototype.watch=function(e,t,n){var r=this;return this._watcherVM.$watch((function(){return e(r.state,r.getters)}),t,n)},ca.prototype.replaceState=function(e){var t=this;this._withCommit((function(){t._vm._data.$$state=e}))},ca.prototype.registerModule=function(e,t,n){void 0===n&&(n={}),"string"==typeof e&&(e=[e]),this._modules.register(e,t),pa(this,this.state,e,this._modules.get(e),n.preserveState),fa(this,this.state)},ca.prototype.unregisterModule=function(e){var t=this;"string"==typeof e&&(e=[e]),this._modules.unregister(e),this._withCommit((function(){var n=ma(t.state,e.slice(0,-1));sa.delete(n,e[e.length-1])})),da(this)},ca.prototype.hasModule=function(e){return"string"==typeof e&&(e=[e]),this._modules.isRegistered(e)},ca.prototype.hotUpdate=function(e){this._modules.update(e),da(this,!0)},ca.prototype._withCommit=function(e){var t=this._committing;this._committing=!0,e(),this._committing=t},Object.defineProperties(ca.prototype,ha);var va=ka((function(e,t){var n={};return Sa(t).forEach((function(t){var r=t.key,i=t.val;n[r]=function(){var t=this.$store.state,n=this.$store.getters;if(e){var r=Ma(this.$store,"mapState",e);if(!r)return;t=r.context.state,n=r.context.getters}return"function"==typeof i?i.call(this,t,n):t[i]},n[r].vuex=!0})),n})),wa=ka((function(e,t){var n={};return Sa(t).forEach((function(t){var r=t.key,i=t.val;n[r]=function(){for(var t=[],n=arguments.length;n--;)t[n]=arguments[n];var r=this.$store.commit;if(e){var o=Ma(this.$store,"mapMutations",e);if(!o)return;r=o.context.commit}return"function"==typeof i?i.apply(this,[r].concat(t)):r.apply(this.$store,[i].concat(t))}})),n})),ba=ka((function(e,t){var n={};return Sa(t).forEach((function(t){var r=t.key,i=t.val;i=e+i,n[r]=function(){if(!e||Ma(this.$store,"mapGetters",e))return this.$store.getters[i]},n[r].vuex=!0})),n})),xa=ka((function(e,t){var n={};return Sa(t).forEach((function(t){var r=t.key,i=t.val;n[r]=function(){for(var t=[],n=arguments.length;n--;)t[n]=arguments[n];var r=this.$store.dispatch;if(e){var o=Ma(this.$store,"mapActions",e);if(!o)return;r=o.context.dispatch}return"function"==typeof i?i.apply(this,[r].concat(t)):r.apply(this.$store,[i].concat(t))}})),n}));function Sa(e){return function(e){return Array.isArray(e)||ra(e)}(e)?Array.isArray(e)?e.map((function(e){return{key:e,val:e}})):Object.keys(e).map((function(t){return{key:t,val:e[t]}})):[]}function ka(e){return function(t,n){return"string"!=typeof t?(n=t,t=""):"/"!==t.charAt(t.length-1)&&(t+="/"),e(t,n)}}function Ma(e,t,n){return e._modulesNamespaceMap[n]}function Oa(e,t,n){var r=n?e.groupCollapsed:e.group;try{r.call(e,t)}catch(i){e.log(t)}}function Ca(e){try{e.groupEnd()}catch(t){e.log("—— log end ——")}}function Na(){var e=new Date;return" @ "+Da(e.getHours(),2)+":"+Da(e.getMinutes(),2)+":"+Da(e.getSeconds(),2)+"."+Da(e.getMilliseconds(),3)}function Da(e,t){return n="0",r=t-e.toString().length,new Array(r+1).join(n)+e;var n,r}var Ta,Aa,$a={Store:ca,install:ya,version:"3.6.2",mapState:va,mapMutations:wa,mapGetters:ba,mapActions:xa,createNamespacedHelpers:function(e){return{mapState:va.bind(null,e),mapGetters:ba.bind(null,e),mapMutations:wa.bind(null,e),mapActions:xa.bind(null,e)}},createLogger:function(e){void 0===e&&(e={});var t=e.collapsed;void 0===t&&(t=!0);var n=e.filter;void 0===n&&(n=function(e,t,n){return!0});var r=e.transformer;void 0===r&&(r=function(e){return e});var i=e.mutationTransformer;void 0===i&&(i=function(e){return e});var o=e.actionFilter;void 0===o&&(o=function(e,t){return!0});var s=e.actionTransformer;void 0===s&&(s=function(e){return e});var l=e.logMutations;void 0===l&&(l=!0);var a=e.logActions;void 0===a&&(a=!0);var c=e.logger;return void 0===c&&(c=console),function(e){var h=ta(e.state);void 0!==c&&(l&&e.subscribe((function(e,o){var s=ta(o);if(n(e,h,s)){var l=Na(),a=i(e),u="mutation "+e.type+l;Oa(c,u,t),c.log("%c prev state","color: #9E9E9E; font-weight: bold",r(h)),c.log("%c mutation","color: #03A9F4; font-weight: bold",a),c.log("%c next state","color: #4CAF50; font-weight: bold",r(s)),Ca(c)}h=s})),a&&e.subscribeAction((function(e,n){if(o(e,n)){var r=Na(),i=s(e),l="action "+e.type+r;Oa(c,l,t),c.log("%c action","color: #03A9F4; font-weight: bold",i),Ca(c)}})))}}},Ea={},Pa={};function Ra(){if(Ta)return Pa;function e(e){return null==e}function t(e){return null!=e}function n(e,t){return t.tag===e.tag&&t.key===e.key}function r(e){var t=e.tag;e.vm=new t({data:e.args})}function i(e,n,r){var i,o,s={};for(i=n;i<=r;++i)t(o=e[i].key)&&(s[o]=i);return s}function o(e,t,n){for(;t<=n;++t)r(e[t])}function s(e,n,r){for(;n<=r;++n){var i=e[n];t(i)&&(i.vm.$destroy(),i.vm=null)}}function l(e,t){e!==t&&(t.vm=e.vm,function(e){for(var t=Object.keys(e.args),n=0;nm?o(c,p,v):p>v&&s(a,f,m)}(a,c):t(c)?o(c,0,c.length-1):t(a)&&s(a,0,a.length-1)},Pa}function Ia(){return Aa||(Aa=1,function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.Vuelidate=C,e.validationMixin=e.default=void 0,Object.defineProperty(e,"withParams",{enumerable:!0,get:function(){return n.withParams}});var t=Ra(),n=h();function r(e){return function(e){if(Array.isArray(e))return i(e)}(e)||function(e){if("undefined"!=typeof Symbol&&null!=e[Symbol.iterator]||null!=e["@@iterator"])return Array.from(e)}(e)||function(e,t){if(!e)return;if("string"==typeof e)return i(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);"Object"===n&&e.constructor&&(n=e.constructor.name);if("Map"===n||"Set"===n)return Array.from(e);if("Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return i(e,t)}(e)||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.")}()}function i(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n1?l:l.$sub[0]:null}}},computed:{run:function(){var e=this,t=this.lazyParentModel();if(Array.isArray(t)&&t.__ob__){var n=t.__ob__.dep;n.depend();var r=n.constructor.target;if(!this._indirectWatcher){var i=r.constructor;this._indirectWatcher=new i(this,(function(){return e.runRule(t)}),null,{lazy:!0})}var o=this.getModel();if(!this._indirectWatcher.dirty&&this._lastModel===o)return this._indirectWatcher.depend(),r.value;this._lastModel=o,this._indirectWatcher.evaluate(),this._indirectWatcher.depend()}else this._indirectWatcher&&(this._indirectWatcher.teardown(),this._indirectWatcher=null);return this._indirectWatcher?this._indirectWatcher.value:this.runRule(t)},$params:function(){return this.run.params},proxy:function(){var e=this.run.output;return e[m]?!!e.v:!!e},$pending:function(){var e=this.run.output;return!!e[m]&&e.p}},destroyed:function(){this._indirectWatcher&&(this._indirectWatcher.teardown(),this._indirectWatcher=null)}}),l=i.extend({data:function(){return{dirty:!1,validations:null,lazyModel:null,model:null,prop:null,lazyParentModel:null,rootModel:null}},methods:s(s({},v),{},{refProxy:function(e){return this.getRef(e).proxy},getRef:function(e){return this.refs[e]},isNested:function(e){return"function"!=typeof this.validations[e]}}),computed:s(s({},g),{},{nestedKeys:function(){return this.keys.filter(this.isNested)},ruleKeys:function(){var e=this;return this.keys.filter((function(t){return!e.isNested(t)}))},keys:function(){return Object.keys(this.validations).filter((function(e){return"$params"!==e}))},proxy:function(){var e=this,t=u(this.keys,(function(t){return{enumerable:!0,configurable:!0,get:function(){return e.refProxy(t)}}})),n=u(w,(function(t){return{enumerable:!0,configurable:!0,get:function(){return e[t]}}})),r=u(b,(function(t){return{enumerable:!1,configurable:!0,get:function(){return e[t]}}})),i=this.hasIter()?{$iter:{enumerable:!0,value:Object.defineProperties({},s({},t))}}:{};return Object.defineProperties({},s(s(s(s({},t),i),{},{$model:{enumerable:!0,get:function(){var t=e.lazyParentModel();return null!=t?t[e.prop]:null},set:function(t){var n=e.lazyParentModel();null!=n&&(n[e.prop]=t,e.$touch())}}},n),r))},children:function(){var e=this;return[].concat(r(this.nestedKeys.map((function(t){return y(e,t)}))),r(this.ruleKeys.map((function(t){return S(e,t)})))).filter(Boolean)}})}),a=l.extend({methods:{isNested:function(e){return void 0!==this.validations[e]()},getRef:function(e){var t=this;return{get proxy(){return t.validations[e]()||!1}}}}}),h=l.extend({computed:{keys:function(){var e=this.getModel();return f(e)?Object.keys(e):[]},tracker:function(){var e=this,t=this.validations.$trackBy;return t?function(n){return"".concat(p(e.rootModel,e.getModelKey(n),t))}:function(e){return"".concat(e)}},getModelLazy:function(){var e=this;return function(){return e.getModel()}},children:function(){var e=this,n=this.validations,r=this.getModel(),i=s({},n);delete i.$trackBy;var o={};return this.keys.map((function(n){var s=e.tracker(n);return o.hasOwnProperty(s)?null:(o[s]=!0,(0,t.h)(l,s,{validations:i,prop:n,lazyParentModel:e.getModelLazy,model:r[n],rootModel:e.rootModel}))})).filter(Boolean)}},methods:{isNested:function(){return!0},getRef:function(e){return this.refs[this.tracker(e)]},hasIter:function(){return!0}}}),y=function(e,n){if("$each"===n)return(0,t.h)(h,n,{validations:e.validations[n],lazyParentModel:e.lazyParentModel,prop:n,lazyModel:e.getModel,rootModel:e.rootModel});var r=e.validations[n];if(Array.isArray(r)){var i=e.rootModel,o=u(r,(function(e){return function(){return p(i,i.$v,e)}}),(function(e){return Array.isArray(e)?e.join("."):e}));return(0,t.h)(a,n,{validations:o,lazyParentModel:c,prop:n,lazyModel:c,rootModel:i})}return(0,t.h)(l,n,{validations:r,lazyParentModel:e.getModel,prop:n,lazyModel:e.getModelKey,rootModel:e.rootModel})},S=function(e,n){return(0,t.h)(o,n,{rule:e.validations[n],lazyParentModel:e.lazyParentModel,lazyModel:e.getModel,rootModel:e.rootModel})};return x={VBase:i,Validation:l}},k=null;var M=function(e,n){var r=function(e){if(k)return k;for(var t=e.constructor;t.super;)t=t.super;return k=t,t}(e),i=S(r),o=i.Validation;return new(0,i.VBase)({computed:{children:function(){var r="function"==typeof n?n.call(e):n;return[(0,t.h)(o,"$v",{validations:r,lazyParentModel:c,prop:"$v",model:e,rootModel:e})]}}})},O={data:function(){var e=this.$options.validations;return e&&(this._vuelidate=M(this,e)),{}},beforeCreate:function(){var e=this.$options;e.validations&&(e.computed||(e.computed={}),e.computed.$v||(e.computed.$v=function(){return this._vuelidate?this._vuelidate.refs.$v.proxy:null}))},beforeDestroy:function(){this._vuelidate&&(this._vuelidate.$destroy(),this._vuelidate=null)}};function C(e){e.mixin(O)}e.validationMixin=O;var N=C;e.default=N}(Ea)),Ea}const za=e(Ia());export{Vl as A,Yl as B,Xl as C,yt as D,_n as E,Ce as F,Jl as G,Ql as H,Bs as I,$a as J,Cn as N,Vn as P,Ee as S,Mn as T,za as V,xs as a,Ts as b,tl as c,Qs as d,Zs as e,Ls as f,$s as g,ks as h,Es as i,mt as j,ms as k,el as l,Vs as m,Qo as n,Nt as o,_s as p,xl as q,Sl as r,As as s,Ws as t,Fs as u,Se as v,Xs as w,bl as x,wl as y,yl as z}; diff --git a/kirby/panel/dist/js/vue.js b/kirby/panel/dist/js/vue.js deleted file mode 100644 index efebcc6..0000000 --- a/kirby/panel/dist/js/vue.js +++ /dev/null @@ -1,11 +0,0 @@ -/*! - * Vue.js v2.7.14 - * (c) 2014-2022 Evan You - * Released under the MIT License. - */ -/*! - * Vue.js v2.7.14 - * (c) 2014-2022 Evan You - * Released under the MIT License. - */ -!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).Vue=e()}(this,(function(){"use strict";var t=Object.freeze({}),e=Array.isArray;function n(t){return null==t}function r(t){return null!=t}function o(t){return!0===t}function i(t){return"string"==typeof t||"number"==typeof t||"symbol"==typeof t||"boolean"==typeof t}function a(t){return"function"==typeof t}function s(t){return null!==t&&"object"==typeof t}var c=Object.prototype.toString;function u(t){return"[object Object]"===c.call(t)}function l(t){var e=parseFloat(String(t));return e>=0&&Math.floor(e)===e&&isFinite(t)}function f(t){return r(t)&&"function"==typeof t.then&&"function"==typeof t.catch}function d(t){return null==t?"":Array.isArray(t)||u(t)&&t.toString===c?JSON.stringify(t,null,2):String(t)}function p(t){var e=parseFloat(t);return isNaN(e)?t:e}function v(t,e){for(var n=Object.create(null),r=t.split(","),o=0;o-1)return t.splice(r,1)}}var y=Object.prototype.hasOwnProperty;function _(t,e){return y.call(t,e)}function b(t){var e=Object.create(null);return function(n){return e[n]||(e[n]=t(n))}}var $=/-(\w)/g,w=b((function(t){return t.replace($,(function(t,e){return e?e.toUpperCase():""}))})),x=b((function(t){return t.charAt(0).toUpperCase()+t.slice(1)})),C=/\B([A-Z])/g,k=b((function(t){return t.replace(C,"-$1").toLowerCase()}));var S=Function.prototype.bind?function(t,e){return t.bind(e)}:function(t,e){function n(n){var r=arguments.length;return r?r>1?t.apply(e,arguments):t.call(e,n):t.call(e)}return n._length=t.length,n};function O(t,e){e=e||0;for(var n=t.length-e,r=new Array(n);n--;)r[n]=t[n+e];return r}function T(t,e){for(var n in e)t[n]=e[n];return t}function A(t){for(var e={},n=0;n0,G=q&&q.indexOf("edge/")>0;q&&q.indexOf("android");var X=q&&/iphone|ipad|ipod|ios/.test(q);q&&/chrome\/\d+/.test(q),q&&/phantomjs/.test(q);var Y,Q=q&&q.match(/firefox\/(\d+)/),tt={}.watch,et=!1;if(J)try{var nt={};Object.defineProperty(nt,"passive",{get:function(){et=!0}}),window.addEventListener("test-passive",null,nt)}catch(t){}var rt=function(){return void 0===Y&&(Y=!J&&"undefined"!=typeof global&&(global.process&&"server"===global.process.env.VUE_ENV)),Y},ot=J&&window.__VUE_DEVTOOLS_GLOBAL_HOOK__;function it(t){return"function"==typeof t&&/native code/.test(t.toString())}var at,st="undefined"!=typeof Symbol&&it(Symbol)&&"undefined"!=typeof Reflect&&it(Reflect.ownKeys);at="undefined"!=typeof Set&&it(Set)?Set:function(){function t(){this.set=Object.create(null)}return t.prototype.has=function(t){return!0===this.set[t]},t.prototype.add=function(t){this.set[t]=!0},t.prototype.clear=function(){this.set=Object.create(null)},t}();var ct=null;function ut(t){void 0===t&&(t=null),t||ct&&ct._scope.off(),ct=t,t&&t._scope.on()}var lt=function(){function t(t,e,n,r,o,i,a,s){this.tag=t,this.data=e,this.children=n,this.text=r,this.elm=o,this.ns=void 0,this.context=i,this.fnContext=void 0,this.fnOptions=void 0,this.fnScopeId=void 0,this.key=e&&e.key,this.componentOptions=a,this.componentInstance=void 0,this.parent=void 0,this.raw=!1,this.isStatic=!1,this.isRootInsert=!0,this.isComment=!1,this.isCloned=!1,this.isOnce=!1,this.asyncFactory=s,this.asyncMeta=void 0,this.isAsyncPlaceholder=!1}return Object.defineProperty(t.prototype,"child",{get:function(){return this.componentInstance},enumerable:!1,configurable:!0}),t}(),ft=function(t){void 0===t&&(t="");var e=new lt;return e.text=t,e.isComment=!0,e};function dt(t){return new lt(void 0,void 0,void 0,String(t))}function pt(t){var e=new lt(t.tag,t.data,t.children&&t.children.slice(),t.text,t.elm,t.context,t.componentOptions,t.asyncFactory);return e.ns=t.ns,e.isStatic=t.isStatic,e.key=t.key,e.isComment=t.isComment,e.fnContext=t.fnContext,e.fnOptions=t.fnOptions,e.fnScopeId=t.fnScopeId,e.asyncMeta=t.asyncMeta,e.isCloned=!0,e}var vt=0,ht=[],mt=function(){function t(){this._pending=!1,this.id=vt++,this.subs=[]}return t.prototype.addSub=function(t){this.subs.push(t)},t.prototype.removeSub=function(t){this.subs[this.subs.indexOf(t)]=null,this._pending||(this._pending=!0,ht.push(this))},t.prototype.depend=function(e){t.target&&t.target.addDep(this)},t.prototype.notify=function(t){for(var e=this.subs.filter((function(t){return t})),n=0,r=e.length;n0&&(Yt((c=Qt(c,"".concat(a||"","_").concat(s)))[0])&&Yt(l)&&(f[u]=dt(l.text+c[0].text),c.shift()),f.push.apply(f,c)):i(c)?Yt(l)?f[u]=dt(l.text+c):""!==c&&f.push(dt(c)):Yt(c)&&Yt(l)?f[u]=dt(l.text+c.text):(o(t._isVList)&&r(c.tag)&&n(c.key)&&r(a)&&(c.key="__vlist".concat(a,"_").concat(s,"__")),f.push(c)));return f}function te(t,n,c,u,l,f){return(e(c)||i(c))&&(l=u,u=c,c=void 0),o(f)&&(l=2),function(t,n,o,i,c){if(r(o)&&r(o.__ob__))return ft();r(o)&&r(o.is)&&(n=o.is);if(!n)return ft();e(i)&&a(i[0])&&((o=o||{}).scopedSlots={default:i[0]},i.length=0);2===c?i=Xt(i):1===c&&(i=function(t){for(var n=0;n0,s=n?!!n.$stable:!a,c=n&&n.$key;if(n){if(n._normalized)return n._normalized;if(s&&o&&o!==t&&c===o.$key&&!a&&!o.$hasNormal)return o;for(var u in i={},n)n[u]&&"$"!==u[0]&&(i[u]=$e(e,r,u,n[u]))}else i={};for(var l in r)l in i||(i[l]=we(r,l));return n&&Object.isExtensible(n)&&(n._normalized=i),z(i,"$stable",s),z(i,"$key",c),z(i,"$hasNormal",a),i}function $e(t,n,r,o){var i=function(){var n=ct;ut(t);var r=arguments.length?o.apply(null,arguments):o({}),i=(r=r&&"object"==typeof r&&!e(r)?[r]:Xt(r))&&r[0];return ut(n),r&&(!i||1===r.length&&i.isComment&&!_e(i))?void 0:r};return o.proxy&&Object.defineProperty(n,r,{get:i,enumerable:!0,configurable:!0}),i}function we(t,e){return function(){return t[e]}}function xe(e){return{get attrs(){if(!e._attrsProxy){var n=e._attrsProxy={};z(n,"_v_attr_proxy",!0),Ce(n,e.$attrs,t,e,"$attrs")}return e._attrsProxy},get listeners(){e._listenersProxy||Ce(e._listenersProxy={},e.$listeners,t,e,"$listeners");return e._listenersProxy},get slots(){return function(t){t._slotsProxy||Se(t._slotsProxy={},t.$scopedSlots);return t._slotsProxy}(e)},emit:S(e.$emit,e),expose:function(t){t&&Object.keys(t).forEach((function(n){return Bt(e,t,n)}))}}}function Ce(t,e,n,r,o){var i=!1;for(var a in e)a in t?e[a]!==n[a]&&(i=!0):(i=!0,ke(t,a,r,o));for(var a in t)a in e||(i=!0,delete t[a]);return i}function ke(t,e,n,r){Object.defineProperty(t,e,{enumerable:!0,configurable:!0,get:function(){return n[r][e]}})}function Se(t,e){for(var n in e)t[n]=e[n];for(var n in t)n in e||delete t[n]}function Oe(){var t=ct;return t._setupContext||(t._setupContext=xe(t))}var Te,Ae=null;function je(t,e){return(t.__esModule||st&&"Module"===t[Symbol.toStringTag])&&(t=t.default),s(t)?e.extend(t):t}function Ee(t){if(e(t))for(var n=0;ndocument.createEvent("Event").timeStamp&&(Ze=function(){return Ge.now()})}var Xe=function(t,e){if(t.post){if(!e.post)return 1}else if(e.post)return-1;return t.id-e.id};function Ye(){var t,e;for(We=Ze(),Je=!0,Ue.sort(Xe),qe=0;qeqe&&Ue[n].id>t.id;)n--;Ue.splice(n+1,0,t)}else Ue.push(t);Ke||(Ke=!0,Cn(Ye))}}var tn="watcher",en="".concat(tn," callback"),nn="".concat(tn," getter"),rn="".concat(tn," cleanup");function on(t,e){return cn(t,null,{flush:"post"})}var an,sn={};function cn(n,r,o){var i=void 0===o?t:o,s=i.immediate,c=i.deep,u=i.flush,l=void 0===u?"pre":u;i.onTrack,i.onTrigger;var f,d,p=ct,v=function(t,e,n){return void 0===n&&(n=null),dn(t,null,n,p,e)},h=!1,m=!1;if(Ft(n)?(f=function(){return n.value},h=It(n)):Mt(n)?(f=function(){return n.__ob__.dep.depend(),n},c=!0):e(n)?(m=!0,h=n.some((function(t){return Mt(t)||It(t)})),f=function(){return n.map((function(t){return Ft(t)?t.value:Mt(t)?Bn(t):a(t)?v(t,nn):void 0}))}):f=a(n)?r?function(){return v(n,nn)}:function(){if(!p||!p._isDestroyed)return d&&d(),v(n,tn,[y])}:j,r&&c){var g=f;f=function(){return Bn(g())}}var y=function(t){d=_.onStop=function(){v(t,rn)}};if(rt())return y=j,r?s&&v(r,en,[f(),m?[]:void 0,y]):f(),j;var _=new Vn(ct,f,j,{lazy:!0});_.noRecurse=!r;var b=m?[]:sn;return _.run=function(){if(_.active)if(r){var t=_.get();(c||h||(m?t.some((function(t,e){return I(t,b[e])})):I(t,b)))&&(d&&d(),v(r,en,[t,b===sn?void 0:b,y]),b=t)}else _.get()},"sync"===l?_.update=_.run:"post"===l?(_.post=!0,_.update=function(){return Qe(_)}):_.update=function(){if(p&&p===ct&&!p._isMounted){var t=p._preWatchers||(p._preWatchers=[]);t.indexOf(_)<0&&t.push(_)}else Qe(_)},r?s?_.run():b=_.get():"post"===l&&p?p.$once("hook:mounted",(function(){return _.get()})):_.get(),function(){_.teardown()}}var un=function(){function t(t){void 0===t&&(t=!1),this.detached=t,this.active=!0,this.effects=[],this.cleanups=[],this.parent=an,!t&&an&&(this.index=(an.scopes||(an.scopes=[])).push(this)-1)}return t.prototype.run=function(t){if(this.active){var e=an;try{return an=this,t()}finally{an=e}}},t.prototype.on=function(){an=this},t.prototype.off=function(){an=this.parent},t.prototype.stop=function(t){if(this.active){var e=void 0,n=void 0;for(e=0,n=this.effects.length;e1)return n&&a(e)?e.call(r):e}},h:function(t,e,n){return te(ct,t,e,n,2,!0)},getCurrentInstance:function(){return ct&&{proxy:ct}},useSlots:function(){return Oe().slots},useAttrs:function(){return Oe().attrs},useListeners:function(){return Oe().listeners},mergeDefaults:function(t,n){var r=e(t)?t.reduce((function(t,e){return t[e]={},t}),{}):t;for(var o in n){var i=r[o];i?e(i)||a(i)?r[o]={type:i,default:n[o]}:i.default=n[o]:null===i&&(r[o]={default:n[o]})}return r},nextTick:Cn,set:jt,del:Et,useCssModule:function(e){return t},useCssVars:function(t){if(J){var e=ct;e&&on((function(){var n=e.$el,r=t(e,e._setupProxy);if(n&&1===n.nodeType){var o=n.style;for(var i in r)o.setProperty("--".concat(i),r[i])}}))}},defineAsyncComponent:function(t){a(t)&&(t={loader:t});var e=t.loader,n=t.loadingComponent,r=t.errorComponent,o=t.delay,i=void 0===o?200:o,s=t.timeout;t.suspensible;var c=t.onError,u=null,l=0,f=function(){var t;return u||(t=u=e().catch((function(t){if(t=t instanceof Error?t:new Error(String(t)),c)return new Promise((function(e,n){c(t,(function(){return e((l++,u=null,f()))}),(function(){return n(t)}),l+1)}));throw t})).then((function(e){return t!==u&&u?u:(e&&(e.__esModule||"Module"===e[Symbol.toStringTag])&&(e=e.default),e)})))};return function(){return{component:f(),delay:i,timeout:s,error:r,loading:n}}},onBeforeMount:Sn,onMounted:On,onBeforeUpdate:Tn,onUpdated:An,onBeforeUnmount:jn,onUnmounted:En,onActivated:Nn,onDeactivated:Pn,onServerPrefetch:Dn,onRenderTracked:Mn,onRenderTriggered:In,onErrorCaptured:function(t,e){void 0===e&&(e=ct),Ln(t,e)}}),Hn=new at;function Bn(t){return Un(t,Hn),Hn.clear(),t}function Un(t,n){var r,o,i=e(t);if(!(!i&&!s(t)||t.__v_skip||Object.isFrozen(t)||t instanceof lt)){if(t.__ob__){var a=t.__ob__.dep.id;if(n.has(a))return;n.add(a)}if(i)for(r=t.length;r--;)Un(t[r],n);else if(Ft(t))Un(t.value,n);else for(r=(o=Object.keys(t)).length;r--;)Un(t[o[r]],n)}}var zn=0,Vn=function(){function t(t,e,n,r,o){!function(t,e){void 0===e&&(e=an),e&&e.active&&e.effects.push(t)}(this,an&&!an._vm?an:t?t._scope:void 0),(this.vm=t)&&o&&(t._watcher=this),r?(this.deep=!!r.deep,this.user=!!r.user,this.lazy=!!r.lazy,this.sync=!!r.sync,this.before=r.before):this.deep=this.user=this.lazy=this.sync=!1,this.cb=n,this.id=++zn,this.active=!0,this.post=!1,this.dirty=this.lazy,this.deps=[],this.newDeps=[],this.depIds=new at,this.newDepIds=new at,this.expression="",a(e)?this.getter=e:(this.getter=function(t){if(!V.test(t)){var e=t.split(".");return function(t){for(var n=0;n-1)if(i&&!_(o,"default"))s=!1;else if(""===s||s===k(t)){var u=xr(String,o.type);(u<0||c-1:"string"==typeof t?t.split(",").indexOf(n)>-1:(r=t,"[object RegExp]"===c.call(r)&&t.test(n));var r}function Tr(t,e){var n=t.cache,r=t.keys,o=t._vnode;for(var i in n){var a=n[i];if(a){var s=a.name;s&&!e(s)&&Ar(n,i,r,o)}}}function Ar(t,e,n,r){var o=t[e];!o||r&&o.tag===r.tag||o.componentInstance.$destroy(),t[e]=null,g(n,e)}!function(e){e.prototype._init=function(e){var n=this;n._uid=tr++,n._isVue=!0,n.__v_skip=!0,n._scope=new un(!0),n._scope._vm=!0,e&&e._isComponent?function(t,e){var n=t.$options=Object.create(t.constructor.options),r=e._parentVnode;n.parent=e.parent,n._parentVnode=r;var o=r.componentOptions;n.propsData=o.propsData,n._parentListeners=o.listeners,n._renderChildren=o.children,n._componentTag=o.tag,e.render&&(n.render=e.render,n.staticRenderFns=e.staticRenderFns)}(n,e):n.$options=gr(er(n.constructor),e||{},n),n._renderProxy=n,n._self=n,function(t){var e=t.$options,n=e.parent;if(n&&!e.abstract){for(;n.$options.abstract&&n.$parent;)n=n.$parent;n.$children.push(t)}t.$parent=n,t.$root=n?n.$root:t,t.$children=[],t.$refs={},t._provided=n?n._provided:Object.create(null),t._watcher=null,t._inactive=null,t._directInactive=!1,t._isMounted=!1,t._isDestroyed=!1,t._isBeingDestroyed=!1}(n),function(t){t._events=Object.create(null),t._hasHookEvent=!1;var e=t.$options._parentListeners;e&&Me(t,e)}(n),function(e){e._vnode=null,e._staticTrees=null;var n=e.$options,r=e.$vnode=n._parentVnode,o=r&&r.context;e.$slots=ge(n._renderChildren,o),e.$scopedSlots=r?be(e.$parent,r.data.scopedSlots,e.$slots):t,e._c=function(t,n,r,o){return te(e,t,n,r,o,!1)},e.$createElement=function(t,n,r,o){return te(e,t,n,r,o,!0)};var i=r&&r.data;At(e,"$attrs",i&&i.attrs||t,null,!0),At(e,"$listeners",n._parentListeners||t,null,!0)}(n),Be(n,"beforeCreate",void 0,!1),function(t){var e=Qn(t.$options.inject,t);e&&(kt(!1),Object.keys(e).forEach((function(n){At(t,n,e[n])})),kt(!0))}(n),qn(n),function(t){var e=t.$options.provide;if(e){var n=a(e)?e.call(t):e;if(!s(n))return;for(var r=ln(t),o=st?Reflect.ownKeys(n):Object.keys(n),i=0;i1?O(n):n;for(var r=O(arguments,1),o='event handler for "'.concat(t,'"'),i=0,a=n.length;iparseInt(this.max)&&Ar(e,n[0],n,this._vnode),this.vnodeToCache=null}}},created:function(){this.cache=Object.create(null),this.keys=[]},destroyed:function(){for(var t in this.cache)Ar(this.cache,t,this.keys)},mounted:function(){var t=this;this.cacheVNode(),this.$watch("include",(function(e){Tr(t,(function(t){return Or(e,t)}))})),this.$watch("exclude",(function(e){Tr(t,(function(t){return!Or(e,t)}))}))},updated:function(){this.cacheVNode()},render:function(){var t=this.$slots.default,e=Ee(t),n=e&&e.componentOptions;if(n){var r=Sr(n),o=this.include,i=this.exclude;if(o&&(!r||!Or(o,r))||i&&r&&Or(i,r))return e;var a=this.cache,s=this.keys,c=null==e.key?n.Ctor.cid+(n.tag?"::".concat(n.tag):""):e.key;a[c]?(e.componentInstance=a[c].componentInstance,g(s,c),s.push(c)):(this.vnodeToCache=e,this.keyToCache=c),e.data.keepAlive=!0}return e||t&&t[0]}},Nr={KeepAlive:Er};!function(t){var e={get:function(){return H}};Object.defineProperty(t,"config",e),t.util={warn:lr,extend:T,mergeOptions:gr,defineReactive:At},t.set=jt,t.delete=Et,t.nextTick=Cn,t.observable=function(t){return Tt(t),t},t.options=Object.create(null),R.forEach((function(e){t.options[e+"s"]=Object.create(null)})),t.options._base=t,T(t.options.components,Nr),function(t){t.use=function(t){var e=this._installedPlugins||(this._installedPlugins=[]);if(e.indexOf(t)>-1)return this;var n=O(arguments,1);return n.unshift(this),a(t.install)?t.install.apply(t,n):a(t)&&t.apply(null,n),e.push(t),this}}(t),function(t){t.mixin=function(t){return this.options=gr(this.options,t),this}}(t),kr(t),function(t){R.forEach((function(e){t[e]=function(t,n){return n?("component"===e&&u(n)&&(n.name=n.name||t,n=this.options._base.extend(n)),"directive"===e&&a(n)&&(n={bind:n,update:n}),this.options[e+"s"][t]=n,n):this.options[e+"s"][t]}}))}(t)}(Cr),Object.defineProperty(Cr.prototype,"$isServer",{get:rt}),Object.defineProperty(Cr.prototype,"$ssrContext",{get:function(){return this.$vnode&&this.$vnode.ssrContext}}),Object.defineProperty(Cr,"FunctionalRenderContext",{value:nr}),Cr.version=Rn;var Pr=v("style,class"),Dr=v("input,textarea,option,select,progress"),Mr=function(t,e,n){return"value"===n&&Dr(t)&&"button"!==e||"selected"===n&&"option"===t||"checked"===n&&"input"===t||"muted"===n&&"video"===t},Ir=v("contenteditable,draggable,spellcheck"),Lr=v("events,caret,typing,plaintext-only"),Rr=v("allowfullscreen,async,autofocus,autoplay,checked,compact,controls,declare,default,defaultchecked,defaultmuted,defaultselected,defer,disabled,enabled,formnovalidate,hidden,indeterminate,inert,ismap,itemscope,loop,multiple,muted,nohref,noresize,noshade,novalidate,nowrap,open,pauseonexit,readonly,required,reversed,scoped,seamless,selected,sortable,truespeed,typemustmatch,visible"),Fr="http://www.w3.org/1999/xlink",Hr=function(t){return":"===t.charAt(5)&&"xlink"===t.slice(0,5)},Br=function(t){return Hr(t)?t.slice(6,t.length):""},Ur=function(t){return null==t||!1===t};function zr(t){for(var e=t.data,n=t,o=t;r(o.componentInstance);)(o=o.componentInstance._vnode)&&o.data&&(e=Vr(o.data,e));for(;r(n=n.parent);)n&&n.data&&(e=Vr(e,n.data));return function(t,e){if(r(t)||r(e))return Kr(t,Jr(e));return""}(e.staticClass,e.class)}function Vr(t,e){return{staticClass:Kr(t.staticClass,e.staticClass),class:r(t.class)?[t.class,e.class]:e.class}}function Kr(t,e){return t?e?t+" "+e:t:e||""}function Jr(t){return Array.isArray(t)?function(t){for(var e,n="",o=0,i=t.length;o-1?_o(t,e,n):Rr(e)?Ur(n)?t.removeAttribute(e):(n="allowfullscreen"===e&&"EMBED"===t.tagName?"true":e,t.setAttribute(e,n)):Ir(e)?t.setAttribute(e,function(t,e){return Ur(e)||"false"===e?"false":"contenteditable"===t&&Lr(e)?e:"true"}(e,n)):Hr(e)?Ur(n)?t.removeAttributeNS(Fr,Br(e)):t.setAttributeNS(Fr,e,n):_o(t,e,n)}function _o(t,e,n){if(Ur(n))t.removeAttribute(e);else{if(W&&!Z&&"TEXTAREA"===t.tagName&&"placeholder"===e&&""!==n&&!t.__ieph){var r=function(e){e.stopImmediatePropagation(),t.removeEventListener("input",r)};t.addEventListener("input",r),t.__ieph=!0}t.setAttribute(e,n)}}var bo={create:go,update:go};function $o(t,e){var o=e.elm,i=e.data,a=t.data;if(!(n(i.staticClass)&&n(i.class)&&(n(a)||n(a.staticClass)&&n(a.class)))){var s=zr(e),c=o._transitionClasses;r(c)&&(s=Kr(s,Jr(c))),s!==o._prevClass&&(o.setAttribute("class",s),o._prevClass=s)}}var wo,xo,Co,ko,So,Oo,To={create:$o,update:$o},Ao=/[\w).+\-_$\]]/;function jo(t){var e,n,r,o,i,a=!1,s=!1,c=!1,u=!1,l=0,f=0,d=0,p=0;for(r=0;r=0&&" "===(h=t.charAt(v));v--);h&&Ao.test(h)||(u=!0)}}else void 0===o?(p=r+1,o=t.slice(0,r).trim()):m();function m(){(i||(i=[])).push(t.slice(p,r).trim()),p=r+1}if(void 0===o?o=t.slice(0,r).trim():0!==p&&m(),i)for(r=0;r-1?{exp:t.slice(0,ko),key:'"'+t.slice(ko+1)+'"'}:{exp:t,key:null};xo=t,ko=So=Oo=0;for(;!qo();)Wo(Co=Jo())?Go(Co):91===Co&&Zo(Co);return{exp:t.slice(0,So),key:t.slice(So+1,Oo)}}(t);return null===n.key?"".concat(t,"=").concat(e):"$set(".concat(n.exp,", ").concat(n.key,", ").concat(e,")")}function Jo(){return xo.charCodeAt(++ko)}function qo(){return ko>=wo}function Wo(t){return 34===t||39===t}function Zo(t){var e=1;for(So=ko;!qo();)if(Wo(t=Jo()))Go(t);else if(91===t&&e++,93===t&&e--,0===e){Oo=ko;break}}function Go(t){for(var e=t;!qo()&&(t=Jo())!==e;);}var Xo,Yo="__r";function Qo(t,e,n){var r=Xo;return function o(){var i=e.apply(null,arguments);null!==i&&ni(t,o,n,r)}}var ti=mn&&!(Q&&Number(Q[1])<=53);function ei(t,e,n,r){if(ti){var o=We,i=e;e=i._wrapper=function(t){if(t.target===t.currentTarget||t.timeStamp>=o||t.timeStamp<=0||t.target.ownerDocument!==document)return i.apply(this,arguments)}}Xo.addEventListener(t,e,et?{capture:n,passive:r}:n)}function ni(t,e,n,r){(r||Xo).removeEventListener(t,e._wrapper||e,n)}function ri(t,e){if(!n(t.data.on)||!n(e.data.on)){var o=e.data.on||{},i=t.data.on||{};Xo=e.elm||t.elm,function(t){if(r(t.__r)){var e=W?"change":"input";t[e]=[].concat(t.__r,t[e]||[]),delete t.__r}r(t.__c)&&(t.change=[].concat(t.__c,t.change||[]),delete t.__c)}(o),Wt(o,i,ei,ni,Qo,e.context),Xo=void 0}}var oi,ii={create:ri,update:ri,destroy:function(t){return ri(t,io)}};function ai(t,e){if(!n(t.data.domProps)||!n(e.data.domProps)){var i,a,s=e.elm,c=t.data.domProps||{},u=e.data.domProps||{};for(i in(r(u.__ob__)||o(u._v_attr_proxy))&&(u=e.data.domProps=T({},u)),c)i in u||(s[i]="");for(i in u){if(a=u[i],"textContent"===i||"innerHTML"===i){if(e.children&&(e.children.length=0),a===c[i])continue;1===s.childNodes.length&&s.removeChild(s.childNodes[0])}if("value"===i&&"PROGRESS"!==s.tagName){s._value=a;var l=n(a)?"":String(a);si(s,l)&&(s.value=l)}else if("innerHTML"===i&&Zr(s.tagName)&&n(s.innerHTML)){(oi=oi||document.createElement("div")).innerHTML="".concat(a,"");for(var f=oi.firstChild;s.firstChild;)s.removeChild(s.firstChild);for(;f.firstChild;)s.appendChild(f.firstChild)}else if(a!==c[i])try{s[i]=a}catch(t){}}}}function si(t,e){return!t.composing&&("OPTION"===t.tagName||function(t,e){var n=!0;try{n=document.activeElement!==t}catch(t){}return n&&t.value!==e}(t,e)||function(t,e){var n=t.value,o=t._vModifiers;if(r(o)){if(o.number)return p(n)!==p(e);if(o.trim)return n.trim()!==e.trim()}return n!==e}(t,e))}var ci={create:ai,update:ai},ui=b((function(t){var e={},n=/:(.+)/;return t.split(/;(?![^(]*\))/g).forEach((function(t){if(t){var r=t.split(n);r.length>1&&(e[r[0].trim()]=r[1].trim())}})),e}));function li(t){var e=fi(t.style);return t.staticStyle?T(t.staticStyle,e):e}function fi(t){return Array.isArray(t)?A(t):"string"==typeof t?ui(t):t}var di,pi=/^--/,vi=/\s*!important$/,hi=function(t,e,n){if(pi.test(e))t.style.setProperty(e,n);else if(vi.test(n))t.style.setProperty(k(e),n.replace(vi,""),"important");else{var r=gi(e);if(Array.isArray(n))for(var o=0,i=n.length;o-1?e.split(bi).forEach((function(e){return t.classList.add(e)})):t.classList.add(e);else{var n=" ".concat(t.getAttribute("class")||""," ");n.indexOf(" "+e+" ")<0&&t.setAttribute("class",(n+e).trim())}}function wi(t,e){if(e&&(e=e.trim()))if(t.classList)e.indexOf(" ")>-1?e.split(bi).forEach((function(e){return t.classList.remove(e)})):t.classList.remove(e),t.classList.length||t.removeAttribute("class");else{for(var n=" ".concat(t.getAttribute("class")||""," "),r=" "+e+" ";n.indexOf(r)>=0;)n=n.replace(r," ");(n=n.trim())?t.setAttribute("class",n):t.removeAttribute("class")}}function xi(t){if(t){if("object"==typeof t){var e={};return!1!==t.css&&T(e,Ci(t.name||"v")),T(e,t),e}return"string"==typeof t?Ci(t):void 0}}var Ci=b((function(t){return{enterClass:"".concat(t,"-enter"),enterToClass:"".concat(t,"-enter-to"),enterActiveClass:"".concat(t,"-enter-active"),leaveClass:"".concat(t,"-leave"),leaveToClass:"".concat(t,"-leave-to"),leaveActiveClass:"".concat(t,"-leave-active")}})),ki=J&&!Z,Si="transition",Oi="animation",Ti="transition",Ai="transitionend",ji="animation",Ei="animationend";ki&&(void 0===window.ontransitionend&&void 0!==window.onwebkittransitionend&&(Ti="WebkitTransition",Ai="webkitTransitionEnd"),void 0===window.onanimationend&&void 0!==window.onwebkitanimationend&&(ji="WebkitAnimation",Ei="webkitAnimationEnd"));var Ni=J?window.requestAnimationFrame?window.requestAnimationFrame.bind(window):setTimeout:function(t){return t()};function Pi(t){Ni((function(){Ni(t)}))}function Di(t,e){var n=t._transitionClasses||(t._transitionClasses=[]);n.indexOf(e)<0&&(n.push(e),$i(t,e))}function Mi(t,e){t._transitionClasses&&g(t._transitionClasses,e),wi(t,e)}function Ii(t,e,n){var r=Ri(t,e),o=r.type,i=r.timeout,a=r.propCount;if(!o)return n();var s=o===Si?Ai:Ei,c=0,u=function(){t.removeEventListener(s,l),n()},l=function(e){e.target===t&&++c>=a&&u()};setTimeout((function(){c0&&(n=Si,l=a,f=i.length):e===Oi?u>0&&(n=Oi,l=u,f=c.length):f=(n=(l=Math.max(a,u))>0?a>u?Si:Oi:null)?n===Si?i.length:c.length:0,{type:n,timeout:l,propCount:f,hasTransform:n===Si&&Li.test(r[Ti+"Property"])}}function Fi(t,e){for(;t.length1}function Ki(t,e){!0!==e.data.show&&Bi(e)}var Ji=function(t){var a,s,c={},u=t.modules,l=t.nodeOps;for(a=0;av?b(t,n(o[g+1])?null:o[g+1].elm,o,p,g,i):p>g&&w(e,f,v)}(f,h,m,i,u):r(m)?(r(t.text)&&l.setTextContent(f,""),b(f,null,m,0,m.length-1,i)):r(h)?w(h,0,h.length-1):r(t.text)&&l.setTextContent(f,""):t.text!==e.text&&l.setTextContent(f,e.text),r(v)&&r(p=v.hook)&&r(p=p.postpatch)&&p(t,e)}}}function S(t,e,n){if(o(n)&&r(t.parent))t.parent.data.pendingInsert=e;else for(var i=0;i-1,a.selected!==i&&(a.selected=i);else if(P(Xi(a),r))return void(t.selectedIndex!==s&&(t.selectedIndex=s));o||(t.selectedIndex=-1)}}function Gi(t,e){return e.every((function(e){return!P(e,t)}))}function Xi(t){return"_value"in t?t._value:t.value}function Yi(t){t.target.composing=!0}function Qi(t){t.target.composing&&(t.target.composing=!1,ta(t.target,"input"))}function ta(t,e){var n=document.createEvent("HTMLEvents");n.initEvent(e,!0,!0),t.dispatchEvent(n)}function ea(t){return!t.componentInstance||t.data&&t.data.transition?t:ea(t.componentInstance._vnode)}var na={bind:function(t,e,n){var r=e.value,o=(n=ea(n)).data&&n.data.transition,i=t.__vOriginalDisplay="none"===t.style.display?"":t.style.display;r&&o?(n.data.show=!0,Bi(n,(function(){t.style.display=i}))):t.style.display=r?i:"none"},update:function(t,e,n){var r=e.value;!r!=!e.oldValue&&((n=ea(n)).data&&n.data.transition?(n.data.show=!0,r?Bi(n,(function(){t.style.display=t.__vOriginalDisplay})):Ui(n,(function(){t.style.display="none"}))):t.style.display=r?t.__vOriginalDisplay:"none")},unbind:function(t,e,n,r,o){o||(t.style.display=t.__vOriginalDisplay)}},ra={model:qi,show:na},oa={name:String,appear:Boolean,css:Boolean,mode:String,type:String,enterClass:String,leaveClass:String,enterToClass:String,leaveToClass:String,enterActiveClass:String,leaveActiveClass:String,appearClass:String,appearActiveClass:String,appearToClass:String,duration:[Number,String,Object]};function ia(t){var e=t&&t.componentOptions;return e&&e.Ctor.options.abstract?ia(Ee(e.children)):t}function aa(t){var e={},n=t.$options;for(var r in n.propsData)e[r]=t[r];var o=n._parentListeners;for(var r in o)e[w(r)]=o[r];return e}function sa(t,e){if(/\d-keep-alive$/.test(e.tag))return t("keep-alive",{props:e.componentOptions.propsData})}var ca=function(t){return t.tag||_e(t)},ua=function(t){return"show"===t.name},la={name:"transition",props:oa,abstract:!0,render:function(t){var e=this,n=this.$slots.default;if(n&&(n=n.filter(ca)).length){var r=this.mode,o=n[0];if(function(t){for(;t=t.parent;)if(t.data.transition)return!0}(this.$vnode))return o;var a=ia(o);if(!a)return o;if(this._leaving)return sa(t,o);var s="__transition-".concat(this._uid,"-");a.key=null==a.key?a.isComment?s+"comment":s+a.tag:i(a.key)?0===String(a.key).indexOf(s)?a.key:s+a.key:a.key;var c=(a.data||(a.data={})).transition=aa(this),u=this._vnode,l=ia(u);if(a.data.directives&&a.data.directives.some(ua)&&(a.data.show=!0),l&&l.data&&!function(t,e){return e.key===t.key&&e.tag===t.tag}(a,l)&&!_e(l)&&(!l.componentInstance||!l.componentInstance._vnode.isComment)){var f=l.data.transition=T({},c);if("out-in"===r)return this._leaving=!0,Zt(f,"afterLeave",(function(){e._leaving=!1,e.$forceUpdate()})),sa(t,o);if("in-out"===r){if(_e(a))return u;var d,p=function(){d()};Zt(c,"afterEnter",p),Zt(c,"enterCancelled",p),Zt(f,"delayLeave",(function(t){d=t}))}}return o}}},fa=T({tag:String,moveClass:String},oa);delete fa.mode;var da={props:fa,beforeMount:function(){var t=this,e=this._update;this._update=function(n,r){var o=Le(t);t.__patch__(t._vnode,t.kept,!1,!0),t._vnode=t.kept,o(),e.call(t,n,r)}},render:function(t){for(var e=this.tag||this.$vnode.data.tag||"span",n=Object.create(null),r=this.prevChildren=this.children,o=this.$slots.default||[],i=this.children=[],a=aa(this),s=0;s-1?Yr[t]=e.constructor===window.HTMLUnknownElement||e.constructor===window.HTMLElement:Yr[t]=/HTMLUnknownElement/.test(e.toString())},T(Cr.options.directives,ra),T(Cr.options.components,ma),Cr.prototype.__patch__=J?Ji:j,Cr.prototype.$mount=function(t,e){return function(t,e,n){var r;t.$el=e,t.$options.render||(t.$options.render=ft),Be(t,"beforeMount"),r=function(){t._update(t._render(),n)},new Vn(t,r,j,{before:function(){t._isMounted&&!t._isDestroyed&&Be(t,"beforeUpdate")}},!0),n=!1;var o=t._preWatchers;if(o)for(var i=0;i\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/,Ta=/^\s*((?:v-[\w-]+:|@|:|#)\[[^=]+?\][^\s"'<>\/=]*)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/,Aa="[a-zA-Z_][\\-\\.0-9_a-zA-Z".concat(B.source,"]*"),ja="((?:".concat(Aa,"\\:)?").concat(Aa,")"),Ea=new RegExp("^<".concat(ja)),Na=/^\s*(\/?)>/,Pa=new RegExp("^<\\/".concat(ja,"[^>]*>")),Da=/^]+>/i,Ma=/^",""":'"',"&":"&"," ":"\n"," ":"\t","'":"'"},Ha=/&(?:lt|gt|quot|amp|#39);/g,Ba=/&(?:lt|gt|quot|amp|#39|#10|#9);/g,Ua=v("pre,textarea",!0),za=function(t,e){return t&&Ua(t)&&"\n"===e[0]};function Va(t,e){var n=e?Ba:Ha;return t.replace(n,(function(t){return Fa[t]}))}function Ka(t,e){for(var n,r,o=[],i=e.expectHTML,a=e.isUnaryTag||E,s=e.canBeLeftOpenTag||E,c=0,u=function(){if(n=t,r&&La(r)){var u=0,d=r.toLowerCase(),p=Ra[d]||(Ra[d]=new RegExp("([\\s\\S]*?)(]*>)","i"));w=t.replace(p,(function(t,n,r){return u=r.length,La(d)||"noscript"===d||(n=n.replace(//g,"$1").replace(//g,"$1")),za(d,n)&&(n=n.slice(1)),e.chars&&e.chars(n),""}));c+=t.length-w.length,t=w,f(d,c-u,c)}else{var v=t.indexOf("<");if(0===v){if(Ma.test(t)){var h=t.indexOf("--\x3e");if(h>=0)return e.shouldKeepComment&&e.comment&&e.comment(t.substring(4,h),c,c+h+3),l(h+3),"continue"}if(Ia.test(t)){var m=t.indexOf("]>");if(m>=0)return l(m+2),"continue"}var g=t.match(Da);if(g)return l(g[0].length),"continue";var y=t.match(Pa);if(y){var _=c;return l(y[0].length),f(y[1],_,c),"continue"}var b=function(){var e=t.match(Ea);if(e){var n={tagName:e[1],attrs:[],start:c};l(e[0].length);for(var r=void 0,o=void 0;!(r=t.match(Na))&&(o=t.match(Ta)||t.match(Oa));)o.start=c,l(o[0].length),o.end=c,n.attrs.push(o);if(r)return n.unarySlash=r[1],l(r[0].length),n.end=c,n}}();if(b)return function(t){var n=t.tagName,c=t.unarySlash;i&&("p"===r&&Sa(n)&&f(r),s(n)&&r===n&&f(n));for(var u=a(n)||!!c,l=t.attrs.length,d=new Array(l),p=0;p=0){for(w=t.slice(v);!(Pa.test(w)||Ea.test(w)||Ma.test(w)||Ia.test(w)||(x=w.indexOf("<",1))<0);)v+=x,w=t.slice(v);$=t.substring(0,v)}v<0&&($=t),$&&l($.length),e.chars&&$&&e.chars($,c-$.length,c)}if(t===n)return e.chars&&e.chars(t),"break"};t;){if("break"===u())break}function l(e){c+=e,t=t.substring(e)}function f(t,n,i){var a,s;if(null==n&&(n=c),null==i&&(i=c),t)for(s=t.toLowerCase(),a=o.length-1;a>=0&&o[a].lowerCasedTag!==s;a--);else a=0;if(a>=0){for(var u=o.length-1;u>=a;u--)e.end&&e.end(o[u].tag,n,i);o.length=a,r=a&&o[a-1].tag}else"br"===s?e.start&&e.start(t,[],!0,n,i):"p"===s&&(e.start&&e.start(t,[],!1,n,i),e.end&&e.end(t,n,i))}f()}var Ja,qa,Wa,Za,Ga,Xa,Ya,Qa,ts=/^@|^v-on:/,es=/^v-|^@|^:|^#/,ns=/([\s\S]*?)\s+(?:in|of)\s+([\s\S]*)/,rs=/,([^,\}\]]*)(?:,([^,\}\]]*))?$/,os=/^\(|\)$/g,is=/^\[.*\]$/,as=/:(.*)$/,ss=/^:|^\.|^v-bind:/,cs=/\.[^.\]]+(?=[^\]]*$)/g,us=/^v-slot(:|$)|^#/,ls=/[\r\n]/,fs=/[ \f\t\r\n]+/g,ds=b(xa),ps="_empty_";function vs(t,e,n){return{type:1,tag:t,attrsList:e,attrsMap:$s(e),rawAttrsMap:{},parent:n,children:[]}}function hs(t,e){Ja=e.warn||No,Xa=e.isPreTag||E,Ya=e.mustUseProp||E,Qa=e.getTagNamespace||E,e.isReservedTag,Wa=Po(e.modules,"transformNode"),Za=Po(e.modules,"preTransformNode"),Ga=Po(e.modules,"postTransformNode"),qa=e.delimiters;var n,r,o=[],i=!1!==e.preserveWhitespace,a=e.whitespace,s=!1,c=!1;function u(t){if(l(t),s||t.processed||(t=ms(t,e)),o.length||t===n||n.if&&(t.elseif||t.else)&&ys(n,{exp:t.elseif,block:t}),r&&!t.forbidden)if(t.elseif||t.else)a=t,u=function(t){for(var e=t.length;e--;){if(1===t[e].type)return t[e];t.pop()}}(r.children),u&&u.if&&ys(u,{exp:a.elseif,block:a});else{if(t.slotScope){var i=t.slotTarget||'"default"';(r.scopedSlots||(r.scopedSlots={}))[i]=t}r.children.push(t),t.parent=r}var a,u;t.children=t.children.filter((function(t){return!t.slotScope})),l(t),t.pre&&(s=!1),Xa(t.tag)&&(c=!1);for(var f=0;fc&&(s.push(i=t.slice(c,o)),a.push(JSON.stringify(i)));var u=jo(r[1].trim());a.push("_s(".concat(u,")")),s.push({"@binding":u}),c=o+r[0].length}return c-1")+("true"===i?":(".concat(e,")"):":_q(".concat(e,",").concat(i,")"))),Fo(t,"change","var $$a=".concat(e,",")+"$$el=$event.target,"+"$$c=$$el.checked?(".concat(i,"):(").concat(a,");")+"if(Array.isArray($$a)){"+"var $$v=".concat(r?"_n("+o+")":o,",")+"$$i=_i($$a,$$v);"+"if($$el.checked){$$i<0&&(".concat(Ko(e,"$$a.concat([$$v])"),")}")+"else{$$i>-1&&(".concat(Ko(e,"$$a.slice(0,$$i).concat($$a.slice($$i+1))"),")}")+"}else{".concat(Ko(e,"$$c"),"}"),null,!0)}(t,r,o);else if("input"===i&&"radio"===a)!function(t,e,n){var r=n&&n.number,o=Ho(t,"value")||"null";o=r?"_n(".concat(o,")"):o,Do(t,"checked","_q(".concat(e,",").concat(o,")")),Fo(t,"change",Ko(e,o),null,!0)}(t,r,o);else if("input"===i||"textarea"===i)!function(t,e,n){var r=t.attrsMap.type,o=n||{},i=o.lazy,a=o.number,s=o.trim,c=!i&&"range"!==r,u=i?"change":"range"===r?Yo:"input",l="$event.target.value";s&&(l="$event.target.value.trim()");a&&(l="_n(".concat(l,")"));var f=Ko(e,l);c&&(f="if($event.target.composing)return;".concat(f));Do(t,"value","(".concat(e,")")),Fo(t,u,f,null,!0),(s||a)&&Fo(t,"blur","$forceUpdate()")}(t,r,o);else if(!H.isReservedTag(i))return Vo(t,r,o),!1;return!0},text:function(t,e){e.value&&Do(t,"textContent","_s(".concat(e.value,")"),e)},html:function(t,e){e.value&&Do(t,"innerHTML","_s(".concat(e.value,")"),e)}},As={expectHTML:!0,modules:ks,directives:Ts,isPreTag:function(t){return"pre"===t},isUnaryTag:Ca,mustUseProp:Mr,canBeLeftOpenTag:ka,isReservedTag:Gr,getTagNamespace:Xr,staticKeys:function(t){return t.reduce((function(t,e){return t.concat(e.staticKeys||[])}),[]).join(",")}(ks)},js=b((function(t){return v("type,tag,attrsList,attrsMap,plain,parent,children,attrs,start,end,rawAttrsMap"+(t?","+t:""))}));function Es(t,e){t&&(Ss=js(e.staticKeys||""),Os=e.isReservedTag||E,Ns(t),Ps(t,!1))}function Ns(t){if(t.static=function(t){if(2===t.type)return!1;if(3===t.type)return!0;return!(!t.pre&&(t.hasBindings||t.if||t.for||h(t.tag)||!Os(t.tag)||function(t){for(;t.parent;){if("template"!==(t=t.parent).tag)return!1;if(t.for)return!0}return!1}(t)||!Object.keys(t).every(Ss)))}(t),1===t.type){if(!Os(t.tag)&&"slot"!==t.tag&&null==t.attrsMap["inline-template"])return;for(var e=0,n=t.children.length;e|^function(?:\s+[\w$]+)?\s*\(/,Ms=/\([^)]*?\);*$/,Is=/^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\['[^']*?']|\["[^"]*?"]|\[\d+]|\[[A-Za-z_$][\w$]*])*$/,Ls={esc:27,tab:9,enter:13,space:32,up:38,left:37,right:39,down:40,delete:[8,46]},Rs={esc:["Esc","Escape"],tab:"Tab",enter:"Enter",space:[" ","Spacebar"],up:["Up","ArrowUp"],left:["Left","ArrowLeft"],right:["Right","ArrowRight"],down:["Down","ArrowDown"],delete:["Backspace","Delete","Del"]},Fs=function(t){return"if(".concat(t,")return null;")},Hs={stop:"$event.stopPropagation();",prevent:"$event.preventDefault();",self:Fs("$event.target !== $event.currentTarget"),ctrl:Fs("!$event.ctrlKey"),shift:Fs("!$event.shiftKey"),alt:Fs("!$event.altKey"),meta:Fs("!$event.metaKey"),left:Fs("'button' in $event && $event.button !== 0"),middle:Fs("'button' in $event && $event.button !== 1"),right:Fs("'button' in $event && $event.button !== 2")};function Bs(t,e){var n=e?"nativeOn:":"on:",r="",o="";for(var i in t){var a=Us(t[i]);t[i]&&t[i].dynamic?o+="".concat(i,",").concat(a,","):r+='"'.concat(i,'":').concat(a,",")}return r="{".concat(r.slice(0,-1),"}"),o?n+"_d(".concat(r,",[").concat(o.slice(0,-1),"])"):n+r}function Us(t){if(!t)return"function(){}";if(Array.isArray(t))return"[".concat(t.map((function(t){return Us(t)})).join(","),"]");var e=Is.test(t.value),n=Ds.test(t.value),r=Is.test(t.value.replace(Ms,""));if(t.modifiers){var o="",i="",a=[],s=function(e){if(Hs[e])i+=Hs[e],Ls[e]&&a.push(e);else if("exact"===e){var n=t.modifiers;i+=Fs(["ctrl","shift","alt","meta"].filter((function(t){return!n[t]})).map((function(t){return"$event.".concat(t,"Key")})).join("||"))}else a.push(e)};for(var c in t.modifiers)s(c);a.length&&(o+=function(t){return"if(!$event.type.indexOf('key')&&"+"".concat(t.map(zs).join("&&"),")return null;")}(a)),i&&(o+=i);var u=e?"return ".concat(t.value,".apply(null, arguments)"):n?"return (".concat(t.value,").apply(null, arguments)"):r?"return ".concat(t.value):t.value;return"function($event){".concat(o).concat(u,"}")}return e||n?t.value:"function($event){".concat(r?"return ".concat(t.value):t.value,"}")}function zs(t){var e=parseInt(t,10);if(e)return"$event.keyCode!==".concat(e);var n=Ls[t],r=Rs[t];return"_k($event.keyCode,"+"".concat(JSON.stringify(t),",")+"".concat(JSON.stringify(n),",")+"$event.key,"+"".concat(JSON.stringify(r))+")"}var Vs={on:function(t,e){t.wrapListeners=function(t){return"_g(".concat(t,",").concat(e.value,")")}},bind:function(t,e){t.wrapData=function(n){return"_b(".concat(n,",'").concat(t.tag,"',").concat(e.value,",").concat(e.modifiers&&e.modifiers.prop?"true":"false").concat(e.modifiers&&e.modifiers.sync?",true":"",")")}},cloak:j},Ks=function(t){this.options=t,this.warn=t.warn||No,this.transforms=Po(t.modules,"transformCode"),this.dataGenFns=Po(t.modules,"genData"),this.directives=T(T({},Vs),t.directives);var e=t.isReservedTag||E;this.maybeComponent=function(t){return!!t.component||!e(t.tag)},this.onceId=0,this.staticRenderFns=[],this.pre=!1};function Js(t,e){var n=new Ks(e),r=t?"script"===t.tag?"null":qs(t,n):'_c("div")';return{render:"with(this){return ".concat(r,"}"),staticRenderFns:n.staticRenderFns}}function qs(t,e){if(t.parent&&(t.pre=t.pre||t.parent.pre),t.staticRoot&&!t.staticProcessed)return Ws(t,e);if(t.once&&!t.onceProcessed)return Zs(t,e);if(t.for&&!t.forProcessed)return Ys(t,e);if(t.if&&!t.ifProcessed)return Gs(t,e);if("template"!==t.tag||t.slotTarget||e.pre){if("slot"===t.tag)return function(t,e){var n=t.slotName||'"default"',r=nc(t,e),o="_t(".concat(n).concat(r?",function(){return ".concat(r,"}"):""),i=t.attrs||t.dynamicAttrs?ic((t.attrs||[]).concat(t.dynamicAttrs||[]).map((function(t){return{name:w(t.name),value:t.value,dynamic:t.dynamic}}))):null,a=t.attrsMap["v-bind"];!i&&!a||r||(o+=",null");i&&(o+=",".concat(i));a&&(o+="".concat(i?"":",null",",").concat(a));return o+")"}(t,e);var n=void 0;if(t.component)n=function(t,e,n){var r=e.inlineTemplate?null:nc(e,n,!0);return"_c(".concat(t,",").concat(Qs(e,n)).concat(r?",".concat(r):"",")")}(t.component,t,e);else{var r=void 0,o=e.maybeComponent(t);(!t.plain||t.pre&&o)&&(r=Qs(t,e));var i=void 0,a=e.options.bindings;o&&a&&!1!==a.__isScriptSetup&&(i=function(t,e){var n=w(e),r=x(n),o=function(o){return t[e]===o?e:t[n]===o?n:t[r]===o?r:void 0},i=o("setup-const")||o("setup-reactive-const");if(i)return i;var a=o("setup-let")||o("setup-ref")||o("setup-maybe-ref");if(a)return a}(a,t.tag)),i||(i="'".concat(t.tag,"'"));var s=t.inlineTemplate?null:nc(t,e,!0);n="_c(".concat(i).concat(r?",".concat(r):"").concat(s?",".concat(s):"",")")}for(var c=0;c>>0}(a)):"",")")}(t,t.scopedSlots,e),",")),t.model&&(n+="model:{value:".concat(t.model.value,",callback:").concat(t.model.callback,",expression:").concat(t.model.expression,"},")),t.inlineTemplate){var i=function(t,e){var n=t.children[0];if(n&&1===n.type){var r=Js(n,e.options);return"inlineTemplate:{render:function(){".concat(r.render,"},staticRenderFns:[").concat(r.staticRenderFns.map((function(t){return"function(){".concat(t,"}")})).join(","),"]}")}}(t,e);i&&(n+="".concat(i,","))}return n=n.replace(/,$/,"")+"}",t.dynamicAttrs&&(n="_b(".concat(n,',"').concat(t.tag,'",').concat(ic(t.dynamicAttrs),")")),t.wrapData&&(n=t.wrapData(n)),t.wrapListeners&&(n=t.wrapListeners(n)),n}function tc(t){return 1===t.type&&("slot"===t.tag||t.children.some(tc))}function ec(t,e){var n=t.attrsMap["slot-scope"];if(t.if&&!t.ifProcessed&&!n)return Gs(t,e,ec,"null");if(t.for&&!t.forProcessed)return Ys(t,e,ec);var r=t.slotScope===ps?"":String(t.slotScope),o="function(".concat(r,"){")+"return ".concat("template"===t.tag?t.if&&n?"(".concat(t.if,")?").concat(nc(t,e)||"undefined",":undefined"):nc(t,e)||"undefined":qs(t,e),"}"),i=r?"":",proxy:true";return"{key:".concat(t.slotTarget||'"default"',",fn:").concat(o).concat(i,"}")}function nc(t,e,n,r,o){var i=t.children;if(i.length){var a=i[0];if(1===i.length&&a.for&&"template"!==a.tag&&"slot"!==a.tag){var s=n?e.maybeComponent(a)?",1":",0":"";return"".concat((r||qs)(a,e)).concat(s)}var c=n?function(t,e){for(var n=0,r=0;r':'

      ',lc.innerHTML.indexOf(" ")>0}var vc=!!J&&pc(!1),hc=!!J&&pc(!0),mc=b((function(t){var e=to(t);return e&&e.innerHTML})),gc=Cr.prototype.$mount;return Cr.prototype.$mount=function(t,e){if((t=t&&to(t))===document.body||t===document.documentElement)return this;var n=this.$options;if(!n.render){var r=n.template;if(r)if("string"==typeof r)"#"===r.charAt(0)&&(r=mc(r));else{if(!r.nodeType)return this;r=r.innerHTML}else t&&(r=function(t){if(t.outerHTML)return t.outerHTML;var e=document.createElement("div");return e.appendChild(t.cloneNode(!0)),e.innerHTML}(t));if(r){var o=dc(r,{outputSourceRange:!1,shouldDecodeNewlines:vc,shouldDecodeNewlinesForHref:hc,delimiters:n.delimiters,comments:n.comments},this),i=o.render,a=o.staticRenderFns;n.render=i,n.staticRenderFns=a}}return gc.call(this,t,e)},Cr.compile=dc,T(Cr,Fn),Cr.effect=function(t,e){var n=new Vn(ct,t,j,{sync:!0});e&&(n.update=function(){e((function(){return n.run()}))})},Cr})); \ No newline at end of file diff --git a/kirby/panel/dist/js/vue.min.js b/kirby/panel/dist/js/vue.min.js new file mode 100644 index 0000000..5b49427 --- /dev/null +++ b/kirby/panel/dist/js/vue.min.js @@ -0,0 +1,11 @@ +/*! + * Vue.js v2.7.16 + * (c) 2014-2023 Evan You + * Released under the MIT License. + */ +/*! + * Vue.js v2.7.16 + * (c) 2014-2023 Evan You + * Released under the MIT License. + */ +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).Vue=e()}(this,(function(){"use strict";var t=Object.freeze({}),e=Array.isArray;function n(t){return null==t}function r(t){return null!=t}function o(t){return!0===t}function i(t){return"string"==typeof t||"number"==typeof t||"symbol"==typeof t||"boolean"==typeof t}function a(t){return"function"==typeof t}function s(t){return null!==t&&"object"==typeof t}var c=Object.prototype.toString;function u(t){return"[object Object]"===c.call(t)}function l(t){var e=parseFloat(String(t));return e>=0&&Math.floor(e)===e&&isFinite(t)}function f(t){return r(t)&&"function"==typeof t.then&&"function"==typeof t.catch}function d(t){return null==t?"":Array.isArray(t)||u(t)&&t.toString===c?JSON.stringify(t,p,2):String(t)}function p(t,e){return e&&e.__v_isRef?e.value:e}function v(t){var e=parseFloat(t);return isNaN(e)?t:e}function h(t,e){for(var n=Object.create(null),r=t.split(","),o=0;o-1)return t.splice(r,1)}}var _=Object.prototype.hasOwnProperty;function b(t,e){return _.call(t,e)}function $(t){var e=Object.create(null);return function(n){return e[n]||(e[n]=t(n))}}var w=/-(\w)/g,x=$((function(t){return t.replace(w,(function(t,e){return e?e.toUpperCase():""}))})),C=$((function(t){return t.charAt(0).toUpperCase()+t.slice(1)})),k=/\B([A-Z])/g,S=$((function(t){return t.replace(k,"-$1").toLowerCase()}));var O=Function.prototype.bind?function(t,e){return t.bind(e)}:function(t,e){function n(n){var r=arguments.length;return r?r>1?t.apply(e,arguments):t.call(e,n):t.call(e)}return n._length=t.length,n};function T(t,e){e=e||0;for(var n=t.length-e,r=new Array(n);n--;)r[n]=t[n+e];return r}function A(t,e){for(var n in e)t[n]=e[n];return t}function j(t){for(var e={},n=0;n0,X=W&&W.indexOf("edge/")>0;W&&W.indexOf("android");var Y=W&&/iphone|ipad|ipod|ios/.test(W);W&&/chrome\/\d+/.test(W),W&&/phantomjs/.test(W);var Q,tt=W&&W.match(/firefox\/(\d+)/),et={}.watch,nt=!1;if(q)try{var rt={};Object.defineProperty(rt,"passive",{get:function(){nt=!0}}),window.addEventListener("test-passive",null,rt)}catch(t){}var ot=function(){return void 0===Q&&(Q=!q&&"undefined"!=typeof global&&(global.process&&"server"===global.process.env.VUE_ENV)),Q},it=q&&window.__VUE_DEVTOOLS_GLOBAL_HOOK__;function at(t){return"function"==typeof t&&/native code/.test(t.toString())}var st,ct="undefined"!=typeof Symbol&&at(Symbol)&&"undefined"!=typeof Reflect&&at(Reflect.ownKeys);st="undefined"!=typeof Set&&at(Set)?Set:function(){function t(){this.set=Object.create(null)}return t.prototype.has=function(t){return!0===this.set[t]},t.prototype.add=function(t){this.set[t]=!0},t.prototype.clear=function(){this.set=Object.create(null)},t}();var ut=null;function lt(t){void 0===t&&(t=null),t||ut&&ut._scope.off(),ut=t,t&&t._scope.on()}var ft=function(){function t(t,e,n,r,o,i,a,s){this.tag=t,this.data=e,this.children=n,this.text=r,this.elm=o,this.ns=void 0,this.context=i,this.fnContext=void 0,this.fnOptions=void 0,this.fnScopeId=void 0,this.key=e&&e.key,this.componentOptions=a,this.componentInstance=void 0,this.parent=void 0,this.raw=!1,this.isStatic=!1,this.isRootInsert=!0,this.isComment=!1,this.isCloned=!1,this.isOnce=!1,this.asyncFactory=s,this.asyncMeta=void 0,this.isAsyncPlaceholder=!1}return Object.defineProperty(t.prototype,"child",{get:function(){return this.componentInstance},enumerable:!1,configurable:!0}),t}(),dt=function(t){void 0===t&&(t="");var e=new ft;return e.text=t,e.isComment=!0,e};function pt(t){return new ft(void 0,void 0,void 0,String(t))}function vt(t){var e=new ft(t.tag,t.data,t.children&&t.children.slice(),t.text,t.elm,t.context,t.componentOptions,t.asyncFactory);return e.ns=t.ns,e.isStatic=t.isStatic,e.key=t.key,e.isComment=t.isComment,e.fnContext=t.fnContext,e.fnOptions=t.fnOptions,e.fnScopeId=t.fnScopeId,e.asyncMeta=t.asyncMeta,e.isCloned=!0,e}"function"==typeof SuppressedError&&SuppressedError;var ht=0,mt=[],gt=function(){for(var t=0;t0&&(ne((c=re(c,"".concat(a||"","_").concat(s)))[0])&&ne(l)&&(f[u]=pt(l.text+c[0].text),c.shift()),f.push.apply(f,c)):i(c)?ne(l)?f[u]=pt(l.text+c):""!==c&&f.push(pt(c)):ne(c)&&ne(l)?f[u]=pt(l.text+c.text):(o(t._isVList)&&r(c.tag)&&n(c.key)&&r(a)&&(c.key="__vlist".concat(a,"_").concat(s,"__")),f.push(c)));return f}var oe=1,ie=2;function ae(t,n,c,u,l,f){return(e(c)||i(c))&&(l=u,u=c,c=void 0),o(f)&&(l=ie),function(t,n,o,i,c){if(r(o)&&r(o.__ob__))return dt();r(o)&&r(o.is)&&(n=o.is);if(!n)return dt();e(i)&&a(i[0])&&((o=o||{}).scopedSlots={default:i[0]},i.length=0);c===ie?i=ee(i):c===oe&&(i=function(t){for(var n=0;n0,s=n?!!n.$stable:!a,c=n&&n.$key;if(n){if(n._normalized)return n._normalized;if(s&&o&&o!==t&&c===o.$key&&!a&&!o.$hasNormal)return o;for(var u in i={},n)n[u]&&"$"!==u[0]&&(i[u]=Oe(e,r,u,n[u]))}else i={};for(var l in r)l in i||(i[l]=Te(r,l));return n&&Object.isExtensible(n)&&(n._normalized=i),V(i,"$stable",s),V(i,"$key",c),V(i,"$hasNormal",a),i}function Oe(t,n,r,o){var i=function(){var n=ut;lt(t);var r=arguments.length?o.apply(null,arguments):o({}),i=(r=r&&"object"==typeof r&&!e(r)?[r]:ee(r))&&r[0];return lt(n),r&&(!i||1===r.length&&i.isComment&&!ke(i))?void 0:r};return o.proxy&&Object.defineProperty(n,r,{get:i,enumerable:!0,configurable:!0}),i}function Te(t,e){return function(){return t[e]}}function Ae(e){return{get attrs(){if(!e._attrsProxy){var n=e._attrsProxy={};V(n,"_v_attr_proxy",!0),je(n,e.$attrs,t,e,"$attrs")}return e._attrsProxy},get listeners(){e._listenersProxy||je(e._listenersProxy={},e.$listeners,t,e,"$listeners");return e._listenersProxy},get slots(){return function(t){t._slotsProxy||Ne(t._slotsProxy={},t.$scopedSlots);return t._slotsProxy}(e)},emit:O(e.$emit,e),expose:function(t){t&&Object.keys(t).forEach((function(n){return zt(e,t,n)}))}}}function je(t,e,n,r,o){var i=!1;for(var a in e)a in t?e[a]!==n[a]&&(i=!0):(i=!0,Ee(t,a,r,o));for(var a in t)a in e||(i=!0,delete t[a]);return i}function Ee(t,e,n,r){Object.defineProperty(t,e,{enumerable:!0,configurable:!0,get:function(){return n[r][e]}})}function Ne(t,e){for(var n in e)t[n]=e[n];for(var n in t)n in e||delete t[n]}function Pe(){var t=ut;return t._setupContext||(t._setupContext=Ae(t))}var De,Me,Ie=null;function Le(t,e){return(t.__esModule||ct&&"Module"===t[Symbol.toStringTag])&&(t=t.default),s(t)?e.extend(t):t}function Re(t){if(e(t))for(var n=0;ndocument.createEvent("Event").timeStamp&&(on=function(){return an.now()})}var sn=function(t,e){if(t.post){if(!e.post)return 1}else if(e.post)return-1;return t.id-e.id};function cn(){var t,e;for(rn=on(),en=!0,Xe.sort(sn),nn=0;nnnn&&Xe[n].id>t.id;)n--;Xe.splice(n+1,0,t)}else Xe.push(t);tn||(tn=!0,En(cn))}}var ln="watcher",fn="".concat(ln," callback"),dn="".concat(ln," getter"),pn="".concat(ln," cleanup");function vn(t,e){return mn(t,null,{flush:"post"})}var hn={};function mn(n,r,o){var i=void 0===o?t:o,s=i.immediate,c=i.deep,u=i.flush,l=void 0===u?"pre":u;i.onTrack,i.onTrigger;var f,d,p=ut,v=function(t,e,n){void 0===n&&(n=null);var r=_n(t,null,n,p,e);return c&&r&&r.__ob__&&r.__ob__.dep.depend(),r},h=!1,m=!1;if(Bt(n)?(f=function(){return n.value},h=Rt(n)):Lt(n)?(f=function(){return n.__ob__.dep.depend(),n},c=!0):e(n)?(m=!0,h=n.some((function(t){return Lt(t)||Rt(t)})),f=function(){return n.map((function(t){return Bt(t)?t.value:Lt(t)?(t.__ob__.dep.depend(),Wn(t)):a(t)?v(t,dn):void 0}))}):f=a(n)?r?function(){return v(n,dn)}:function(){if(!p||!p._isDestroyed)return d&&d(),v(n,ln,[y])}:E,r&&c){var g=f;f=function(){return Wn(g())}}var y=function(t){d=_.onStop=function(){v(t,pn)}};if(ot())return y=E,r?s&&v(r,fn,[f(),m?[]:void 0,y]):f(),E;var _=new Xn(ut,f,E,{lazy:!0});_.noRecurse=!r;var b=m?[]:hn;return _.run=function(){if(_.active)if(r){var t=_.get();(c||h||(m?t.some((function(t,e){return L(t,b[e])})):L(t,b)))&&(d&&d(),v(r,fn,[t,b===hn?void 0:b,y]),b=t)}else _.get()},"sync"===l?_.update=_.run:"post"===l?(_.post=!0,_.update=function(){return un(_)}):_.update=function(){if(p&&p===ut&&!p._isMounted){var t=p._preWatchers||(p._preWatchers=[]);t.indexOf(_)<0&&t.push(_)}else un(_)},r?s?_.run():b=_.get():"post"===l&&p?p.$once("hook:mounted",(function(){return _.get()})):_.get(),function(){_.teardown()}}function gn(t){var e=t._provided,n=t.$parent&&t.$parent._provided;return n===e?t._provided=Object.create(n):e}function yn(t,e,n){bt();try{if(e)for(var r=e;r=r.$parent;){var o=r.$options.errorCaptured;if(o)for(var i=0;i1)return n&&a(e)?e.call(r):e}},h:function(t,e,n){return ae(ut,t,e,n,2,!0)},getCurrentInstance:function(){return ut&&{proxy:ut}},useSlots:function(){return Pe().slots},useAttrs:function(){return Pe().attrs},useListeners:function(){return Pe().listeners},mergeDefaults:function(t,n){var r=e(t)?t.reduce((function(t,e){return t[e]={},t}),{}):t;for(var o in n){var i=r[o];i?e(i)||a(i)?r[o]={type:i,default:n[o]}:i.default=n[o]:null===i&&(r[o]={default:n[o]})}return r},nextTick:En,set:Nt,del:Pt,useCssModule:function(e){return t},useCssVars:function(t){if(q){var e=ut;e&&vn((function(){var n=e.$el,r=t(e,e._setupProxy);if(n&&1===n.nodeType){var o=n.style;for(var i in r)o.setProperty("--".concat(i),r[i])}}))}},defineAsyncComponent:function(t){a(t)&&(t={loader:t});var e=t.loader,n=t.loadingComponent,r=t.errorComponent,o=t.delay,i=void 0===o?200:o,s=t.timeout;t.suspensible;var c=t.onError,u=null,l=0,f=function(){var t;return u||(t=u=e().catch((function(t){if(t=t instanceof Error?t:new Error(String(t)),c)return new Promise((function(e,n){c(t,(function(){return e((l++,u=null,f()))}),(function(){return n(t)}),l+1)}));throw t})).then((function(e){return t!==u&&u?u:(e&&(e.__esModule||"Module"===e[Symbol.toStringTag])&&(e=e.default),e)})))};return function(){return{component:f(),delay:i,timeout:s,error:r,loading:n}}},onBeforeMount:Pn,onMounted:Dn,onBeforeUpdate:Mn,onUpdated:In,onBeforeUnmount:Ln,onUnmounted:Rn,onActivated:Fn,onDeactivated:Hn,onServerPrefetch:Bn,onRenderTracked:Un,onRenderTriggered:zn,onErrorCaptured:function(t,e){void 0===e&&(e=ut),Vn(t,e)}}),qn=new st;function Wn(t){return Zn(t,qn),qn.clear(),t}function Zn(t,n){var r,o,i=e(t);if(!(!i&&!s(t)||t.__v_skip||Object.isFrozen(t)||t instanceof ft)){if(t.__ob__){var a=t.__ob__.dep.id;if(n.has(a))return;n.add(a)}if(i)for(r=t.length;r--;)Zn(t[r],n);else if(Bt(t))Zn(t.value,n);else for(r=(o=Object.keys(t)).length;r--;)Zn(t[o[r]],n)}}var Gn=0,Xn=function(){function t(t,e,n,r,o){!function(t,e){void 0===e&&(e=Me),e&&e.active&&e.effects.push(t)}(this,Me&&!Me._vm?Me:t?t._scope:void 0),(this.vm=t)&&o&&(t._watcher=this),r?(this.deep=!!r.deep,this.user=!!r.user,this.lazy=!!r.lazy,this.sync=!!r.sync,this.before=r.before):this.deep=this.user=this.lazy=this.sync=!1,this.cb=n,this.id=++Gn,this.active=!0,this.post=!1,this.dirty=this.lazy,this.deps=[],this.newDeps=[],this.depIds=new st,this.newDepIds=new st,this.expression="",a(e)?this.getter=e:(this.getter=function(t){if(!K.test(t)){var e=t.split(".");return function(t){for(var n=0;n-1)if(i&&!b(o,"default"))s=!1;else if(""===s||s===S(t)){var u=jr(String,o.type);(u<0||c-1:"string"==typeof t?t.split(",").indexOf(n)>-1:(r=t,"[object RegExp]"===c.call(r)&&t.test(n));var r}function Mr(t,e){var n=t.cache,r=t.keys,o=t._vnode,i=t.$vnode;for(var a in n){var s=n[a];if(s){var c=s.name;c&&!e(c)&&Ir(n,a,r,o)}}i.componentOptions.children=void 0}function Ir(t,e,n,r){var o=t[e];!o||r&&o.tag===r.tag||o.componentInstance.$destroy(),t[e]=null,y(n,e)}!function(e){e.prototype._init=function(e){var n=this;n._uid=sr++,n._isVue=!0,n.__v_skip=!0,n._scope=new ze(!0),n._scope.parent=void 0,n._scope._vm=!0,e&&e._isComponent?function(t,e){var n=t.$options=Object.create(t.constructor.options),r=e._parentVnode;n.parent=e.parent,n._parentVnode=r;var o=r.componentOptions;n.propsData=o.propsData,n._parentListeners=o.listeners,n._renderChildren=o.children,n._componentTag=o.tag,e.render&&(n.render=e.render,n.staticRenderFns=e.staticRenderFns)}(n,e):n.$options=Cr(cr(n.constructor),e||{},n),n._renderProxy=n,n._self=n,function(t){var e=t.$options,n=e.parent;if(n&&!e.abstract){for(;n.$options.abstract&&n.$parent;)n=n.$parent;n.$children.push(t)}t.$parent=n,t.$root=n?n.$root:t,t.$children=[],t.$refs={},t._provided=n?n._provided:Object.create(null),t._watcher=null,t._inactive=null,t._directInactive=!1,t._isMounted=!1,t._isDestroyed=!1,t._isBeingDestroyed=!1}(n),function(t){t._events=Object.create(null),t._hasHookEvent=!1;var e=t.$options._parentListeners;e&&Ue(t,e)}(n),function(e){e._vnode=null,e._staticTrees=null;var n=e.$options,r=e.$vnode=n._parentVnode,o=r&&r.context;e.$slots=xe(n._renderChildren,o),e.$scopedSlots=r?Se(e.$parent,r.data.scopedSlots,e.$slots):t,e._c=function(t,n,r,o){return ae(e,t,n,r,o,!1)},e.$createElement=function(t,n,r,o){return ae(e,t,n,r,o,!0)};var i=r&&r.data;Et(e,"$attrs",i&&i.attrs||t,null,!0),Et(e,"$listeners",n._parentListeners||t,null,!0)}(n),Ge(n,"beforeCreate",void 0,!1),function(t){var e=ar(t.$options.inject,t);e&&(Ot(!1),Object.keys(e).forEach((function(n){Et(t,n,e[n])})),Ot(!0))}(n),tr(n),function(t){var e=t.$options.provide;if(e){var n=a(e)?e.call(t):e;if(!s(n))return;for(var r=gn(t),o=ct?Reflect.ownKeys(n):Object.keys(n),i=0;i1?T(n):n;for(var r=T(arguments,1),o='event handler for "'.concat(t,'"'),i=0,a=n.length;iparseInt(this.max)&&Ir(e,n[0],n,this._vnode),this.vnodeToCache=null}}},created:function(){this.cache=Object.create(null),this.keys=[]},destroyed:function(){for(var t in this.cache)Ir(this.cache,t,this.keys)},mounted:function(){var t=this;this.cacheVNode(),this.$watch("include",(function(e){Mr(t,(function(t){return Dr(e,t)}))})),this.$watch("exclude",(function(e){Mr(t,(function(t){return!Dr(e,t)}))}))},updated:function(){this.cacheVNode()},render:function(){var t=this.$slots.default,e=Re(t),n=e&&e.componentOptions;if(n){var r=Pr(n),o=this.include,i=this.exclude;if(o&&(!r||!Dr(o,r))||i&&r&&Dr(i,r))return e;var a=this.cache,s=this.keys,c=null==e.key?n.Ctor.cid+(n.tag?"::".concat(n.tag):""):e.key;a[c]?(e.componentInstance=a[c].componentInstance,y(s,c),s.push(c)):(this.vnodeToCache=e,this.keyToCache=c),e.data.keepAlive=!0}return e||t&&t[0]}},Fr={KeepAlive:Rr};!function(t){var e={get:function(){return B}};Object.defineProperty(t,"config",e),t.util={warn:gr,extend:A,mergeOptions:Cr,defineReactive:Et},t.set=Nt,t.delete=Pt,t.nextTick=En,t.observable=function(t){return jt(t),t},t.options=Object.create(null),F.forEach((function(e){t.options[e+"s"]=Object.create(null)})),t.options._base=t,A(t.options.components,Fr),function(t){t.use=function(t){var e=this._installedPlugins||(this._installedPlugins=[]);if(e.indexOf(t)>-1)return this;var n=T(arguments,1);return n.unshift(this),a(t.install)?t.install.apply(t,n):a(t)&&t.apply(null,n),e.push(t),this}}(t),function(t){t.mixin=function(t){return this.options=Cr(this.options,t),this}}(t),Nr(t),function(t){F.forEach((function(e){t[e]=function(t,n){return n?("component"===e&&u(n)&&(n.name=n.name||t,n=this.options._base.extend(n)),"directive"===e&&a(n)&&(n={bind:n,update:n}),this.options[e+"s"][t]=n,n):this.options[e+"s"][t]}}))}(t)}(Er),Object.defineProperty(Er.prototype,"$isServer",{get:ot}),Object.defineProperty(Er.prototype,"$ssrContext",{get:function(){return this.$vnode&&this.$vnode.ssrContext}}),Object.defineProperty(Er,"FunctionalRenderContext",{value:ur}),Er.version=Kn;var Hr=h("style,class"),Br=h("input,textarea,option,select,progress"),Ur=function(t,e,n){return"value"===n&&Br(t)&&"button"!==e||"selected"===n&&"option"===t||"checked"===n&&"input"===t||"muted"===n&&"video"===t},zr=h("contenteditable,draggable,spellcheck"),Vr=h("events,caret,typing,plaintext-only"),Kr=function(t,e){return Gr(e)||"false"===e?"false":"contenteditable"===t&&Vr(e)?e:"true"},Jr=h("allowfullscreen,async,autofocus,autoplay,checked,compact,controls,declare,default,defaultchecked,defaultmuted,defaultselected,defer,disabled,enabled,formnovalidate,hidden,indeterminate,inert,ismap,itemscope,loop,multiple,muted,nohref,noresize,noshade,novalidate,nowrap,open,pauseonexit,readonly,required,reversed,scoped,seamless,selected,sortable,truespeed,typemustmatch,visible"),qr="http://www.w3.org/1999/xlink",Wr=function(t){return":"===t.charAt(5)&&"xlink"===t.slice(0,5)},Zr=function(t){return Wr(t)?t.slice(6,t.length):""},Gr=function(t){return null==t||!1===t};function Xr(t){for(var e=t.data,n=t,o=t;r(o.componentInstance);)(o=o.componentInstance._vnode)&&o.data&&(e=Yr(o.data,e));for(;r(n=n.parent);)n&&n.data&&(e=Yr(e,n.data));return function(t,e){if(r(t)||r(e))return Qr(t,to(e));return""}(e.staticClass,e.class)}function Yr(t,e){return{staticClass:Qr(t.staticClass,e.staticClass),class:r(t.class)?[t.class,e.class]:e.class}}function Qr(t,e){return t?e?t+" "+e:t:e||""}function to(t){return Array.isArray(t)?function(t){for(var e,n="",o=0,i=t.length;o-1?Oo(t,e,n):Jr(e)?Gr(n)?t.removeAttribute(e):(n="allowfullscreen"===e&&"EMBED"===t.tagName?"true":e,t.setAttribute(e,n)):zr(e)?t.setAttribute(e,Kr(e,n)):Wr(e)?Gr(n)?t.removeAttributeNS(qr,Zr(e)):t.setAttributeNS(qr,e,n):Oo(t,e,n)}function Oo(t,e,n){if(Gr(n))t.removeAttribute(e);else{if(Z&&!G&&"TEXTAREA"===t.tagName&&"placeholder"===e&&""!==n&&!t.__ieph){var r=function(e){e.stopImmediatePropagation(),t.removeEventListener("input",r)};t.addEventListener("input",r),t.__ieph=!0}t.setAttribute(e,n)}}var To={create:ko,update:ko};function Ao(t,e){var o=e.elm,i=e.data,a=t.data;if(!(n(i.staticClass)&&n(i.class)&&(n(a)||n(a.staticClass)&&n(a.class)))){var s=Xr(e),c=o._transitionClasses;r(c)&&(s=Qr(s,to(c))),s!==o._prevClass&&(o.setAttribute("class",s),o._prevClass=s)}}var jo,Eo,No,Po,Do,Mo,Io={create:Ao,update:Ao},Lo=/[\w).+\-_$\]]/;function Ro(t){var e,n,r,o,i,a=!1,s=!1,c=!1,u=!1,l=0,f=0,d=0,p=0;for(r=0;r=0&&" "===(h=t.charAt(v));v--);h&&Lo.test(h)||(u=!0)}}else void 0===o?(p=r+1,o=t.slice(0,r).trim()):m();function m(){(i||(i=[])).push(t.slice(p,r).trim()),p=r+1}if(void 0===o?o=t.slice(0,r).trim():0!==p&&m(),i)for(r=0;r-1?{exp:t.slice(0,Po),key:'"'+t.slice(Po+1)+'"'}:{exp:t,key:null};Eo=t,Po=Do=Mo=0;for(;!ei();)ni(No=ti())?oi(No):91===No&&ri(No);return{exp:t.slice(0,Do),key:t.slice(Do+1,Mo)}}(t);return null===n.key?"".concat(t,"=").concat(e):"$set(".concat(n.exp,", ").concat(n.key,", ").concat(e,")")}function ti(){return Eo.charCodeAt(++Po)}function ei(){return Po>=jo}function ni(t){return 34===t||39===t}function ri(t){var e=1;for(Do=Po;!ei();)if(ni(t=ti()))oi(t);else if(91===t&&e++,93===t&&e--,0===e){Mo=Po;break}}function oi(t){for(var e=t;!ei()&&(t=ti())!==e;);}var ii,ai="__r",si="__c";function ci(t,e,n){var r=ii;return function o(){null!==e.apply(null,arguments)&&fi(t,o,n,r)}}var ui=xn&&!(tt&&Number(tt[1])<=53);function li(t,e,n,r){if(ui){var o=rn,i=e;e=i._wrapper=function(t){if(t.target===t.currentTarget||t.timeStamp>=o||t.timeStamp<=0||t.target.ownerDocument!==document)return i.apply(this,arguments)}}ii.addEventListener(t,e,nt?{capture:n,passive:r}:n)}function fi(t,e,n,r){(r||ii).removeEventListener(t,e._wrapper||e,n)}function di(t,e){if(!n(t.data.on)||!n(e.data.on)){var o=e.data.on||{},i=t.data.on||{};ii=e.elm||t.elm,function(t){if(r(t[ai])){var e=Z?"change":"input";t[e]=[].concat(t[ai],t[e]||[]),delete t[ai]}r(t[si])&&(t.change=[].concat(t[si],t.change||[]),delete t[si])}(o),Yt(o,i,li,fi,ci,e.context),ii=void 0}}var pi,vi={create:di,update:di,destroy:function(t){return di(t,vo)}};function hi(t,e){if(!n(t.data.domProps)||!n(e.data.domProps)){var i,a,s=e.elm,c=t.data.domProps||{},u=e.data.domProps||{};for(i in(r(u.__ob__)||o(u._v_attr_proxy))&&(u=e.data.domProps=A({},u)),c)i in u||(s[i]="");for(i in u){if(a=u[i],"textContent"===i||"innerHTML"===i){if(e.children&&(e.children.length=0),a===c[i])continue;1===s.childNodes.length&&s.removeChild(s.childNodes[0])}if("value"===i&&"PROGRESS"!==s.tagName){s._value=a;var l=n(a)?"":String(a);mi(s,l)&&(s.value=l)}else if("innerHTML"===i&&ro(s.tagName)&&n(s.innerHTML)){(pi=pi||document.createElement("div")).innerHTML="".concat(a,"");for(var f=pi.firstChild;s.firstChild;)s.removeChild(s.firstChild);for(;f.firstChild;)s.appendChild(f.firstChild)}else if(a!==c[i])try{s[i]=a}catch(t){}}}}function mi(t,e){return!t.composing&&("OPTION"===t.tagName||function(t,e){var n=!0;try{n=document.activeElement!==t}catch(t){}return n&&t.value!==e}(t,e)||function(t,e){var n=t.value,o=t._vModifiers;if(r(o)){if(o.number)return v(n)!==v(e);if(o.trim)return n.trim()!==e.trim()}return n!==e}(t,e))}var gi={create:hi,update:hi},yi=$((function(t){var e={},n=/:(.+)/;return t.split(/;(?![^(]*\))/g).forEach((function(t){if(t){var r=t.split(n);r.length>1&&(e[r[0].trim()]=r[1].trim())}})),e}));function _i(t){var e=bi(t.style);return t.staticStyle?A(t.staticStyle,e):e}function bi(t){return Array.isArray(t)?j(t):"string"==typeof t?yi(t):t}var $i,wi=/^--/,xi=/\s*!important$/,Ci=function(t,e,n){if(wi.test(e))t.style.setProperty(e,n);else if(xi.test(n))t.style.setProperty(S(e),n.replace(xi,""),"important");else{var r=Si(e);if(Array.isArray(n))for(var o=0,i=n.length;o-1?e.split(Ai).forEach((function(e){return t.classList.add(e)})):t.classList.add(e);else{var n=" ".concat(t.getAttribute("class")||""," ");n.indexOf(" "+e+" ")<0&&t.setAttribute("class",(n+e).trim())}}function Ei(t,e){if(e&&(e=e.trim()))if(t.classList)e.indexOf(" ")>-1?e.split(Ai).forEach((function(e){return t.classList.remove(e)})):t.classList.remove(e),t.classList.length||t.removeAttribute("class");else{for(var n=" ".concat(t.getAttribute("class")||""," "),r=" "+e+" ";n.indexOf(r)>=0;)n=n.replace(r," ");(n=n.trim())?t.setAttribute("class",n):t.removeAttribute("class")}}function Ni(t){if(t){if("object"==typeof t){var e={};return!1!==t.css&&A(e,Pi(t.name||"v")),A(e,t),e}return"string"==typeof t?Pi(t):void 0}}var Pi=$((function(t){return{enterClass:"".concat(t,"-enter"),enterToClass:"".concat(t,"-enter-to"),enterActiveClass:"".concat(t,"-enter-active"),leaveClass:"".concat(t,"-leave"),leaveToClass:"".concat(t,"-leave-to"),leaveActiveClass:"".concat(t,"-leave-active")}})),Di=q&&!G,Mi="transition",Ii="animation",Li="transition",Ri="transitionend",Fi="animation",Hi="animationend";Di&&(void 0===window.ontransitionend&&void 0!==window.onwebkittransitionend&&(Li="WebkitTransition",Ri="webkitTransitionEnd"),void 0===window.onanimationend&&void 0!==window.onwebkitanimationend&&(Fi="WebkitAnimation",Hi="webkitAnimationEnd"));var Bi=q?window.requestAnimationFrame?window.requestAnimationFrame.bind(window):setTimeout:function(t){return t()};function Ui(t){Bi((function(){Bi(t)}))}function zi(t,e){var n=t._transitionClasses||(t._transitionClasses=[]);n.indexOf(e)<0&&(n.push(e),ji(t,e))}function Vi(t,e){t._transitionClasses&&y(t._transitionClasses,e),Ei(t,e)}function Ki(t,e,n){var r=qi(t,e),o=r.type,i=r.timeout,a=r.propCount;if(!o)return n();var s=o===Mi?Ri:Hi,c=0,u=function(){t.removeEventListener(s,l),n()},l=function(e){e.target===t&&++c>=a&&u()};setTimeout((function(){c0&&(n=Mi,l=a,f=i.length):e===Ii?u>0&&(n=Ii,l=u,f=c.length):f=(n=(l=Math.max(a,u))>0?a>u?Mi:Ii:null)?n===Mi?i.length:c.length:0,{type:n,timeout:l,propCount:f,hasTransform:n===Mi&&Ji.test(r[Li+"Property"])}}function Wi(t,e){for(;t.length1}function ta(t,e){!0!==e.data.show&&Gi(e)}var ea=function(t){var a,s,c={},u=t.modules,l=t.nodeOps;for(a=0;av?b(t,n(o[g+1])?null:o[g+1].elm,o,p,g,i):p>g&&w(e,f,v)}(f,h,m,i,u):r(m)?(r(t.text)&&l.setTextContent(f,""),b(f,null,m,0,m.length-1,i)):r(h)?w(h,0,h.length-1):r(t.text)&&l.setTextContent(f,""):t.text!==e.text&&l.setTextContent(f,e.text),r(v)&&r(p=v.hook)&&r(p=p.postpatch)&&p(t,e)}}}function S(t,e,n){if(o(n)&&r(t.parent))t.parent.data.pendingInsert=e;else for(var i=0;i-1,a.selected!==i&&(a.selected=i);else if(D(aa(a),r))return void(t.selectedIndex!==s&&(t.selectedIndex=s));o||(t.selectedIndex=-1)}}function ia(t,e){return e.every((function(e){return!D(e,t)}))}function aa(t){return"_value"in t?t._value:t.value}function sa(t){t.target.composing=!0}function ca(t){t.target.composing&&(t.target.composing=!1,ua(t.target,"input"))}function ua(t,e){var n=document.createEvent("HTMLEvents");n.initEvent(e,!0,!0),t.dispatchEvent(n)}function la(t){return!t.componentInstance||t.data&&t.data.transition?t:la(t.componentInstance._vnode)}var fa={bind:function(t,e,n){var r=e.value,o=(n=la(n)).data&&n.data.transition,i=t.__vOriginalDisplay="none"===t.style.display?"":t.style.display;r&&o?(n.data.show=!0,Gi(n,(function(){t.style.display=i}))):t.style.display=r?i:"none"},update:function(t,e,n){var r=e.value;!r!=!e.oldValue&&((n=la(n)).data&&n.data.transition?(n.data.show=!0,r?Gi(n,(function(){t.style.display=t.__vOriginalDisplay})):Xi(n,(function(){t.style.display="none"}))):t.style.display=r?t.__vOriginalDisplay:"none")},unbind:function(t,e,n,r,o){o||(t.style.display=t.__vOriginalDisplay)}},da={model:na,show:fa},pa={name:String,appear:Boolean,css:Boolean,mode:String,type:String,enterClass:String,leaveClass:String,enterToClass:String,leaveToClass:String,enterActiveClass:String,leaveActiveClass:String,appearClass:String,appearActiveClass:String,appearToClass:String,duration:[Number,String,Object]};function va(t){var e=t&&t.componentOptions;return e&&e.Ctor.options.abstract?va(Re(e.children)):t}function ha(t){var e={},n=t.$options;for(var r in n.propsData)e[r]=t[r];var o=n._parentListeners;for(var r in o)e[x(r)]=o[r];return e}function ma(t,e){if(/\d-keep-alive$/.test(e.tag))return t("keep-alive",{props:e.componentOptions.propsData})}var ga=function(t){return t.tag||ke(t)},ya=function(t){return"show"===t.name},_a={name:"transition",props:pa,abstract:!0,render:function(t){var e=this,n=this.$slots.default;if(n&&(n=n.filter(ga)).length){var r=this.mode,o=n[0];if(function(t){for(;t=t.parent;)if(t.data.transition)return!0}(this.$vnode))return o;var a=va(o);if(!a)return o;if(this._leaving)return ma(t,o);var s="__transition-".concat(this._uid,"-");a.key=null==a.key?a.isComment?s+"comment":s+a.tag:i(a.key)?0===String(a.key).indexOf(s)?a.key:s+a.key:a.key;var c=(a.data||(a.data={})).transition=ha(this),u=this._vnode,l=va(u);if(a.data.directives&&a.data.directives.some(ya)&&(a.data.show=!0),l&&l.data&&!function(t,e){return e.key===t.key&&e.tag===t.tag}(a,l)&&!ke(l)&&(!l.componentInstance||!l.componentInstance._vnode.isComment)){var f=l.data.transition=A({},c);if("out-in"===r)return this._leaving=!0,Qt(f,"afterLeave",(function(){e._leaving=!1,e.$forceUpdate()})),ma(t,o);if("in-out"===r){if(ke(a))return u;var d,p=function(){d()};Qt(c,"afterEnter",p),Qt(c,"enterCancelled",p),Qt(f,"delayLeave",(function(t){d=t}))}}return o}}},ba=A({tag:String,moveClass:String},pa);delete ba.mode;var $a={props:ba,beforeMount:function(){var t=this,e=this._update;this._update=function(n,r){var o=Je(t);t.__patch__(t._vnode,t.kept,!1,!0),t._vnode=t.kept,o(),e.call(t,n,r)}},render:function(t){for(var e=this.tag||this.$vnode.data.tag||"span",n=Object.create(null),r=this.prevChildren=this.children,o=this.$slots.default||[],i=this.children=[],a=ha(this),s=0;s-1?ao[t]=e.constructor===window.HTMLUnknownElement||e.constructor===window.HTMLElement:ao[t]=/HTMLUnknownElement/.test(e.toString())},A(Er.options.directives,da),A(Er.options.components,ka),Er.prototype.__patch__=q?ea:E,Er.prototype.$mount=function(t,e){return function(t,e,n){var r;t.$el=e,t.$options.render||(t.$options.render=dt),Ge(t,"beforeMount"),r=function(){t._update(t._render(),n)},new Xn(t,r,E,{before:function(){t._isMounted&&!t._isDestroyed&&Ge(t,"beforeUpdate")}},!0),n=!1;var o=t._preWatchers;if(o)for(var i=0;i\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/,La=/^\s*((?:v-[\w-]+:|@|:|#)\[[^=]+?\][^\s"'<>\/=]*)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/,Ra="[a-zA-Z_][\\-\\.0-9_a-zA-Z".concat(U.source,"]*"),Fa="((?:".concat(Ra,"\\:)?").concat(Ra,")"),Ha=new RegExp("^<".concat(Fa)),Ba=/^\s*(\/?)>/,Ua=new RegExp("^<\\/".concat(Fa,"[^>]*>")),za=/^]+>/i,Va=/^",""":'"',"&":"&"," ":"\n"," ":"\t","'":"'"},Za=/&(?:lt|gt|quot|amp|#39);/g,Ga=/&(?:lt|gt|quot|amp|#39|#10|#9);/g,Xa=h("pre,textarea",!0),Ya=function(t,e){return t&&Xa(t)&&"\n"===e[0]};function Qa(t,e){var n=e?Ga:Za;return t.replace(n,(function(t){return Wa[t]}))}function ts(t,e){for(var n,r,o=[],i=e.expectHTML,a=e.isUnaryTag||N,s=e.canBeLeftOpenTag||N,c=0,u=function(){if(n=t,r&&Ja(r)){var u=0,d=r.toLowerCase(),p=qa[d]||(qa[d]=new RegExp("([\\s\\S]*?)(]*>)","i"));w=t.replace(p,(function(t,n,r){return u=r.length,Ja(d)||"noscript"===d||(n=n.replace(//g,"$1").replace(//g,"$1")),Ya(d,n)&&(n=n.slice(1)),e.chars&&e.chars(n),""}));c+=t.length-w.length,t=w,f(d,c-u,c)}else{var v=t.indexOf("<");if(0===v){if(Va.test(t)){var h=t.indexOf("--\x3e");if(h>=0)return e.shouldKeepComment&&e.comment&&e.comment(t.substring(4,h),c,c+h+3),l(h+3),"continue"}if(Ka.test(t)){var m=t.indexOf("]>");if(m>=0)return l(m+2),"continue"}var g=t.match(za);if(g)return l(g[0].length),"continue";var y=t.match(Ua);if(y){var _=c;return l(y[0].length),f(y[1],_,c),"continue"}var b=function(){var e=t.match(Ha);if(e){var n={tagName:e[1],attrs:[],start:c};l(e[0].length);for(var r=void 0,o=void 0;!(r=t.match(Ba))&&(o=t.match(La)||t.match(Ia));)o.start=c,l(o[0].length),o.end=c,n.attrs.push(o);if(r)return n.unarySlash=r[1],l(r[0].length),n.end=c,n}}();if(b)return function(t){var n=t.tagName,c=t.unarySlash;i&&("p"===r&&Ma(n)&&f(r),s(n)&&r===n&&f(n));for(var u=a(n)||!!c,l=t.attrs.length,d=new Array(l),p=0;p=0){for(w=t.slice(v);!(Ua.test(w)||Ha.test(w)||Va.test(w)||Ka.test(w)||(x=w.indexOf("<",1))<0);)v+=x,w=t.slice(v);$=t.substring(0,v)}v<0&&($=t),$&&l($.length),e.chars&&$&&e.chars($,c-$.length,c)}if(t===n)return e.chars&&e.chars(t),"break"};t;){if("break"===u())break}function l(e){c+=e,t=t.substring(e)}function f(t,n,i){var a,s;if(null==n&&(n=c),null==i&&(i=c),t)for(s=t.toLowerCase(),a=o.length-1;a>=0&&o[a].lowerCasedTag!==s;a--);else a=0;if(a>=0){for(var u=o.length-1;u>=a;u--)e.end&&e.end(o[u].tag,n,i);o.length=a,r=a&&o[a-1].tag}else"br"===s?e.start&&e.start(t,[],!0,n,i):"p"===s&&(e.start&&e.start(t,[],!1,n,i),e.end&&e.end(t,n,i))}f()}var es,ns,rs,os,is,as,ss,cs,us=/^@|^v-on:/,ls=/^v-|^@|^:|^#/,fs=/([\s\S]*?)\s+(?:in|of)\s+([\s\S]*)/,ds=/,([^,\}\]]*)(?:,([^,\}\]]*))?$/,ps=/^\(|\)$/g,vs=/^\[.*\]$/,hs=/:(.*)$/,ms=/^:|^\.|^v-bind:/,gs=/\.[^.\]]+(?=[^\]]*$)/g,ys=/^v-slot(:|$)|^#/,_s=/[\r\n]/,bs=/[ \f\t\r\n]+/g,$s=$(Na),ws="_empty_";function xs(t,e,n){return{type:1,tag:t,attrsList:e,attrsMap:js(e),rawAttrsMap:{},parent:n,children:[]}}function Cs(t,e){es=e.warn||Ho,as=e.isPreTag||N,ss=e.mustUseProp||N,cs=e.getTagNamespace||N,e.isReservedTag,rs=Bo(e.modules,"transformNode"),os=Bo(e.modules,"preTransformNode"),is=Bo(e.modules,"postTransformNode"),ns=e.delimiters;var n,r,o=[],i=!1!==e.preserveWhitespace,a=e.whitespace,s=!1,c=!1;function u(t){if(l(t),s||t.processed||(t=ks(t,e)),o.length||t===n||n.if&&(t.elseif||t.else)&&Os(n,{exp:t.elseif,block:t}),r&&!t.forbidden)if(t.elseif||t.else)a=t,u=function(t){for(var e=t.length;e--;){if(1===t[e].type)return t[e];t.pop()}}(r.children),u&&u.if&&Os(u,{exp:a.elseif,block:a});else{if(t.slotScope){var i=t.slotTarget||'"default"';(r.scopedSlots||(r.scopedSlots={}))[i]=t}r.children.push(t),t.parent=r}var a,u;t.children=t.children.filter((function(t){return!t.slotScope})),l(t),t.pre&&(s=!1),as(t.tag)&&(c=!1);for(var f=0;fc&&(s.push(i=t.slice(c,o)),a.push(JSON.stringify(i)));var u=Ro(r[1].trim());a.push("_s(".concat(u,")")),s.push({"@binding":u}),c=o+r[0].length}return c-1")+("true"===i?":(".concat(e,")"):":_q(".concat(e,",").concat(i,")"))),qo(t,"change","var $$a=".concat(e,",")+"$$el=$event.target,"+"$$c=$$el.checked?(".concat(i,"):(").concat(a,");")+"if(Array.isArray($$a)){"+"var $$v=".concat(r?"_n("+o+")":o,",")+"$$i=_i($$a,$$v);"+"if($$el.checked){$$i<0&&(".concat(Qo(e,"$$a.concat([$$v])"),")}")+"else{$$i>-1&&(".concat(Qo(e,"$$a.slice(0,$$i).concat($$a.slice($$i+1))"),")}")+"}else{".concat(Qo(e,"$$c"),"}"),null,!0)}(t,r,o);else if("input"===i&&"radio"===a)!function(t,e,n){var r=n&&n.number,o=Wo(t,"value")||"null";o=r?"_n(".concat(o,")"):o,Uo(t,"checked","_q(".concat(e,",").concat(o,")")),qo(t,"change",Qo(e,o),null,!0)}(t,r,o);else if("input"===i||"textarea"===i)!function(t,e,n){var r=t.attrsMap.type,o=n||{},i=o.lazy,a=o.number,s=o.trim,c=!i&&"range"!==r,u=i?"change":"range"===r?ai:"input",l="$event.target.value";s&&(l="$event.target.value.trim()");a&&(l="_n(".concat(l,")"));var f=Qo(e,l);c&&(f="if($event.target.composing)return;".concat(f));Uo(t,"value","(".concat(e,")")),qo(t,u,f,null,!0),(s||a)&&qo(t,"blur","$forceUpdate()")}(t,r,o);else if(!B.isReservedTag(i))return Yo(t,r,o),!1;return!0},text:function(t,e){e.value&&Uo(t,"textContent","_s(".concat(e.value,")"),e)},html:function(t,e){e.value&&Uo(t,"innerHTML","_s(".concat(e.value,")"),e)}},Rs={expectHTML:!0,modules:Ds,directives:Ls,isPreTag:function(t){return"pre"===t},isUnaryTag:Pa,mustUseProp:Ur,canBeLeftOpenTag:Da,isReservedTag:oo,getTagNamespace:io,staticKeys:function(t){return t.reduce((function(t,e){return t.concat(e.staticKeys||[])}),[]).join(",")}(Ds)},Fs=$((function(t){return h("type,tag,attrsList,attrsMap,plain,parent,children,attrs,start,end,rawAttrsMap"+(t?","+t:""))}));function Hs(t,e){t&&(Ms=Fs(e.staticKeys||""),Is=e.isReservedTag||N,Bs(t),Us(t,!1))}function Bs(t){if(t.static=function(t){if(2===t.type)return!1;if(3===t.type)return!0;return!(!t.pre&&(t.hasBindings||t.if||t.for||m(t.tag)||!Is(t.tag)||function(t){for(;t.parent;){if("template"!==(t=t.parent).tag)return!1;if(t.for)return!0}return!1}(t)||!Object.keys(t).every(Ms)))}(t),1===t.type){if(!Is(t.tag)&&"slot"!==t.tag&&null==t.attrsMap["inline-template"])return;for(var e=0,n=t.children.length;e|^function(?:\s+[\w$]+)?\s*\(/,Vs=/\([^)]*?\);*$/,Ks=/^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\['[^']*?']|\["[^"]*?"]|\[\d+]|\[[A-Za-z_$][\w$]*])*$/,Js={esc:27,tab:9,enter:13,space:32,up:38,left:37,right:39,down:40,delete:[8,46]},qs={esc:["Esc","Escape"],tab:"Tab",enter:"Enter",space:[" ","Spacebar"],up:["Up","ArrowUp"],left:["Left","ArrowLeft"],right:["Right","ArrowRight"],down:["Down","ArrowDown"],delete:["Backspace","Delete","Del"]},Ws=function(t){return"if(".concat(t,")return null;")},Zs={stop:"$event.stopPropagation();",prevent:"$event.preventDefault();",self:Ws("$event.target !== $event.currentTarget"),ctrl:Ws("!$event.ctrlKey"),shift:Ws("!$event.shiftKey"),alt:Ws("!$event.altKey"),meta:Ws("!$event.metaKey"),left:Ws("'button' in $event && $event.button !== 0"),middle:Ws("'button' in $event && $event.button !== 1"),right:Ws("'button' in $event && $event.button !== 2")};function Gs(t,e){var n=e?"nativeOn:":"on:",r="",o="";for(var i in t){var a=Xs(t[i]);t[i]&&t[i].dynamic?o+="".concat(i,",").concat(a,","):r+='"'.concat(i,'":').concat(a,",")}return r="{".concat(r.slice(0,-1),"}"),o?n+"_d(".concat(r,",[").concat(o.slice(0,-1),"])"):n+r}function Xs(t){if(!t)return"function(){}";if(Array.isArray(t))return"[".concat(t.map((function(t){return Xs(t)})).join(","),"]");var e=Ks.test(t.value),n=zs.test(t.value),r=Ks.test(t.value.replace(Vs,""));if(t.modifiers){var o="",i="",a=[],s=function(e){if(Zs[e])i+=Zs[e],Js[e]&&a.push(e);else if("exact"===e){var n=t.modifiers;i+=Ws(["ctrl","shift","alt","meta"].filter((function(t){return!n[t]})).map((function(t){return"$event.".concat(t,"Key")})).join("||"))}else a.push(e)};for(var c in t.modifiers)s(c);a.length&&(o+=function(t){return"if(!$event.type.indexOf('key')&&"+"".concat(t.map(Ys).join("&&"),")return null;")}(a)),i&&(o+=i);var u=e?"return ".concat(t.value,".apply(null, arguments)"):n?"return (".concat(t.value,").apply(null, arguments)"):r?"return ".concat(t.value):t.value;return"function($event){".concat(o).concat(u,"}")}return e||n?t.value:"function($event){".concat(r?"return ".concat(t.value):t.value,"}")}function Ys(t){var e=parseInt(t,10);if(e)return"$event.keyCode!==".concat(e);var n=Js[t],r=qs[t];return"_k($event.keyCode,"+"".concat(JSON.stringify(t),",")+"".concat(JSON.stringify(n),",")+"$event.key,"+"".concat(JSON.stringify(r))+")"}var Qs={on:function(t,e){t.wrapListeners=function(t){return"_g(".concat(t,",").concat(e.value,")")}},bind:function(t,e){t.wrapData=function(n){return"_b(".concat(n,",'").concat(t.tag,"',").concat(e.value,",").concat(e.modifiers&&e.modifiers.prop?"true":"false").concat(e.modifiers&&e.modifiers.sync?",true":"",")")}},cloak:E},tc=function(t){this.options=t,this.warn=t.warn||Ho,this.transforms=Bo(t.modules,"transformCode"),this.dataGenFns=Bo(t.modules,"genData"),this.directives=A(A({},Qs),t.directives);var e=t.isReservedTag||N;this.maybeComponent=function(t){return!!t.component||!e(t.tag)},this.onceId=0,this.staticRenderFns=[],this.pre=!1};function ec(t,e){var n=new tc(e),r=t?"script"===t.tag?"null":nc(t,n):'_c("div")';return{render:"with(this){return ".concat(r,"}"),staticRenderFns:n.staticRenderFns}}function nc(t,e){if(t.parent&&(t.pre=t.pre||t.parent.pre),t.staticRoot&&!t.staticProcessed)return rc(t,e);if(t.once&&!t.onceProcessed)return oc(t,e);if(t.for&&!t.forProcessed)return sc(t,e);if(t.if&&!t.ifProcessed)return ic(t,e);if("template"!==t.tag||t.slotTarget||e.pre){if("slot"===t.tag)return function(t,e){var n=t.slotName||'"default"',r=fc(t,e),o="_t(".concat(n).concat(r?",function(){return ".concat(r,"}"):""),i=t.attrs||t.dynamicAttrs?vc((t.attrs||[]).concat(t.dynamicAttrs||[]).map((function(t){return{name:x(t.name),value:t.value,dynamic:t.dynamic}}))):null,a=t.attrsMap["v-bind"];!i&&!a||r||(o+=",null");i&&(o+=",".concat(i));a&&(o+="".concat(i?"":",null",",").concat(a));return o+")"}(t,e);var n=void 0;if(t.component)n=function(t,e,n){var r=e.inlineTemplate?null:fc(e,n,!0);return"_c(".concat(t,",").concat(cc(e,n)).concat(r?",".concat(r):"",")")}(t.component,t,e);else{var r=void 0,o=e.maybeComponent(t);(!t.plain||t.pre&&o)&&(r=cc(t,e));var i=void 0,a=e.options.bindings;o&&a&&!1!==a.__isScriptSetup&&(i=function(t,e){var n=x(e),r=C(n),o=function(o){return t[e]===o?e:t[n]===o?n:t[r]===o?r:void 0},i=o("setup-const")||o("setup-reactive-const");if(i)return i;var a=o("setup-let")||o("setup-ref")||o("setup-maybe-ref");if(a)return a}(a,t.tag)),i||(i="'".concat(t.tag,"'"));var s=t.inlineTemplate?null:fc(t,e,!0);n="_c(".concat(i).concat(r?",".concat(r):"").concat(s?",".concat(s):"",")")}for(var c=0;c>>0}(a)):"",")")}(t,t.scopedSlots,e),",")),t.model&&(n+="model:{value:".concat(t.model.value,",callback:").concat(t.model.callback,",expression:").concat(t.model.expression,"},")),t.inlineTemplate){var i=function(t,e){var n=t.children[0];if(n&&1===n.type){var r=ec(n,e.options);return"inlineTemplate:{render:function(){".concat(r.render,"},staticRenderFns:[").concat(r.staticRenderFns.map((function(t){return"function(){".concat(t,"}")})).join(","),"]}")}}(t,e);i&&(n+="".concat(i,","))}return n=n.replace(/,$/,"")+"}",t.dynamicAttrs&&(n="_b(".concat(n,',"').concat(t.tag,'",').concat(vc(t.dynamicAttrs),")")),t.wrapData&&(n=t.wrapData(n)),t.wrapListeners&&(n=t.wrapListeners(n)),n}function uc(t){return 1===t.type&&("slot"===t.tag||t.children.some(uc))}function lc(t,e){var n=t.attrsMap["slot-scope"];if(t.if&&!t.ifProcessed&&!n)return ic(t,e,lc,"null");if(t.for&&!t.forProcessed)return sc(t,e,lc);var r=t.slotScope===ws?"":String(t.slotScope),o="function(".concat(r,"){")+"return ".concat("template"===t.tag?t.if&&n?"(".concat(t.if,")?").concat(fc(t,e)||"undefined",":undefined"):fc(t,e)||"undefined":nc(t,e),"}"),i=r?"":",proxy:true";return"{key:".concat(t.slotTarget||'"default"',",fn:").concat(o).concat(i,"}")}function fc(t,e,n,r,o){var i=t.children;if(i.length){var a=i[0];if(1===i.length&&a.for&&"template"!==a.tag&&"slot"!==a.tag){var s=n?e.maybeComponent(a)?",1":",0":"";return"".concat((r||nc)(a,e)).concat(s)}var c=n?function(t,e){for(var n=0,r=0;r':'
      ',_c.innerHTML.indexOf(" ")>0}var xc=!!q&&wc(!1),Cc=!!q&&wc(!0),kc=$((function(t){var e=co(t);return e&&e.innerHTML})),Sc=Er.prototype.$mount;return Er.prototype.$mount=function(t,e){if((t=t&&co(t))===document.body||t===document.documentElement)return this;var n=this.$options;if(!n.render){var r=n.template;if(r)if("string"==typeof r)"#"===r.charAt(0)&&(r=kc(r));else{if(!r.nodeType)return this;r=r.innerHTML}else t&&(r=function(t){if(t.outerHTML)return t.outerHTML;var e=document.createElement("div");return e.appendChild(t.cloneNode(!0)),e.innerHTML}(t));if(r){var o=$c(r,{outputSourceRange:!1,shouldDecodeNewlines:xc,shouldDecodeNewlinesForHref:Cc,delimiters:n.delimiters,comments:n.comments},this),i=o.render,a=o.staticRenderFns;n.render=i,n.staticRenderFns=a}}return Sc.call(this,t,e)},Er.compile=$c,A(Er,Jn),Er.effect=function(t,e){var n=new Xn(ut,t,E,{sync:!0});e&&(n.update=function(){e((function(){return n.run()}))})},Er})); \ No newline at end of file diff --git a/kirby/panel/dist/js/vue.runtime.min.js b/kirby/panel/dist/js/vue.runtime.min.js new file mode 100644 index 0000000..4d56f42 --- /dev/null +++ b/kirby/panel/dist/js/vue.runtime.min.js @@ -0,0 +1,11 @@ +/*! + * Vue.js v2.7.16 + * (c) 2014-2023 Evan You + * Released under the MIT License. + */ +/*! + * Vue.js v2.7.16 + * (c) 2014-2023 Evan You + * Released under the MIT License. + */ +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).Vue=e()}(this,(function(){"use strict";var t=Object.freeze({}),e=Array.isArray;function n(t){return null==t}function r(t){return null!=t}function o(t){return!0===t}function i(t){return"string"==typeof t||"number"==typeof t||"symbol"==typeof t||"boolean"==typeof t}function a(t){return"function"==typeof t}function s(t){return null!==t&&"object"==typeof t}var c=Object.prototype.toString;function u(t){return"[object Object]"===c.call(t)}function f(t){var e=parseFloat(String(t));return e>=0&&Math.floor(e)===e&&isFinite(t)}function l(t){return r(t)&&"function"==typeof t.then&&"function"==typeof t.catch}function d(t){return null==t?"":Array.isArray(t)||u(t)&&t.toString===c?JSON.stringify(t,p,2):String(t)}function p(t,e){return e&&e.__v_isRef?e.value:e}function v(t){var e=parseFloat(t);return isNaN(e)?t:e}function h(t,e){for(var n=Object.create(null),r=t.split(","),o=0;o-1)return t.splice(r,1)}}var y=Object.prototype.hasOwnProperty;function g(t,e){return y.call(t,e)}function b(t){var e=Object.create(null);return function(n){return e[n]||(e[n]=t(n))}}var w=/-(\w)/g,$=b((function(t){return t.replace(w,(function(t,e){return e?e.toUpperCase():""}))})),C=b((function(t){return t.charAt(0).toUpperCase()+t.slice(1)})),x=/\B([A-Z])/g,O=b((function(t){return t.replace(x,"-$1").toLowerCase()}));var k=Function.prototype.bind?function(t,e){return t.bind(e)}:function(t,e){function n(n){var r=arguments.length;return r?r>1?t.apply(e,arguments):t.call(e,n):t.call(e)}return n._length=t.length,n};function S(t,e){e=e||0;for(var n=t.length-e,r=new Array(n);n--;)r[n]=t[n+e];return r}function j(t,e){for(var n in e)t[n]=e[n];return t}function T(t){for(var e={},n=0;n0,Z=K&&K.indexOf("edge/")>0;K&&K.indexOf("android");var J=K&&/iphone|ipad|ipod|ios/.test(K);K&&/chrome\/\d+/.test(K),K&&/phantomjs/.test(K);var X,Q=K&&K.match(/firefox\/(\d+)/),Y={}.watch,tt=!1;if(W)try{var et={};Object.defineProperty(et,"passive",{get:function(){tt=!0}}),window.addEventListener("test-passive",null,et)}catch(t){}var nt=function(){return void 0===X&&(X=!W&&"undefined"!=typeof global&&(global.process&&"server"===global.process.env.VUE_ENV)),X},rt=W&&window.__VUE_DEVTOOLS_GLOBAL_HOOK__;function ot(t){return"function"==typeof t&&/native code/.test(t.toString())}var it,at="undefined"!=typeof Symbol&&ot(Symbol)&&"undefined"!=typeof Reflect&&ot(Reflect.ownKeys);it="undefined"!=typeof Set&&ot(Set)?Set:function(){function t(){this.set=Object.create(null)}return t.prototype.has=function(t){return!0===this.set[t]},t.prototype.add=function(t){this.set[t]=!0},t.prototype.clear=function(){this.set=Object.create(null)},t}();var st=null;function ct(t){void 0===t&&(t=null),t||st&&st._scope.off(),st=t,t&&t._scope.on()}var ut=function(){function t(t,e,n,r,o,i,a,s){this.tag=t,this.data=e,this.children=n,this.text=r,this.elm=o,this.ns=void 0,this.context=i,this.fnContext=void 0,this.fnOptions=void 0,this.fnScopeId=void 0,this.key=e&&e.key,this.componentOptions=a,this.componentInstance=void 0,this.parent=void 0,this.raw=!1,this.isStatic=!1,this.isRootInsert=!0,this.isComment=!1,this.isCloned=!1,this.isOnce=!1,this.asyncFactory=s,this.asyncMeta=void 0,this.isAsyncPlaceholder=!1}return Object.defineProperty(t.prototype,"child",{get:function(){return this.componentInstance},enumerable:!1,configurable:!0}),t}(),ft=function(t){void 0===t&&(t="");var e=new ut;return e.text=t,e.isComment=!0,e};function lt(t){return new ut(void 0,void 0,void 0,String(t))}function dt(t){var e=new ut(t.tag,t.data,t.children&&t.children.slice(),t.text,t.elm,t.context,t.componentOptions,t.asyncFactory);return e.ns=t.ns,e.isStatic=t.isStatic,e.key=t.key,e.isComment=t.isComment,e.fnContext=t.fnContext,e.fnOptions=t.fnOptions,e.fnScopeId=t.fnScopeId,e.asyncMeta=t.asyncMeta,e.isCloned=!0,e}"function"==typeof SuppressedError&&SuppressedError;var pt=0,vt=[],ht=function(){for(var t=0;t0&&(le((c=de(c,"".concat(a||"","_").concat(s)))[0])&&le(f)&&(l[u]=lt(f.text+c[0].text),c.shift()),l.push.apply(l,c)):i(c)?le(f)?l[u]=lt(f.text+c):""!==c&&l.push(lt(c)):le(c)&&le(f)?l[u]=lt(f.text+c.text):(o(t._isVList)&&r(c.tag)&&n(c.key)&&r(a)&&(c.key="__vlist".concat(a,"_").concat(s,"__")),l.push(c)));return l}function pe(t,n){var o,i,a,c,u=null;if(e(t)||"string"==typeof t)for(u=new Array(t.length),o=0,i=t.length;o0,s=n?!!n.$stable:!a,c=n&&n.$key;if(n){if(n._normalized)return n._normalized;if(s&&o&&o!==t&&c===o.$key&&!a&&!o.$hasNormal)return o;for(var u in i={},n)n[u]&&"$"!==u[0]&&(i[u]=Pe(e,r,u,n[u]))}else i={};for(var f in r)f in i||(i[f]=Ie(r,f));return n&&Object.isExtensible(n)&&(n._normalized=i),B(i,"$stable",s),B(i,"$key",c),B(i,"$hasNormal",a),i}function Pe(t,n,r,o){var i=function(){var n=st;ct(t);var r=arguments.length?o.apply(null,arguments):o({}),i=(r=r&&"object"==typeof r&&!e(r)?[r]:fe(r))&&r[0];return ct(n),r&&(!i||1===r.length&&i.isComment&&!Ae(i))?void 0:r};return o.proxy&&Object.defineProperty(n,r,{get:i,enumerable:!0,configurable:!0}),i}function Ie(t,e){return function(){return t[e]}}function De(e){return{get attrs(){if(!e._attrsProxy){var n=e._attrsProxy={};B(n,"_v_attr_proxy",!0),Ne(n,e.$attrs,t,e,"$attrs")}return e._attrsProxy},get listeners(){e._listenersProxy||Ne(e._listenersProxy={},e.$listeners,t,e,"$listeners");return e._listenersProxy},get slots(){return function(t){t._slotsProxy||Le(t._slotsProxy={},t.$scopedSlots);return t._slotsProxy}(e)},emit:k(e.$emit,e),expose:function(t){t&&Object.keys(t).forEach((function(n){return Vt(e,t,n)}))}}}function Ne(t,e,n,r,o){var i=!1;for(var a in e)a in t?e[a]!==n[a]&&(i=!0):(i=!0,Me(t,a,r,o));for(var a in t)a in e||(i=!0,delete t[a]);return i}function Me(t,e,n,r){Object.defineProperty(t,e,{enumerable:!0,configurable:!0,get:function(){return n[r][e]}})}function Le(t,e){for(var n in e)t[n]=e[n];for(var n in t)n in e||delete t[n]}function Re(){var t=st;return t._setupContext||(t._setupContext=De(t))}var Fe=null;function Ue(t,e){return(t.__esModule||at&&"Module"===t[Symbol.toStringTag])&&(t=t.default),s(t)?e.extend(t):t}function Ve(t){if(e(t))for(var n=0;n1)return n&&a(e)?e.call(r):e}},h:function(t,e,n){return He(st,t,e,n,2,!0)},getCurrentInstance:function(){return st&&{proxy:st}},useSlots:function(){return Re().slots},useAttrs:function(){return Re().attrs},useListeners:function(){return Re().listeners},mergeDefaults:function(t,n){var r=e(t)?t.reduce((function(t,e){return t[e]={},t}),{}):t;for(var o in n){var i=r[o];i?e(i)||a(i)?r[o]={type:i,default:n[o]}:i.default=n[o]:null===i&&(r[o]={default:n[o]})}return r},nextTick:an,set:At,del:Et,useCssModule:function(e){return t},useCssVars:function(t){if(W){var e=st;e&&Qt((function(){var n=e.$el,r=t(e,e._setupProxy);if(n&&1===n.nodeType){var o=n.style;for(var i in r)o.setProperty("--".concat(i),r[i])}}))}},defineAsyncComponent:function(t){a(t)&&(t={loader:t});var e=t.loader,n=t.loadingComponent,r=t.errorComponent,o=t.delay,i=void 0===o?200:o,s=t.timeout;t.suspensible;var c=t.onError,u=null,f=0,l=function(){var t;return u||(t=u=e().catch((function(t){if(t=t instanceof Error?t:new Error(String(t)),c)return new Promise((function(e,n){c(t,(function(){return e((f++,u=null,l()))}),(function(){return n(t)}),f+1)}));throw t})).then((function(e){return t!==u&&u?u:(e&&(e.__esModule||"Module"===e[Symbol.toStringTag])&&(e=e.default),e)})))};return function(){return{component:l(),delay:i,timeout:s,error:r,loading:n}}},onBeforeMount:cn,onMounted:un,onBeforeUpdate:fn,onUpdated:ln,onBeforeUnmount:dn,onUnmounted:pn,onActivated:vn,onDeactivated:hn,onServerPrefetch:mn,onRenderTracked:_n,onRenderTriggered:yn,onErrorCaptured:function(t,e){void 0===e&&(e=st),gn(t,e)}}),$n=new it;function Cn(t){return xn(t,$n),$n.clear(),t}function xn(t,n){var r,o,i=e(t);if(!(!i&&!s(t)||t.__v_skip||Object.isFrozen(t)||t instanceof ut)){if(t.__ob__){var a=t.__ob__.dep.id;if(n.has(a))return;n.add(a)}if(i)for(r=t.length;r--;)xn(t[r],n);else if(Ft(t))xn(t.value,n);else for(r=(o=Object.keys(t)).length;r--;)xn(t[o[r]],n)}}var On,kn=0,Sn=function(){function t(t,e,n,r,o){var i,s;i=this,void 0===(s=Yt&&!Yt._vm?Yt:t?t._scope:void 0)&&(s=Yt),s&&s.active&&s.effects.push(i),(this.vm=t)&&o&&(t._watcher=this),r?(this.deep=!!r.deep,this.user=!!r.user,this.lazy=!!r.lazy,this.sync=!!r.sync,this.before=r.before):this.deep=this.user=this.lazy=this.sync=!1,this.cb=n,this.id=++kn,this.active=!0,this.post=!1,this.dirty=this.lazy,this.deps=[],this.newDeps=[],this.depIds=new it,this.newDepIds=new it,this.expression="",a(e)?this.getter=e:(this.getter=function(t){if(!z.test(t)){var e=t.split(".");return function(t){for(var n=0;ndocument.createEvent("Event").timeStamp&&(Wn=function(){return Kn.now()})}var qn=function(t,e){if(t.post){if(!e.post)return 1}else if(e.post)return-1;return t.id-e.id};function Gn(){var t,e;for(Hn=Wn(),Bn=!0,Rn.sort(qn),zn=0;znzn&&Rn[n].id>t.id;)n--;Rn.splice(n+1,0,t)}else Rn.push(t);Vn||(Vn=!0,an(Gn))}}function Jn(t,e){if(t){for(var n=Object.create(null),r=at?Reflect.ownKeys(t):Object.keys(t),o=0;o-1)if(i&&!g(o,"default"))s=!1;else if(""===s||s===O(t)){var u=yr(String,o.type);(u<0||c-1:"string"==typeof t?t.split(",").indexOf(n)>-1:(r=t,"[object RegExp]"===c.call(r)&&t.test(n));var r}function Ir(t,e){var n=t.cache,r=t.keys,o=t._vnode,i=t.$vnode;for(var a in n){var s=n[a];if(s){var c=s.name;c&&!e(c)&&Dr(n,a,r,o)}}i.componentOptions.children=void 0}function Dr(t,e,n,r){var o=t[e];!o||r&&o.tag===r.tag||o.componentInstance.$destroy(),t[e]=null,_(n,e)}!function(e){e.prototype._init=function(e){var n=this;n._uid=Sr++,n._isVue=!0,n.__v_skip=!0,n._scope=new ne(!0),n._scope.parent=void 0,n._scope._vm=!0,e&&e._isComponent?function(t,e){var n=t.$options=Object.create(t.constructor.options),r=e._parentVnode;n.parent=e.parent,n._parentVnode=r;var o=r.componentOptions;n.propsData=o.propsData,n._parentListeners=o.listeners,n._renderChildren=o.children,n._componentTag=o.tag,e.render&&(n.render=e.render,n.staticRenderFns=e.staticRenderFns)}(n,e):n.$options=dr(jr(n.constructor),e||{},n),n._renderProxy=n,n._self=n,function(t){var e=t.$options,n=e.parent;if(n&&!e.abstract){for(;n.$options.abstract&&n.$parent;)n=n.$parent;n.$children.push(t)}t.$parent=n,t.$root=n?n.$root:t,t.$children=[],t.$refs={},t._provided=n?n._provided:Object.create(null),t._watcher=null,t._inactive=null,t._directInactive=!1,t._isMounted=!1,t._isDestroyed=!1,t._isBeingDestroyed=!1}(n),function(t){t._events=Object.create(null),t._hasHookEvent=!1;var e=t.$options._parentListeners;e&&En(t,e)}(n),function(e){e._vnode=null,e._staticTrees=null;var n=e.$options,r=e.$vnode=n._parentVnode,o=r&&r.context;e.$slots=je(n._renderChildren,o),e.$scopedSlots=r?Ee(e.$parent,r.data.scopedSlots,e.$slots):t,e._c=function(t,n,r,o){return He(e,t,n,r,o,!1)},e.$createElement=function(t,n,r,o){return He(e,t,n,r,o,!0)};var i=r&&r.data;Tt(e,"$attrs",i&&i.attrs||t,null,!0),Tt(e,"$listeners",n._parentListeners||t,null,!0)}(n),Ln(n,"beforeCreate",void 0,!1),function(t){var e=Jn(t.$options.inject,t);e&&(Ot(!1),Object.keys(e).forEach((function(n){Tt(t,n,e[n])})),Ot(!0))}(n),wr(n),function(t){var e=t.$options.provide;if(e){var n=a(e)?e.call(t):e;if(!s(n))return;for(var r=oe(t),o=at?Reflect.ownKeys(n):Object.keys(n),i=0;i1?S(n):n;for(var r=S(arguments,1),o='event handler for "'.concat(t,'"'),i=0,a=n.length;iparseInt(this.max)&&Dr(e,n[0],n,this._vnode),this.vnodeToCache=null}}},created:function(){this.cache=Object.create(null),this.keys=[]},destroyed:function(){for(var t in this.cache)Dr(this.cache,t,this.keys)},mounted:function(){var t=this;this.cacheVNode(),this.$watch("include",(function(e){Ir(t,(function(t){return Pr(e,t)}))})),this.$watch("exclude",(function(e){Ir(t,(function(t){return!Pr(e,t)}))}))},updated:function(){this.cacheVNode()},render:function(){var t=this.$slots.default,e=Ve(t),n=e&&e.componentOptions;if(n){var r=Er(n),o=this.include,i=this.exclude;if(o&&(!r||!Pr(o,r))||i&&r&&Pr(i,r))return e;var a=this.cache,s=this.keys,c=null==e.key?n.Ctor.cid+(n.tag?"::".concat(n.tag):""):e.key;a[c]?(e.componentInstance=a[c].componentInstance,_(s,c),s.push(c)):(this.vnodeToCache=e,this.keyToCache=c),e.data.keepAlive=!0}return e||t&&t[0]}}};!function(t){var e={get:function(){return U}};Object.defineProperty(t,"config",e),t.util={warn:ir,extend:j,mergeOptions:dr,defineReactive:Tt},t.set=At,t.delete=Et,t.nextTick=an,t.observable=function(t){return jt(t),t},t.options=Object.create(null),R.forEach((function(e){t.options[e+"s"]=Object.create(null)})),t.options._base=t,j(t.options.components,Mr),function(t){t.use=function(t){var e=this._installedPlugins||(this._installedPlugins=[]);if(e.indexOf(t)>-1)return this;var n=S(arguments,1);return n.unshift(this),a(t.install)?t.install.apply(t,n):a(t)&&t.apply(null,n),e.push(t),this}}(t),function(t){t.mixin=function(t){return this.options=dr(this.options,t),this}}(t),Ar(t),function(t){R.forEach((function(e){t[e]=function(t,n){return n?("component"===e&&u(n)&&(n.name=n.name||t,n=this.options._base.extend(n)),"directive"===e&&a(n)&&(n={bind:n,update:n}),this.options[e+"s"][t]=n,n):this.options[e+"s"][t]}}))}(t)}(Tr),Object.defineProperty(Tr.prototype,"$isServer",{get:nt}),Object.defineProperty(Tr.prototype,"$ssrContext",{get:function(){return this.$vnode&&this.$vnode.ssrContext}}),Object.defineProperty(Tr,"FunctionalRenderContext",{value:Xn}),Tr.version=bn;var Lr=h("style,class"),Rr=h("input,textarea,option,select,progress"),Fr=h("contenteditable,draggable,spellcheck"),Ur=h("events,caret,typing,plaintext-only"),Vr=function(t,e){return Kr(e)||"false"===e?"false":"contenteditable"===t&&Ur(e)?e:"true"},Br=h("allowfullscreen,async,autofocus,autoplay,checked,compact,controls,declare,default,defaultchecked,defaultmuted,defaultselected,defer,disabled,enabled,formnovalidate,hidden,indeterminate,inert,ismap,itemscope,loop,multiple,muted,nohref,noresize,noshade,novalidate,nowrap,open,pauseonexit,readonly,required,reversed,scoped,seamless,selected,sortable,truespeed,typemustmatch,visible"),zr="http://www.w3.org/1999/xlink",Hr=function(t){return":"===t.charAt(5)&&"xlink"===t.slice(0,5)},Wr=function(t){return Hr(t)?t.slice(6,t.length):""},Kr=function(t){return null==t||!1===t};function qr(t){for(var e=t.data,n=t,o=t;r(o.componentInstance);)(o=o.componentInstance._vnode)&&o.data&&(e=Gr(o.data,e));for(;r(n=n.parent);)n&&n.data&&(e=Gr(e,n.data));return function(t,e){if(r(t)||r(e))return Zr(t,Jr(e));return""}(e.staticClass,e.class)}function Gr(t,e){return{staticClass:Zr(t.staticClass,e.staticClass),class:r(t.class)?[t.class,e.class]:e.class}}function Zr(t,e){return t?e?t+" "+e:t:e||""}function Jr(t){return Array.isArray(t)?function(t){for(var e,n="",o=0,i=t.length;o-1?wo(t,e,n):Br(e)?Kr(n)?t.removeAttribute(e):(n="allowfullscreen"===e&&"EMBED"===t.tagName?"true":e,t.setAttribute(e,n)):Fr(e)?t.setAttribute(e,Vr(e,n)):Hr(e)?Kr(n)?t.removeAttributeNS(zr,Wr(e)):t.setAttributeNS(zr,e,n):wo(t,e,n)}function wo(t,e,n){if(Kr(n))t.removeAttribute(e);else{if(q&&!G&&"TEXTAREA"===t.tagName&&"placeholder"===e&&""!==n&&!t.__ieph){var r=function(e){e.stopImmediatePropagation(),t.removeEventListener("input",r)};t.addEventListener("input",r),t.__ieph=!0}t.setAttribute(e,n)}}var $o={create:go,update:go};function Co(t,e){var o=e.elm,i=e.data,a=t.data;if(!(n(i.staticClass)&&n(i.class)&&(n(a)||n(a.staticClass)&&n(a.class)))){var s=qr(e),c=o._transitionClasses;r(c)&&(s=Zr(s,Jr(c))),s!==o._prevClass&&(o.setAttribute("class",s),o._prevClass=s)}}var xo,Oo={create:Co,update:Co},ko="__r",So="__c";function jo(t,e,n){var r=xo;return function o(){null!==e.apply(null,arguments)&&Eo(t,o,n,r)}}var To=Xe&&!(Q&&Number(Q[1])<=53);function Ao(t,e,n,r){if(To){var o=Hn,i=e;e=i._wrapper=function(t){if(t.target===t.currentTarget||t.timeStamp>=o||t.timeStamp<=0||t.target.ownerDocument!==document)return i.apply(this,arguments)}}xo.addEventListener(t,e,tt?{capture:n,passive:r}:n)}function Eo(t,e,n,r){(r||xo).removeEventListener(t,e._wrapper||e,n)}function Po(t,e){if(!n(t.data.on)||!n(e.data.on)){var o=e.data.on||{},i=t.data.on||{};xo=e.elm||t.elm,function(t){if(r(t[ko])){var e=q?"change":"input";t[e]=[].concat(t[ko],t[e]||[]),delete t[ko]}r(t[So])&&(t.change=[].concat(t[So],t.change||[]),delete t[So])}(o),se(o,i,Ao,Eo,jo,e.context),xo=void 0}}var Io,Do={create:Po,update:Po,destroy:function(t){return Po(t,so)}};function No(t,e){if(!n(t.data.domProps)||!n(e.data.domProps)){var i,a,s=e.elm,c=t.data.domProps||{},u=e.data.domProps||{};for(i in(r(u.__ob__)||o(u._v_attr_proxy))&&(u=e.data.domProps=j({},u)),c)i in u||(s[i]="");for(i in u){if(a=u[i],"textContent"===i||"innerHTML"===i){if(e.children&&(e.children.length=0),a===c[i])continue;1===s.childNodes.length&&s.removeChild(s.childNodes[0])}if("value"===i&&"PROGRESS"!==s.tagName){s._value=a;var f=n(a)?"":String(a);Mo(s,f)&&(s.value=f)}else if("innerHTML"===i&&Yr(s.tagName)&&n(s.innerHTML)){(Io=Io||document.createElement("div")).innerHTML="".concat(a,"");for(var l=Io.firstChild;s.firstChild;)s.removeChild(s.firstChild);for(;l.firstChild;)s.appendChild(l.firstChild)}else if(a!==c[i])try{s[i]=a}catch(t){}}}}function Mo(t,e){return!t.composing&&("OPTION"===t.tagName||function(t,e){var n=!0;try{n=document.activeElement!==t}catch(t){}return n&&t.value!==e}(t,e)||function(t,e){var n=t.value,o=t._vModifiers;if(r(o)){if(o.number)return v(n)!==v(e);if(o.trim)return n.trim()!==e.trim()}return n!==e}(t,e))}var Lo={create:No,update:No},Ro=b((function(t){var e={},n=/:(.+)/;return t.split(/;(?![^(]*\))/g).forEach((function(t){if(t){var r=t.split(n);r.length>1&&(e[r[0].trim()]=r[1].trim())}})),e}));function Fo(t){var e=Uo(t.style);return t.staticStyle?j(t.staticStyle,e):e}function Uo(t){return Array.isArray(t)?T(t):"string"==typeof t?Ro(t):t}var Vo,Bo=/^--/,zo=/\s*!important$/,Ho=function(t,e,n){if(Bo.test(e))t.style.setProperty(e,n);else if(zo.test(n))t.style.setProperty(O(e),n.replace(zo,""),"important");else{var r=Ko(e);if(Array.isArray(n))for(var o=0,i=n.length;o-1?e.split(Zo).forEach((function(e){return t.classList.add(e)})):t.classList.add(e);else{var n=" ".concat(t.getAttribute("class")||""," ");n.indexOf(" "+e+" ")<0&&t.setAttribute("class",(n+e).trim())}}function Xo(t,e){if(e&&(e=e.trim()))if(t.classList)e.indexOf(" ")>-1?e.split(Zo).forEach((function(e){return t.classList.remove(e)})):t.classList.remove(e),t.classList.length||t.removeAttribute("class");else{for(var n=" ".concat(t.getAttribute("class")||""," "),r=" "+e+" ";n.indexOf(r)>=0;)n=n.replace(r," ");(n=n.trim())?t.setAttribute("class",n):t.removeAttribute("class")}}function Qo(t){if(t){if("object"==typeof t){var e={};return!1!==t.css&&j(e,Yo(t.name||"v")),j(e,t),e}return"string"==typeof t?Yo(t):void 0}}var Yo=b((function(t){return{enterClass:"".concat(t,"-enter"),enterToClass:"".concat(t,"-enter-to"),enterActiveClass:"".concat(t,"-enter-active"),leaveClass:"".concat(t,"-leave"),leaveToClass:"".concat(t,"-leave-to"),leaveActiveClass:"".concat(t,"-leave-active")}})),ti=W&&!G,ei="transition",ni="animation",ri="transition",oi="transitionend",ii="animation",ai="animationend";ti&&(void 0===window.ontransitionend&&void 0!==window.onwebkittransitionend&&(ri="WebkitTransition",oi="webkitTransitionEnd"),void 0===window.onanimationend&&void 0!==window.onwebkitanimationend&&(ii="WebkitAnimation",ai="webkitAnimationEnd"));var si=W?window.requestAnimationFrame?window.requestAnimationFrame.bind(window):setTimeout:function(t){return t()};function ci(t){si((function(){si(t)}))}function ui(t,e){var n=t._transitionClasses||(t._transitionClasses=[]);n.indexOf(e)<0&&(n.push(e),Jo(t,e))}function fi(t,e){t._transitionClasses&&_(t._transitionClasses,e),Xo(t,e)}function li(t,e,n){var r=pi(t,e),o=r.type,i=r.timeout,a=r.propCount;if(!o)return n();var s=o===ei?oi:ai,c=0,u=function(){t.removeEventListener(s,f),n()},f=function(e){e.target===t&&++c>=a&&u()};setTimeout((function(){c0&&(n=ei,f=a,l=i.length):e===ni?u>0&&(n=ni,f=u,l=c.length):l=(n=(f=Math.max(a,u))>0?a>u?ei:ni:null)?n===ei?i.length:c.length:0,{type:n,timeout:f,propCount:l,hasTransform:n===ei&&di.test(r[ri+"Property"])}}function vi(t,e){for(;t.length1}function bi(t,e){!0!==e.data.show&&mi(e)}var wi=function(t){var a,s,c={},u=t.modules,f=t.nodeOps;for(a=0;av?b(t,n(o[_+1])?null:o[_+1].elm,o,p,_,i):p>_&&$(e,l,v)}(l,h,m,i,u):r(m)?(r(t.text)&&f.setTextContent(l,""),b(l,null,m,0,m.length-1,i)):r(h)?$(h,0,h.length-1):r(t.text)&&f.setTextContent(l,""):t.text!==e.text&&f.setTextContent(l,e.text),r(v)&&r(p=v.hook)&&r(p=p.postpatch)&&p(t,e)}}}function k(t,e,n){if(o(n)&&r(t.parent))t.parent.data.pendingInsert=e;else for(var i=0;i-1,a.selected!==i&&(a.selected=i);else if(I(ki(a),r))return void(t.selectedIndex!==s&&(t.selectedIndex=s));o||(t.selectedIndex=-1)}}function Oi(t,e){return e.every((function(e){return!I(e,t)}))}function ki(t){return"_value"in t?t._value:t.value}function Si(t){t.target.composing=!0}function ji(t){t.target.composing&&(t.target.composing=!1,Ti(t.target,"input"))}function Ti(t,e){var n=document.createEvent("HTMLEvents");n.initEvent(e,!0,!0),t.dispatchEvent(n)}function Ai(t){return!t.componentInstance||t.data&&t.data.transition?t:Ai(t.componentInstance._vnode)}var Ei={bind:function(t,e,n){var r=e.value,o=(n=Ai(n)).data&&n.data.transition,i=t.__vOriginalDisplay="none"===t.style.display?"":t.style.display;r&&o?(n.data.show=!0,mi(n,(function(){t.style.display=i}))):t.style.display=r?i:"none"},update:function(t,e,n){var r=e.value;!r!=!e.oldValue&&((n=Ai(n)).data&&n.data.transition?(n.data.show=!0,r?mi(n,(function(){t.style.display=t.__vOriginalDisplay})):_i(n,(function(){t.style.display="none"}))):t.style.display=r?t.__vOriginalDisplay:"none")},unbind:function(t,e,n,r,o){o||(t.style.display=t.__vOriginalDisplay)}},Pi={model:$i,show:Ei},Ii={name:String,appear:Boolean,css:Boolean,mode:String,type:String,enterClass:String,leaveClass:String,enterToClass:String,leaveToClass:String,enterActiveClass:String,leaveActiveClass:String,appearClass:String,appearActiveClass:String,appearToClass:String,duration:[Number,String,Object]};function Di(t){var e=t&&t.componentOptions;return e&&e.Ctor.options.abstract?Di(Ve(e.children)):t}function Ni(t){var e={},n=t.$options;for(var r in n.propsData)e[r]=t[r];var o=n._parentListeners;for(var r in o)e[$(r)]=o[r];return e}function Mi(t,e){if(/\d-keep-alive$/.test(e.tag))return t("keep-alive",{props:e.componentOptions.propsData})}var Li=function(t){return t.tag||Ae(t)},Ri=function(t){return"show"===t.name},Fi={name:"transition",props:Ii,abstract:!0,render:function(t){var e=this,n=this.$slots.default;if(n&&(n=n.filter(Li)).length){var r=this.mode,o=n[0];if(function(t){for(;t=t.parent;)if(t.data.transition)return!0}(this.$vnode))return o;var a=Di(o);if(!a)return o;if(this._leaving)return Mi(t,o);var s="__transition-".concat(this._uid,"-");a.key=null==a.key?a.isComment?s+"comment":s+a.tag:i(a.key)?0===String(a.key).indexOf(s)?a.key:s+a.key:a.key;var c=(a.data||(a.data={})).transition=Ni(this),u=this._vnode,f=Di(u);if(a.data.directives&&a.data.directives.some(Ri)&&(a.data.show=!0),f&&f.data&&!function(t,e){return e.key===t.key&&e.tag===t.tag}(a,f)&&!Ae(f)&&(!f.componentInstance||!f.componentInstance._vnode.isComment)){var l=f.data.transition=j({},c);if("out-in"===r)return this._leaving=!0,ce(l,"afterLeave",(function(){e._leaving=!1,e.$forceUpdate()})),Mi(t,o);if("in-out"===r){if(Ae(a))return u;var d,p=function(){d()};ce(c,"afterEnter",p),ce(c,"enterCancelled",p),ce(l,"delayLeave",(function(t){d=t}))}}return o}}},Ui=j({tag:String,moveClass:String},Ii);delete Ui.mode;var Vi={props:Ui,beforeMount:function(){var t=this,e=this._update;this._update=function(n,r){var o=In(t);t.__patch__(t._vnode,t.kept,!1,!0),t._vnode=t.kept,o(),e.call(t,n,r)}},render:function(t){for(var e=this.tag||this.$vnode.data.tag||"span",n=Object.create(null),r=this.prevChildren=this.children,o=this.$slots.default||[],i=this.children=[],a=Ni(this),s=0;s-1?eo[t]=e.constructor===window.HTMLUnknownElement||e.constructor===window.HTMLElement:eo[t]=/HTMLUnknownElement/.test(e.toString())},j(Tr.options.directives,Pi),j(Tr.options.components,Wi),Tr.prototype.__patch__=W?wi:A,Tr.prototype.$mount=function(t,e){return function(t,e,n){var r;t.$el=e,t.$options.render||(t.$options.render=ft),Ln(t,"beforeMount"),r=function(){t._update(t._render(),n)},new Sn(t,r,A,{before:function(){t._isMounted&&!t._isDestroyed&&Ln(t,"beforeUpdate")}},!0),n=!1;var o=t._preWatchers;if(o)for(var i=0;i - * @author owenm - * @license MIT - */ -function t(e){return(t="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(e)}function e(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}function n(){return n=Object.assign||function(t){for(var e=1;e=0||(i[n]=t[n]);return i}(t,e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(t);for(o=0;o=0||Object.prototype.propertyIsEnumerable.call(t,n)&&(i[n]=t[n])}return i}function r(t){if("undefined"!=typeof window&&window.navigator)return!!navigator.userAgent.match(t)}var a=r(/(?:Trident.*rv[ :]?11\.|msie|iemobile|Windows Phone)/i),l=r(/Edge/i),s=r(/firefox/i),c=r(/safari/i)&&!r(/chrome/i)&&!r(/android/i),d=r(/iP(ad|od|hone)/i),u=r(/chrome/i)&&r(/android/i),h={capture:!1,passive:!1};function f(t,e,n){t.addEventListener(e,n,!a&&h)}function p(t,e,n){t.removeEventListener(e,n,!a&&h)}function g(t,e){if(e){if(">"===e[0]&&(e=e.substring(1)),t)try{if(t.matches)return t.matches(e);if(t.msMatchesSelector)return t.msMatchesSelector(e);if(t.webkitMatchesSelector)return t.webkitMatchesSelector(e)}catch(n){return!1}return!1}}function m(t){return t.host&&t!==document&&t.host.nodeType?t.host:t.parentNode}function v(t,e,n,o){if(t){n=n||document;do{if(null!=e&&(">"===e[0]?t.parentNode===n&&g(t,e):g(t,e))||o&&t===n)return t;if(t===n)break}while(t=m(t))}return null}var b,y=/\s+/g;function w(t,e,n){if(t&&e)if(t.classList)t.classList[n?"add":"remove"](e);else{var o=(" "+t.className+" ").replace(y," ").replace(" "+e+" "," ");t.className=(o+(n?" "+e:"")).replace(y," ")}}function E(t,e,n){var o=t&&t.style;if(o){if(void 0===n)return document.defaultView&&document.defaultView.getComputedStyle?n=document.defaultView.getComputedStyle(t,""):t.currentStyle&&(n=t.currentStyle),void 0===e?n:n[e];e in o||-1!==e.indexOf("webkit")||(e="-webkit-"+e),o[e]=n+("string"==typeof n?"":"px")}}function D(t,e){var n="";if("string"==typeof t)n=t;else do{var o=E(t,"transform");o&&"none"!==o&&(n=o+" "+n)}while(!e&&(t=t.parentNode));var i=window.DOMMatrix||window.WebKitCSSMatrix||window.CSSMatrix||window.MSCSSMatrix;return i&&new i(n)}function _(t,e,n){if(t){var o=t.getElementsByTagName(e),i=0,r=o.length;if(n)for(;i=r:i<=r))return o;if(o===S())break;o=N(o,!1)}return!1}function T(t,e,n){for(var o=0,i=0,r=t.children;i2&&void 0!==arguments[2]?arguments[2]:{},r=n.evt,a=i(n,["evt"]);$.pluginEvent.bind(At)(t,e,o({dragEl:H,parentEl:W,ghostEl:V,rootEl:U,nextEl:z,lastDownEl:G,cloneEl:q,cloneHidden:J,dragStarted:ct,putSortable:nt,activeSortable:At.active,originalEvent:r,oldIndex:Z,oldDraggableIndex:Q,newIndex:K,newDraggableIndex:tt,hideGhostForTarget:It,unhideGhostForTarget:Mt,cloneNowHidden:function(){J=!0},cloneNowShown:function(){J=!1},dispatchSortableEvent:function(t){j({sortable:e,name:t,originalEvent:r})}},a))};function j(t){!function(t){var e=t.sortable,n=t.rootEl,i=t.name,r=t.targetEl,s=t.cloneEl,c=t.toEl,d=t.fromEl,u=t.oldIndex,h=t.newIndex,f=t.oldDraggableIndex,p=t.newDraggableIndex,g=t.originalEvent,m=t.putSortable,v=t.extraEventProperties;if(e=e||n&&n[R]){var b,y=e.options,w="on"+i.charAt(0).toUpperCase()+i.substr(1);!window.CustomEvent||a||l?(b=document.createEvent("Event")).initEvent(i,!0,!0):b=new CustomEvent(i,{bubbles:!0,cancelable:!0}),b.to=c||n,b.from=d||n,b.item=r||n,b.clone=s,b.oldIndex=u,b.newIndex=h,b.oldDraggableIndex=f,b.newDraggableIndex=p,b.originalEvent=g,b.pullMode=m?m.lastPutMode:void 0;var E=o({},v,$.getEventProperties(i,e));for(var D in E)b[D]=E[D];n&&n.dispatchEvent(b),y[w]&&y[w].call(e,b)}}(o({putSortable:nt,cloneEl:q,targetEl:H,rootEl:U,oldIndex:Z,oldDraggableIndex:Q,newIndex:K,newDraggableIndex:tt},t))}var H,W,V,U,z,G,q,J,Z,K,Q,tt,et,nt,ot,it,rt,at,lt,st,ct,dt,ut,ht,ft,pt=!1,gt=!1,mt=[],vt=!1,bt=!1,yt=[],wt=!1,Et=[],Dt="undefined"!=typeof document,_t=d,St=l||a?"cssFloat":"float",Ct=Dt&&!u&&!d&&"draggable"in document.createElement("div"),xt=function(){if(Dt){if(a)return!1;var t=document.createElement("x");return t.style.cssText="pointer-events:auto","auto"===t.style.pointerEvents}}(),Tt=function(t,e){var n=E(t),o=parseInt(n.width)-parseInt(n.paddingLeft)-parseInt(n.paddingRight)-parseInt(n.borderLeftWidth)-parseInt(n.borderRightWidth),i=T(t,0,e),r=T(t,1,e),a=i&&E(i),l=r&&E(r),s=a&&parseInt(a.marginLeft)+parseInt(a.marginRight)+C(i).width,c=l&&parseInt(l.marginLeft)+parseInt(l.marginRight)+C(r).width;if("flex"===n.display)return"column"===n.flexDirection||"column-reverse"===n.flexDirection?"vertical":"horizontal";if("grid"===n.display)return n.gridTemplateColumns.split(" ").length<=1?"vertical":"horizontal";if(i&&a.float&&"none"!==a.float){var d="left"===a.float?"left":"right";return!r||"both"!==l.clear&&l.clear!==d?"horizontal":"vertical"}return i&&("block"===a.display||"flex"===a.display||"table"===a.display||"grid"===a.display||s>=o&&"none"===n[St]||r&&"none"===n[St]&&s+c>o)?"vertical":"horizontal"},Ot=function(e){function n(t,e){return function(o,i,r,a){var l=o.options.group.name&&i.options.group.name&&o.options.group.name===i.options.group.name;if(null==t&&(e||l))return!0;if(null==t||!1===t)return!1;if(e&&"clone"===t)return t;if("function"==typeof t)return n(t(o,i,r,a),e)(o,i,r,a);var s=(e?o:i).options.group.name;return!0===t||"string"==typeof t&&t===s||t.join&&t.indexOf(s)>-1}}var o={},i=e.group;i&&"object"==t(i)||(i={name:i}),o.name=i.name,o.checkPull=n(i.pull,!0),o.checkPut=n(i.put),o.revertClone=i.revertClone,e.group=o},It=function(){!xt&&V&&E(V,"display","none")},Mt=function(){!xt&&V&&E(V,"display","")};Dt&&document.addEventListener("click",(function(t){if(gt)return t.preventDefault(),t.stopPropagation&&t.stopPropagation(),t.stopImmediatePropagation&&t.stopImmediatePropagation(),gt=!1,!1}),!0);var Nt=function(t){if(H){t=t.touches?t.touches[0]:t;var e=(i=t.clientX,r=t.clientY,mt.some((function(t){if(!O(t)){var e=C(t),n=t[R].options.emptyInsertThreshold,o=i>=e.left-n&&i<=e.right+n,l=r>=e.top-n&&r<=e.bottom+n;return n&&o&&l?a=t:void 0}})),a);if(e){var n={};for(var o in t)t.hasOwnProperty(o)&&(n[o]=t[o]);n.target=n.rootEl=e,n.preventDefault=void 0,n.stopPropagation=void 0,e[R]._onDragOver(n)}}var i,r,a},Pt=function(t){H&&H.parentNode[R]._isOutsideThisEl(t.target)};function At(t,e){if(!t||!t.nodeType||1!==t.nodeType)throw"Sortable: `el` must be an HTMLElement, not ".concat({}.toString.call(t));this.el=t,this.options=e=n({},e),t[R]=this;var o={group:null,sort:!0,disabled:!1,store:null,handle:null,draggable:/^[uo]l$/i.test(t.nodeName)?">li":">*",swapThreshold:1,invertSwap:!1,invertedSwapThreshold:null,removeCloneOnHide:!0,direction:function(){return Tt(t,this.options)},ghostClass:"sortable-ghost",chosenClass:"sortable-chosen",dragClass:"sortable-drag",ignore:"a, img",filter:null,preventOnFilter:!0,animation:0,easing:null,setData:function(t,e){t.setData("Text",e.textContent)},dropBubble:!1,dragoverBubble:!1,dataIdAttr:"data-id",delay:0,delayOnTouchOnly:!1,touchStartThreshold:(Number.parseInt?Number:window).parseInt(window.devicePixelRatio,10)||1,forceFallback:!1,fallbackClass:"sortable-fallback",fallbackOnBody:!1,fallbackTolerance:0,fallbackOffset:{x:0,y:0},supportPointer:!1!==At.supportPointer&&"PointerEvent"in window,emptyInsertThreshold:5};for(var i in $.initializePlugins(this,t,o),o)!(i in e)&&(e[i]=o[i]);for(var r in Ot(e),this)"_"===r.charAt(0)&&"function"==typeof this[r]&&(this[r]=this[r].bind(this));this.nativeDraggable=!e.forceFallback&&Ct,this.nativeDraggable&&(this.options.touchStartThreshold=1),e.supportPointer?f(t,"pointerdown",this._onTapStart):(f(t,"mousedown",this._onTapStart),f(t,"touchstart",this._onTapStart)),this.nativeDraggable&&(f(t,"dragover",this),f(t,"dragenter",this)),mt.push(this.el),e.store&&e.store.get&&this.sort(e.store.get(this)||[]),n(this,X())}function kt(t,e,n,o,i,r,s,c){var d,u,h=t[R],f=h.options.onMove;return!window.CustomEvent||a||l?(d=document.createEvent("Event")).initEvent("move",!0,!0):d=new CustomEvent("move",{bubbles:!0,cancelable:!0}),d.to=e,d.from=t,d.dragged=n,d.draggedRect=o,d.related=i||e,d.relatedRect=r||C(e),d.willInsertAfter=c,d.originalEvent=s,t.dispatchEvent(d),f&&(u=f.call(h,d,s)),u}function Ft(t){t.draggable=!1}function Rt(){wt=!1}function Xt(t){for(var e=t.tagName+t.className+t.src+t.href+t.textContent,n=e.length,o=0;n--;)o+=e.charCodeAt(n);return o.toString(36)}function Lt(t){return setTimeout(t,0)}function Yt(t){return clearTimeout(t)}At.prototype={constructor:At,_isOutsideThisEl:function(t){this.el.contains(t)||t===this.el||(dt=null)},_getDirection:function(t,e){return"function"==typeof this.options.direction?this.options.direction.call(this,t,e,H):this.options.direction},_onTapStart:function(t){if(t.cancelable){var e=this,n=this.el,o=this.options,i=o.preventOnFilter,r=t.type,a=t.touches&&t.touches[0]||t.pointerType&&"touch"===t.pointerType&&t,l=(a||t).target,s=t.target.shadowRoot&&(t.path&&t.path[0]||t.composedPath&&t.composedPath()[0])||l,c=o.filter;if(function(t){Et.length=0;var e=t.getElementsByTagName("input"),n=e.length;for(;n--;){var o=e[n];o.checked&&Et.push(o)}}(n),!H&&!(/mousedown|pointerdown/.test(r)&&0!==t.button||o.disabled||s.isContentEditable||(l=v(l,o.draggable,n,!1))&&l.animated||G===l)){if(Z=I(l),Q=I(l,o.draggable),"function"==typeof c){if(c.call(this,t,l,this))return j({sortable:e,rootEl:s,name:"filter",targetEl:l,toEl:n,fromEl:n}),B("filter",e,{evt:t}),void(i&&t.cancelable&&t.preventDefault())}else if(c&&(c=c.split(",").some((function(o){if(o=v(s,o.trim(),n,!1))return j({sortable:e,rootEl:o,name:"filter",targetEl:l,fromEl:n,toEl:n}),B("filter",e,{evt:t}),!0}))))return void(i&&t.cancelable&&t.preventDefault());o.handle&&!v(s,o.handle,n,!1)||this._prepareDragStart(t,a,l)}}},_prepareDragStart:function(t,e,n){var o,i=this,r=i.el,c=i.options,d=r.ownerDocument;if(n&&!H&&n.parentNode===r){var u=C(n);if(U=r,W=(H=n).parentNode,z=H.nextSibling,G=n,et=c.group,At.dragged=H,ot={target:H,clientX:(e||t).clientX,clientY:(e||t).clientY},lt=ot.clientX-u.left,st=ot.clientY-u.top,this._lastX=(e||t).clientX,this._lastY=(e||t).clientY,H.style["will-change"]="all",o=function(){B("delayEnded",i,{evt:t}),At.eventCanceled?i._onDrop():(i._disableDelayedDragEvents(),!s&&i.nativeDraggable&&(H.draggable=!0),i._triggerDragStart(t,e),j({sortable:i,name:"choose",originalEvent:t}),w(H,c.chosenClass,!0))},c.ignore.split(",").forEach((function(t){_(H,t.trim(),Ft)})),f(d,"dragover",Nt),f(d,"mousemove",Nt),f(d,"touchmove",Nt),f(d,"mouseup",i._onDrop),f(d,"touchend",i._onDrop),f(d,"touchcancel",i._onDrop),s&&this.nativeDraggable&&(this.options.touchStartThreshold=4,H.draggable=!0),B("delayStart",this,{evt:t}),!c.delay||c.delayOnTouchOnly&&!e||this.nativeDraggable&&(l||a))o();else{if(At.eventCanceled)return void this._onDrop();f(d,"mouseup",i._disableDelayedDrag),f(d,"touchend",i._disableDelayedDrag),f(d,"touchcancel",i._disableDelayedDrag),f(d,"mousemove",i._delayedDragTouchMoveHandler),f(d,"touchmove",i._delayedDragTouchMoveHandler),c.supportPointer&&f(d,"pointermove",i._delayedDragTouchMoveHandler),i._dragStartTimer=setTimeout(o,c.delay)}}},_delayedDragTouchMoveHandler:function(t){var e=t.touches?t.touches[0]:t;Math.max(Math.abs(e.clientX-this._lastX),Math.abs(e.clientY-this._lastY))>=Math.floor(this.options.touchStartThreshold/(this.nativeDraggable&&window.devicePixelRatio||1))&&this._disableDelayedDrag()},_disableDelayedDrag:function(){H&&Ft(H),clearTimeout(this._dragStartTimer),this._disableDelayedDragEvents()},_disableDelayedDragEvents:function(){var t=this.el.ownerDocument;p(t,"mouseup",this._disableDelayedDrag),p(t,"touchend",this._disableDelayedDrag),p(t,"touchcancel",this._disableDelayedDrag),p(t,"mousemove",this._delayedDragTouchMoveHandler),p(t,"touchmove",this._delayedDragTouchMoveHandler),p(t,"pointermove",this._delayedDragTouchMoveHandler)},_triggerDragStart:function(t,e){e=e||"touch"==t.pointerType&&t,!this.nativeDraggable||e?this.options.supportPointer?f(document,"pointermove",this._onTouchMove):f(document,e?"touchmove":"mousemove",this._onTouchMove):(f(H,"dragend",this),f(U,"dragstart",this._onDragStart));try{document.selection?Lt((function(){document.selection.empty()})):window.getSelection().removeAllRanges()}catch(n){}},_dragStarted:function(t,e){if(pt=!1,U&&H){B("dragStarted",this,{evt:e}),this.nativeDraggable&&f(document,"dragover",Pt);var n=this.options;!t&&w(H,n.dragClass,!1),w(H,n.ghostClass,!0),At.active=this,t&&this._appendGhost(),j({sortable:this,name:"start",originalEvent:e})}else this._nulling()},_emulateDragOver:function(){if(it){this._lastX=it.clientX,this._lastY=it.clientY,It();for(var t=document.elementFromPoint(it.clientX,it.clientY),e=t;t&&t.shadowRoot&&(t=t.shadowRoot.elementFromPoint(it.clientX,it.clientY))!==e;)e=t;if(H.parentNode[R]._isOutsideThisEl(t),e)do{if(e[R]){if(e[R]._onDragOver({clientX:it.clientX,clientY:it.clientY,target:t,rootEl:e})&&!this.options.dragoverBubble)break}t=e}while(e=e.parentNode);Mt()}},_onTouchMove:function(t){if(ot){var e=this.options,n=e.fallbackTolerance,o=e.fallbackOffset,i=t.touches?t.touches[0]:t,r=V&&D(V,!0),a=V&&r&&r.a,l=V&&r&&r.d,s=_t&&ft&&M(ft),c=(i.clientX-ot.clientX+o.x)/(a||1)+(s?s[0]-yt[0]:0)/(a||1),d=(i.clientY-ot.clientY+o.y)/(l||1)+(s?s[1]-yt[1]:0)/(l||1);if(!At.active&&!pt){if(n&&Math.max(Math.abs(i.clientX-this._lastX),Math.abs(i.clientY-this._lastY))o.right+i||t.clientX<=o.right&&t.clientY>o.bottom&&t.clientX>=o.left:t.clientX>o.right&&t.clientY>o.top||t.clientX<=o.right&&t.clientY>o.bottom+i}(t,r,this)&&!m.animated){if(m===H)return $(!1);if(m&&a===t.target&&(l=m),l&&(n=C(l)),!1!==kt(U,a,H,e,l,n,t,!!l))return Y(),a.appendChild(H),W=a,G(),$(!0)}else if(l.parentNode===a){n=C(l);var b,y,D,_=H.parentNode!==a,S=!function(t,e,n){var o=n?t.left:t.top,i=n?t.right:t.bottom,r=n?t.width:t.height,a=n?e.left:e.top,l=n?e.right:e.bottom,s=n?e.width:e.height;return o===a||i===l||o+r/2===a+s/2}(H.animated&&H.toRect||e,l.animated&&l.toRect||n,r),T=r?"top":"left",M=x(l,"top","top")||x(H,"top","top"),N=M?M.scrollTop:void 0;if(dt!==l&&(y=n[T],vt=!1,bt=!S&&s.invertSwap||_),b=function(t,e,n,o,i,r,a,l){var s=o?t.clientY:t.clientX,c=o?n.height:n.width,d=o?n.top:n.left,u=o?n.bottom:n.right,h=!1;if(!a)if(l&&htd+c*r/2:su-ht)return-ut}else if(s>d+c*(1-i)/2&&su-c*r/2))return s>d+c/2?1:-1;return 0}(t,l,n,r,S?1:s.swapThreshold,null==s.invertedSwapThreshold?s.swapThreshold:s.invertedSwapThreshold,bt,dt===l),0!==b){var P=I(H);do{P-=b,D=W.children[P]}while(D&&("none"===E(D,"display")||D===V))}if(0===b||D===l)return $(!1);dt=l,ut=b;var A=l.nextElementSibling,F=!1,X=kt(U,a,H,e,l,n,t,F=1===b);if(!1!==X)return 1!==X&&-1!==X||(F=1===X),wt=!0,setTimeout(Rt,30),Y(),F&&!A?a.appendChild(H):l.parentNode.insertBefore(H,F?A:l),M&&k(M,0,N-M.scrollTop),W=H.parentNode,void 0===y||bt||(ht=Math.abs(y-C(l)[T])),G(),$(!0)}if(a.contains(H))return $(!1)}return!1}function L(s,c){B(s,p,o({evt:t,isOwner:u,axis:r?"vertical":"horizontal",revert:i,dragRect:e,targetRect:n,canSort:h,fromSortable:f,target:l,completed:$,onMove:function(n,o){return kt(U,a,H,e,n,C(n),t,o)},changed:G},c))}function Y(){L("dragOverAnimationCapture"),p.captureAnimationState(),p!==f&&f.captureAnimationState()}function $(e){return L("dragOverCompleted",{insertion:e}),e&&(u?d._hideClone():d._showClone(p),p!==f&&(w(H,nt?nt.options.ghostClass:d.options.ghostClass,!1),w(H,s.ghostClass,!0)),nt!==p&&p!==At.active?nt=p:p===At.active&&nt&&(nt=null),f===p&&(p._ignoreWhileAnimating=l),p.animateAll((function(){L("dragOverAnimationComplete"),p._ignoreWhileAnimating=null})),p!==f&&(f.animateAll(),f._ignoreWhileAnimating=null)),(l===H&&!H.animated||l===a&&!l.animated)&&(dt=null),s.dragoverBubble||t.rootEl||l===document||(H.parentNode[R]._isOutsideThisEl(t.target),!e&&Nt(t)),!s.dragoverBubble&&t.stopPropagation&&t.stopPropagation(),g=!0}function G(){K=I(H),tt=I(H,s.draggable),j({sortable:p,name:"change",toEl:a,newIndex:K,newDraggableIndex:tt,originalEvent:t})}},_ignoreWhileAnimating:null,_offMoveEvents:function(){p(document,"mousemove",this._onTouchMove),p(document,"touchmove",this._onTouchMove),p(document,"pointermove",this._onTouchMove),p(document,"dragover",Nt),p(document,"mousemove",Nt),p(document,"touchmove",Nt)},_offUpEvents:function(){var t=this.el.ownerDocument;p(t,"mouseup",this._onDrop),p(t,"touchend",this._onDrop),p(t,"pointerup",this._onDrop),p(t,"touchcancel",this._onDrop),p(document,"selectstart",this)},_onDrop:function(t){var e=this.el,n=this.options;K=I(H),tt=I(H,n.draggable),B("drop",this,{evt:t}),W=H&&H.parentNode,K=I(H),tt=I(H,n.draggable),At.eventCanceled||(pt=!1,bt=!1,vt=!1,clearInterval(this._loopId),clearTimeout(this._dragStartTimer),Yt(this.cloneId),Yt(this._dragStartId),this.nativeDraggable&&(p(document,"drop",this),p(e,"dragstart",this._onDragStart)),this._offMoveEvents(),this._offUpEvents(),c&&E(document.body,"user-select",""),E(H,"transform",""),t&&(ct&&(t.cancelable&&t.preventDefault(),!n.dropBubble&&t.stopPropagation()),V&&V.parentNode&&V.parentNode.removeChild(V),(U===W||nt&&"clone"!==nt.lastPutMode)&&q&&q.parentNode&&q.parentNode.removeChild(q),H&&(this.nativeDraggable&&p(H,"dragend",this),Ft(H),H.style["will-change"]="",ct&&!pt&&w(H,nt?nt.options.ghostClass:this.options.ghostClass,!1),w(H,this.options.chosenClass,!1),j({sortable:this,name:"unchoose",toEl:W,newIndex:null,newDraggableIndex:null,originalEvent:t}),U!==W?(K>=0&&(j({rootEl:W,name:"add",toEl:W,fromEl:U,originalEvent:t}),j({sortable:this,name:"remove",toEl:W,originalEvent:t}),j({rootEl:W,name:"sort",toEl:W,fromEl:U,originalEvent:t}),j({sortable:this,name:"sort",toEl:W,originalEvent:t})),nt&&nt.save()):K!==Z&&K>=0&&(j({sortable:this,name:"update",toEl:W,originalEvent:t}),j({sortable:this,name:"sort",toEl:W,originalEvent:t})),At.active&&(null!=K&&-1!==K||(K=Z,tt=Q),j({sortable:this,name:"end",toEl:W,originalEvent:t}),this.save())))),this._nulling()},_nulling:function(){B("nulling",this),U=H=W=V=z=q=G=J=ot=it=ct=K=tt=Z=Q=dt=ut=nt=et=At.dragged=At.ghost=At.clone=At.active=null,Et.forEach((function(t){t.checked=!0})),Et.length=rt=at=0},handleEvent:function(t){switch(t.type){case"drop":case"dragend":this._onDrop(t);break;case"dragenter":case"dragover":H&&(this._onDragOver(t),function(t){t.dataTransfer&&(t.dataTransfer.dropEffect="move");t.cancelable&&t.preventDefault()}(t));break;case"selectstart":t.preventDefault()}},toArray:function(){for(var t,e=[],n=this.el.children,o=0,i=n.length,r=this.options;ot.replace(ee,((t,e)=>e?e.toUpperCase():""))));function oe(t){null!==t.parentElement&&t.parentElement.removeChild(t)}function ie(t,e,n){const o=0===n?t.children[0]:t.children[n-1].nextSibling;t.insertBefore(e,o)}function re(t,e){this.$nextTick((()=>this.$emit(t.toLowerCase(),e)))}function ae(t){return e=>{null!==this.realList&&this["onDrag"+t](e),re.call(this,t,e)}}function le(t){return["transition-group","TransitionGroup"].includes(t)}function se(t,e,n){return t[n]||(e[n]?e[n]():void 0)}const ce=["Start","Add","Remove","Update","End"],de=["Choose","Unchoose","Sort","Filter","Clone"],ue=["Move",...ce,...de].map((t=>"on"+t));var he=null;const fe={name:"draggable",inheritAttrs:!1,props:{options:Object,list:{type:Array,required:!1,default:null},value:{type:Array,required:!1,default:null},noTransitionOnDrag:{type:Boolean,default:!1},clone:{type:Function,default:t=>t},element:{type:String,default:"div"},tag:{type:String,default:null},move:{type:Function,default:null},componentData:{type:Object,required:!1,default:null}},data:()=>({transitionMode:!1,noneFunctionalComponentMode:!1}),render(t){const e=this.$slots.default;this.transitionMode=function(t){if(!t||1!==t.length)return!1;const[{componentOptions:e}]=t;return!!e&&le(e.tag)}(e);const{children:n,headerOffset:o,footerOffset:i}=function(t,e,n){let o=0,i=0;const r=se(e,n,"header");r&&(o=r.length,t=t?[...r,...t]:[...r]);const a=se(e,n,"footer");return a&&(i=a.length,t=t?[...t,...a]:[...a]),{children:t,headerOffset:o,footerOffset:i}}(e,this.$slots,this.$scopedSlots);this.headerOffset=o,this.footerOffset=i;const r=function(t,e){let n=null;const o=(t,e)=>{n=function(t,e,n){return void 0===n||((t=t||{})[e]=n),t}(n,t,e)};if(o("attrs",Object.keys(t).filter((t=>"id"===t||t.startsWith("data-"))).reduce(((e,n)=>(e[n]=t[n],e)),{})),!e)return n;const{on:i,props:r,attrs:a}=e;return o("on",i),o("props",r),Object.assign(n.attrs,a),n}(this.$attrs,this.componentData);return t(this.getTag(),r,n)},created(){null!==this.list&&null!==this.value&&te.error("Value and list props are mutually exclusive! Please set one or another."),"div"!==this.element&&te.warn("Element props is deprecated please use tag props instead. See https://github.com/SortableJS/Vue.Draggable/blob/master/documentation/migrate.md#element-props"),void 0!==this.options&&te.warn("Options props is deprecated, add sortable options directly as vue.draggable item, or use v-bind. See https://github.com/SortableJS/Vue.Draggable/blob/master/documentation/migrate.md#options-props")},mounted(){if(this.noneFunctionalComponentMode=this.getTag().toLowerCase()!==this.$el.nodeName.toLowerCase()&&!this.getIsFunctional(),this.noneFunctionalComponentMode&&this.transitionMode)throw new Error(`Transition-group inside component is not supported. Please alter tag value or remove transition-group. Current tag value: ${this.getTag()}`);const t={};ce.forEach((e=>{t["on"+e]=ae.call(this,e)})),de.forEach((e=>{t["on"+e]=re.bind(this,e)}));const e=Object.keys(this.$attrs).reduce(((t,e)=>(t[ne(e)]=this.$attrs[e],t)),{}),n=Object.assign({},this.options,e,t,{onMove:(t,e)=>this.onDragMove(t,e)});!("draggable"in n)&&(n.draggable=">*"),this._sortable=new At(this.rootContainer,n),this.computeIndexes()},beforeDestroy(){void 0!==this._sortable&&this._sortable.destroy()},computed:{rootContainer(){return this.transitionMode?this.$el.children[0]:this.$el},realList(){return this.list?this.list:this.value}},watch:{options:{handler(t){this.updateOptions(t)},deep:!0},$attrs:{handler(t){this.updateOptions(t)},deep:!0},realList(){this.computeIndexes()}},methods:{getIsFunctional(){const{fnOptions:t}=this._vnode;return t&&t.functional},getTag(){return this.tag||this.element},updateOptions(t){for(var e in t){const n=ne(e);-1===ue.indexOf(n)&&this._sortable.option(n,t[e])}},getChildrenNodes(){if(this.noneFunctionalComponentMode)return this.$children[0].$slots.default;const t=this.$slots.default;return this.transitionMode?t[0].child.$slots.default:t},computeIndexes(){this.$nextTick((()=>{this.visibleIndexes=function(t,e,n,o){if(!t)return[];const i=t.map((t=>t.elm)),r=e.length-o,a=[...e].map(((t,e)=>e>=r?i.length:i.indexOf(t)));return n?a.filter((t=>-1!==t)):a}(this.getChildrenNodes(),this.rootContainer.children,this.transitionMode,this.footerOffset)}))},getUnderlyingVm(t){const e=function(t,e){return t.map((t=>t.elm)).indexOf(e)}(this.getChildrenNodes()||[],t);if(-1===e)return null;return{index:e,element:this.realList[e]}},getUnderlyingPotencialDraggableComponent:({__vue__:t})=>t&&t.$options&&le(t.$options._componentTag)?t.$parent:!("realList"in t)&&1===t.$children.length&&"realList"in t.$children[0]?t.$children[0]:t,emitChanges(t){this.$nextTick((()=>{this.$emit("change",t)}))},alterList(t){if(this.list)return void t(this.list);const e=[...this.value];t(e),this.$emit("input",e)},spliceList(){this.alterList((t=>t.splice(...arguments)))},updatePosition(t,e){this.alterList((n=>n.splice(e,0,n.splice(t,1)[0])))},getRelatedContextFromMoveEvent({to:t,related:e}){const n=this.getUnderlyingPotencialDraggableComponent(t);if(!n)return{component:n};const o=n.realList,i={list:o,component:n};if(t!==e&&o&&n.getUnderlyingVm){const t=n.getUnderlyingVm(e);if(t)return Object.assign(t,i)}return i},getVmIndex(t){const e=this.visibleIndexes,n=e.length;return t>n-1?n:e[t]},getComponent(){return this.$slots.default[0].componentInstance},resetTransitionData(t){if(!this.noTransitionOnDrag||!this.transitionMode)return;this.getChildrenNodes()[t].data=null;const e=this.getComponent();e.children=[],e.kept=void 0},onDragStart(t){this.context=this.getUnderlyingVm(t.item),t.item._underlying_vm_=this.clone(this.context.element),he=t.item},onDragAdd(t){const e=t.item._underlying_vm_;if(void 0===e)return;oe(t.item);const n=this.getVmIndex(t.newIndex);this.spliceList(n,0,e),this.computeIndexes();const o={element:e,newIndex:n};this.emitChanges({added:o})},onDragRemove(t){if(ie(this.rootContainer,t.item,t.oldIndex),"clone"===t.pullMode)return void oe(t.clone);const e=this.context.index;this.spliceList(e,1);const n={element:this.context.element,oldIndex:e};this.resetTransitionData(e),this.emitChanges({removed:n})},onDragUpdate(t){oe(t.item),ie(t.from,t.item,t.oldIndex);const e=this.context.index,n=this.getVmIndex(t.newIndex);this.updatePosition(e,n);const o={element:this.context.element,oldIndex:e,newIndex:n};this.emitChanges({moved:o})},updateProperty(t,e){t.hasOwnProperty(e)&&(t[e]+=this.headerOffset)},computeFutureIndex(t,e){if(!t.element)return 0;const n=[...e.to.children].filter((t=>"none"!==t.style.display)),o=n.indexOf(e.related),i=t.component.getVmIndex(o);return-1!==n.indexOf(he)||!e.willInsertAfter?i:i+1},onDragMove(t,e){const n=this.move;if(!n||!this.realList)return!0;const o=this.getRelatedContextFromMoveEvent(t),i=this.context,r=this.computeFutureIndex(o,t);Object.assign(i,{futureIndex:r});return n(Object.assign({},t,{relatedContext:o,draggedContext:i}),e)},onDragEnd(){this.computeIndexes(),he=null}}};"undefined"!=typeof window&&"Vue"in window&&window.Vue.component("draggable",fe);export{fe as default}; diff --git a/kirby/panel/dist/js/vuedraggable.min.js b/kirby/panel/dist/js/vuedraggable.min.js new file mode 100644 index 0000000..68f3aaa --- /dev/null +++ b/kirby/panel/dist/js/vuedraggable.min.js @@ -0,0 +1,7 @@ +/**! + * Sortable 1.10.2 + * @author RubaXa + * @author owenm + * @license MIT + */ +function t(e){return(t="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(e)}function e(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}function n(){return n=Object.assign||function(t){for(var e=1;e=0||(i[n]=t[n]);return i}(t,e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(t);for(o=0;o=0||Object.prototype.propertyIsEnumerable.call(t,n)&&(i[n]=t[n])}return i}function r(t){if("undefined"!=typeof window&&window.navigator)return!!navigator.userAgent.match(t)}var a=r(/(?:Trident.*rv[ :]?11\.|msie|iemobile|Windows Phone)/i),l=r(/Edge/i),s=r(/firefox/i),c=r(/safari/i)&&!r(/chrome/i)&&!r(/android/i),d=r(/iP(ad|od|hone)/i),u=r(/chrome/i)&&r(/android/i),h={capture:!1,passive:!1};function f(t,e,n){t.addEventListener(e,n,!a&&h)}function p(t,e,n){t.removeEventListener(e,n,!a&&h)}function g(t,e){if(e){if(">"===e[0]&&(e=e.substring(1)),t)try{if(t.matches)return t.matches(e);if(t.msMatchesSelector)return t.msMatchesSelector(e);if(t.webkitMatchesSelector)return t.webkitMatchesSelector(e)}catch(n){return!1}return!1}}function m(t){return t.host&&t!==document&&t.host.nodeType?t.host:t.parentNode}function v(t,e,n,o){if(t){n=n||document;do{if(null!=e&&(">"===e[0]?t.parentNode===n&&g(t,e):g(t,e))||o&&t===n)return t;if(t===n)break}while(t=m(t))}return null}var b,y=/\s+/g;function w(t,e,n){if(t&&e)if(t.classList)t.classList[n?"add":"remove"](e);else{var o=(" "+t.className+" ").replace(y," ").replace(" "+e+" "," ");t.className=(o+(n?" "+e:"")).replace(y," ")}}function E(t,e,n){var o=t&&t.style;if(o){if(void 0===n)return document.defaultView&&document.defaultView.getComputedStyle?n=document.defaultView.getComputedStyle(t,""):t.currentStyle&&(n=t.currentStyle),void 0===e?n:n[e];e in o||-1!==e.indexOf("webkit")||(e="-webkit-"+e),o[e]=n+("string"==typeof n?"":"px")}}function D(t,e){var n="";if("string"==typeof t)n=t;else do{var o=E(t,"transform");o&&"none"!==o&&(n=o+" "+n)}while(!e&&(t=t.parentNode));var i=window.DOMMatrix||window.WebKitCSSMatrix||window.CSSMatrix||window.MSCSSMatrix;return i&&new i(n)}function _(t,e,n){if(t){var o=t.getElementsByTagName(e),i=0,r=o.length;if(n)for(;i=C(o)[n]))return o;if(o===S())break;o=N(o,!1)}return!1}function T(t,e,n){for(var o=0,i=0,r=t.children;i2&&void 0!==arguments[2]?arguments[2]:{},r=n.evt,a=i(n,["evt"]);$.pluginEvent.bind(At)(t,e,o({dragEl:H,parentEl:W,ghostEl:V,rootEl:U,nextEl:z,lastDownEl:G,cloneEl:q,cloneHidden:J,dragStarted:ct,putSortable:nt,activeSortable:At.active,originalEvent:r,oldIndex:Z,oldDraggableIndex:Q,newIndex:K,newDraggableIndex:tt,hideGhostForTarget:It,unhideGhostForTarget:Mt,cloneNowHidden:function(){J=!0},cloneNowShown:function(){J=!1},dispatchSortableEvent:function(t){j({sortable:e,name:t,originalEvent:r})}},a))};function j(t){!function(t){var e=t.sortable,n=t.rootEl,i=t.name,r=t.targetEl,s=t.cloneEl,c=t.toEl,d=t.fromEl,u=t.oldIndex,h=t.newIndex,f=t.oldDraggableIndex,p=t.newDraggableIndex,g=t.originalEvent,m=t.putSortable,v=t.extraEventProperties;if(e=e||n&&n[R]){var b,y=e.options,w="on"+i.charAt(0).toUpperCase()+i.substr(1);!window.CustomEvent||a||l?(b=document.createEvent("Event")).initEvent(i,!0,!0):b=new CustomEvent(i,{bubbles:!0,cancelable:!0}),b.to=c||n,b.from=d||n,b.item=r||n,b.clone=s,b.oldIndex=u,b.newIndex=h,b.oldDraggableIndex=f,b.newDraggableIndex=p,b.originalEvent=g,b.pullMode=m?m.lastPutMode:void 0;var E=o({},v,$.getEventProperties(i,e));for(var D in E)b[D]=E[D];n&&n.dispatchEvent(b),y[w]&&y[w].call(e,b)}}(o({putSortable:nt,cloneEl:q,targetEl:H,rootEl:U,oldIndex:Z,oldDraggableIndex:Q,newIndex:K,newDraggableIndex:tt},t))}var H,W,V,U,z,G,q,J,Z,K,Q,tt,et,nt,ot,it,rt,at,lt,st,ct,dt,ut,ht,ft,pt=!1,gt=!1,mt=[],vt=!1,bt=!1,yt=[],wt=!1,Et=[],Dt="undefined"!=typeof document,_t=d,St=l||a?"cssFloat":"float",Ct=Dt&&!u&&!d&&"draggable"in document.createElement("div"),xt=function(){if(Dt){if(a)return!1;var t=document.createElement("x");return t.style.cssText="pointer-events:auto","auto"===t.style.pointerEvents}}(),Tt=function(t,e){var n=E(t),o=parseInt(n.width)-parseInt(n.paddingLeft)-parseInt(n.paddingRight)-parseInt(n.borderLeftWidth)-parseInt(n.borderRightWidth),i=T(t,0,e),r=T(t,1,e),a=i&&E(i),l=r&&E(r),s=a&&parseInt(a.marginLeft)+parseInt(a.marginRight)+C(i).width,c=l&&parseInt(l.marginLeft)+parseInt(l.marginRight)+C(r).width;if("flex"===n.display)return"column"===n.flexDirection||"column-reverse"===n.flexDirection?"vertical":"horizontal";if("grid"===n.display)return n.gridTemplateColumns.split(" ").length<=1?"vertical":"horizontal";if(i&&a.float&&"none"!==a.float){var d="left"===a.float?"left":"right";return!r||"both"!==l.clear&&l.clear!==d?"horizontal":"vertical"}return i&&("block"===a.display||"flex"===a.display||"table"===a.display||"grid"===a.display||s>=o&&"none"===n[St]||r&&"none"===n[St]&&s+c>o)?"vertical":"horizontal"},Ot=function(e){function n(t,e){return function(o,i,r,a){var l=o.options.group.name&&i.options.group.name&&o.options.group.name===i.options.group.name;if(null==t&&(e||l))return!0;if(null==t||!1===t)return!1;if(e&&"clone"===t)return t;if("function"==typeof t)return n(t(o,i,r,a),e)(o,i,r,a);var s=(e?o:i).options.group.name;return!0===t||"string"==typeof t&&t===s||t.join&&t.indexOf(s)>-1}}var o={},i=e.group;i&&"object"==t(i)||(i={name:i}),o.name=i.name,o.checkPull=n(i.pull,!0),o.checkPut=n(i.put),o.revertClone=i.revertClone,e.group=o},It=function(){!xt&&V&&E(V,"display","none")},Mt=function(){!xt&&V&&E(V,"display","")};Dt&&document.addEventListener("click",(function(t){if(gt)return t.preventDefault(),t.stopPropagation&&t.stopPropagation(),t.stopImmediatePropagation&&t.stopImmediatePropagation(),gt=!1,!1}),!0);var Nt=function(t){if(H){t=t.touches?t.touches[0]:t;var e=(i=t.clientX,r=t.clientY,mt.some((function(t){if(!O(t)){var e=C(t),n=t[R].options.emptyInsertThreshold,o=i>=e.left-n&&i<=e.right+n,l=r>=e.top-n&&r<=e.bottom+n;return n&&o&&l?a=t:void 0}})),a);if(e){var n={};for(var o in t)t.hasOwnProperty(o)&&(n[o]=t[o]);n.target=n.rootEl=e,n.preventDefault=void 0,n.stopPropagation=void 0,e[R]._onDragOver(n)}}var i,r,a},Pt=function(t){H&&H.parentNode[R]._isOutsideThisEl(t.target)};function At(t,e){if(!t||!t.nodeType||1!==t.nodeType)throw"Sortable: `el` must be an HTMLElement, not ".concat({}.toString.call(t));this.el=t,this.options=e=n({},e),t[R]=this;var o={group:null,sort:!0,disabled:!1,store:null,handle:null,draggable:/^[uo]l$/i.test(t.nodeName)?">li":">*",swapThreshold:1,invertSwap:!1,invertedSwapThreshold:null,removeCloneOnHide:!0,direction:function(){return Tt(t,this.options)},ghostClass:"sortable-ghost",chosenClass:"sortable-chosen",dragClass:"sortable-drag",ignore:"a, img",filter:null,preventOnFilter:!0,animation:0,easing:null,setData:function(t,e){t.setData("Text",e.textContent)},dropBubble:!1,dragoverBubble:!1,dataIdAttr:"data-id",delay:0,delayOnTouchOnly:!1,touchStartThreshold:(Number.parseInt?Number:window).parseInt(window.devicePixelRatio,10)||1,forceFallback:!1,fallbackClass:"sortable-fallback",fallbackOnBody:!1,fallbackTolerance:0,fallbackOffset:{x:0,y:0},supportPointer:!1!==At.supportPointer&&"PointerEvent"in window,emptyInsertThreshold:5};for(var i in $.initializePlugins(this,t,o),o)!(i in e)&&(e[i]=o[i]);for(var r in Ot(e),this)"_"===r.charAt(0)&&"function"==typeof this[r]&&(this[r]=this[r].bind(this));this.nativeDraggable=!e.forceFallback&&Ct,this.nativeDraggable&&(this.options.touchStartThreshold=1),e.supportPointer?f(t,"pointerdown",this._onTapStart):(f(t,"mousedown",this._onTapStart),f(t,"touchstart",this._onTapStart)),this.nativeDraggable&&(f(t,"dragover",this),f(t,"dragenter",this)),mt.push(this.el),e.store&&e.store.get&&this.sort(e.store.get(this)||[]),n(this,X())}function kt(t,e,n,o,i,r,s,c){var d,u,h=t[R],f=h.options.onMove;return!window.CustomEvent||a||l?(d=document.createEvent("Event")).initEvent("move",!0,!0):d=new CustomEvent("move",{bubbles:!0,cancelable:!0}),d.to=e,d.from=t,d.dragged=n,d.draggedRect=o,d.related=i||e,d.relatedRect=r||C(e),d.willInsertAfter=c,d.originalEvent=s,t.dispatchEvent(d),f&&(u=f.call(h,d,s)),u}function Ft(t){t.draggable=!1}function Rt(){wt=!1}function Xt(t){for(var e=t.tagName+t.className+t.src+t.href+t.textContent,n=e.length,o=0;n--;)o+=e.charCodeAt(n);return o.toString(36)}function Lt(t){return setTimeout(t,0)}function Yt(t){return clearTimeout(t)}At.prototype={constructor:At,_isOutsideThisEl:function(t){this.el.contains(t)||t===this.el||(dt=null)},_getDirection:function(t,e){return"function"==typeof this.options.direction?this.options.direction.call(this,t,e,H):this.options.direction},_onTapStart:function(t){if(t.cancelable){var e=this,n=this.el,o=this.options,i=o.preventOnFilter,r=t.type,a=t.touches&&t.touches[0]||t.pointerType&&"touch"===t.pointerType&&t,l=(a||t).target,s=t.target.shadowRoot&&(t.path&&t.path[0]||t.composedPath&&t.composedPath()[0])||l,c=o.filter;if(function(t){Et.length=0;var e=t.getElementsByTagName("input"),n=e.length;for(;n--;){var o=e[n];o.checked&&Et.push(o)}}(n),!H&&!(/mousedown|pointerdown/.test(r)&&0!==t.button||o.disabled||s.isContentEditable||(l=v(l,o.draggable,n,!1))&&l.animated||G===l)){if(Z=I(l),Q=I(l,o.draggable),"function"==typeof c){if(c.call(this,t,l,this))return j({sortable:e,rootEl:s,name:"filter",targetEl:l,toEl:n,fromEl:n}),B("filter",e,{evt:t}),void(i&&t.cancelable&&t.preventDefault())}else if(c&&(c=c.split(",").some((function(o){if(o=v(s,o.trim(),n,!1))return j({sortable:e,rootEl:o,name:"filter",targetEl:l,fromEl:n,toEl:n}),B("filter",e,{evt:t}),!0}))))return void(i&&t.cancelable&&t.preventDefault());o.handle&&!v(s,o.handle,n,!1)||this._prepareDragStart(t,a,l)}}},_prepareDragStart:function(t,e,n){var o,i=this,r=i.el,c=i.options,d=r.ownerDocument;if(n&&!H&&n.parentNode===r){var u=C(n);if(U=r,W=(H=n).parentNode,z=H.nextSibling,G=n,et=c.group,At.dragged=H,ot={target:H,clientX:(e||t).clientX,clientY:(e||t).clientY},lt=ot.clientX-u.left,st=ot.clientY-u.top,this._lastX=(e||t).clientX,this._lastY=(e||t).clientY,H.style["will-change"]="all",o=function(){B("delayEnded",i,{evt:t}),At.eventCanceled?i._onDrop():(i._disableDelayedDragEvents(),!s&&i.nativeDraggable&&(H.draggable=!0),i._triggerDragStart(t,e),j({sortable:i,name:"choose",originalEvent:t}),w(H,c.chosenClass,!0))},c.ignore.split(",").forEach((function(t){_(H,t.trim(),Ft)})),f(d,"dragover",Nt),f(d,"mousemove",Nt),f(d,"touchmove",Nt),f(d,"mouseup",i._onDrop),f(d,"touchend",i._onDrop),f(d,"touchcancel",i._onDrop),s&&this.nativeDraggable&&(this.options.touchStartThreshold=4,H.draggable=!0),B("delayStart",this,{evt:t}),!c.delay||c.delayOnTouchOnly&&!e||this.nativeDraggable&&(l||a))o();else{if(At.eventCanceled)return void this._onDrop();f(d,"mouseup",i._disableDelayedDrag),f(d,"touchend",i._disableDelayedDrag),f(d,"touchcancel",i._disableDelayedDrag),f(d,"mousemove",i._delayedDragTouchMoveHandler),f(d,"touchmove",i._delayedDragTouchMoveHandler),c.supportPointer&&f(d,"pointermove",i._delayedDragTouchMoveHandler),i._dragStartTimer=setTimeout(o,c.delay)}}},_delayedDragTouchMoveHandler:function(t){var e=t.touches?t.touches[0]:t;Math.max(Math.abs(e.clientX-this._lastX),Math.abs(e.clientY-this._lastY))>=Math.floor(this.options.touchStartThreshold/(this.nativeDraggable&&window.devicePixelRatio||1))&&this._disableDelayedDrag()},_disableDelayedDrag:function(){H&&Ft(H),clearTimeout(this._dragStartTimer),this._disableDelayedDragEvents()},_disableDelayedDragEvents:function(){var t=this.el.ownerDocument;p(t,"mouseup",this._disableDelayedDrag),p(t,"touchend",this._disableDelayedDrag),p(t,"touchcancel",this._disableDelayedDrag),p(t,"mousemove",this._delayedDragTouchMoveHandler),p(t,"touchmove",this._delayedDragTouchMoveHandler),p(t,"pointermove",this._delayedDragTouchMoveHandler)},_triggerDragStart:function(t,e){e=e||"touch"==t.pointerType&&t,!this.nativeDraggable||e?this.options.supportPointer?f(document,"pointermove",this._onTouchMove):f(document,e?"touchmove":"mousemove",this._onTouchMove):(f(H,"dragend",this),f(U,"dragstart",this._onDragStart));try{document.selection?Lt((function(){document.selection.empty()})):window.getSelection().removeAllRanges()}catch(n){}},_dragStarted:function(t,e){if(pt=!1,U&&H){B("dragStarted",this,{evt:e}),this.nativeDraggable&&f(document,"dragover",Pt);var n=this.options;!t&&w(H,n.dragClass,!1),w(H,n.ghostClass,!0),At.active=this,t&&this._appendGhost(),j({sortable:this,name:"start",originalEvent:e})}else this._nulling()},_emulateDragOver:function(){if(it){this._lastX=it.clientX,this._lastY=it.clientY,It();for(var t=document.elementFromPoint(it.clientX,it.clientY),e=t;t&&t.shadowRoot&&(t=t.shadowRoot.elementFromPoint(it.clientX,it.clientY))!==e;)e=t;if(H.parentNode[R]._isOutsideThisEl(t),e)do{if(e[R]){if(e[R]._onDragOver({clientX:it.clientX,clientY:it.clientY,target:t,rootEl:e})&&!this.options.dragoverBubble)break}t=e}while(e=e.parentNode);Mt()}},_onTouchMove:function(t){if(ot){var e=this.options,n=e.fallbackTolerance,o=e.fallbackOffset,i=t.touches?t.touches[0]:t,r=V&&D(V,!0),a=V&&r&&r.a,l=V&&r&&r.d,s=_t&&ft&&M(ft),c=(i.clientX-ot.clientX+o.x)/(a||1)+(s?s[0]-yt[0]:0)/(a||1),d=(i.clientY-ot.clientY+o.y)/(l||1)+(s?s[1]-yt[1]:0)/(l||1);if(!At.active&&!pt){if(n&&Math.max(Math.abs(i.clientX-this._lastX),Math.abs(i.clientY-this._lastY))o.right+i||t.clientX<=o.right&&t.clientY>o.bottom&&t.clientX>=o.left:t.clientX>o.right&&t.clientY>o.top||t.clientX<=o.right&&t.clientY>o.bottom+i}(t,r,this)&&!m.animated){if(m===H)return $(!1);if(m&&a===t.target&&(l=m),l&&(n=C(l)),!1!==kt(U,a,H,e,l,n,t,!!l))return Y(),a.appendChild(H),W=a,G(),$(!0)}else if(l.parentNode===a){n=C(l);var b,y,D,_=H.parentNode!==a,S=!function(t,e,n){var o=n?t.left:t.top,i=n?t.right:t.bottom,r=n?t.width:t.height,a=n?e.left:e.top,l=n?e.right:e.bottom,s=n?e.width:e.height;return o===a||i===l||o+r/2===a+s/2}(H.animated&&H.toRect||e,l.animated&&l.toRect||n,r),T=r?"top":"left",M=x(l,"top","top")||x(H,"top","top"),N=M?M.scrollTop:void 0;if(dt!==l&&(y=n[T],vt=!1,bt=!S&&s.invertSwap||_),b=function(t,e,n,o,i,r,a,l){var s=o?t.clientY:t.clientX,c=o?n.height:n.width,d=o?n.top:n.left,u=o?n.bottom:n.right,h=!1;if(!a)if(l&&htd+c*r/2:su-ht)return-ut}else if(s>d+c*(1-i)/2&&su-c*r/2))return s>d+c/2?1:-1;return 0}(t,l,n,r,S?1:s.swapThreshold,null==s.invertedSwapThreshold?s.swapThreshold:s.invertedSwapThreshold,bt,dt===l),0!==b){var P=I(H);do{P-=b,D=W.children[P]}while(D&&("none"===E(D,"display")||D===V))}if(0===b||D===l)return $(!1);dt=l,ut=b;var A=l.nextElementSibling,F=!1,X=kt(U,a,H,e,l,n,t,F=1===b);if(!1!==X)return 1!==X&&-1!==X||(F=1===X),wt=!0,setTimeout(Rt,30),Y(),F&&!A?a.appendChild(H):l.parentNode.insertBefore(H,F?A:l),M&&k(M,0,N-M.scrollTop),W=H.parentNode,void 0===y||bt||(ht=Math.abs(y-C(l)[T])),G(),$(!0)}if(a.contains(H))return $(!1)}return!1}function L(s,c){B(s,p,o({evt:t,isOwner:u,axis:r?"vertical":"horizontal",revert:i,dragRect:e,targetRect:n,canSort:h,fromSortable:f,target:l,completed:$,onMove:function(n,o){return kt(U,a,H,e,n,C(n),t,o)},changed:G},c))}function Y(){L("dragOverAnimationCapture"),p.captureAnimationState(),p!==f&&f.captureAnimationState()}function $(e){return L("dragOverCompleted",{insertion:e}),e&&(u?d._hideClone():d._showClone(p),p!==f&&(w(H,nt?nt.options.ghostClass:d.options.ghostClass,!1),w(H,s.ghostClass,!0)),nt!==p&&p!==At.active?nt=p:p===At.active&&nt&&(nt=null),f===p&&(p._ignoreWhileAnimating=l),p.animateAll((function(){L("dragOverAnimationComplete"),p._ignoreWhileAnimating=null})),p!==f&&(f.animateAll(),f._ignoreWhileAnimating=null)),(l===H&&!H.animated||l===a&&!l.animated)&&(dt=null),s.dragoverBubble||t.rootEl||l===document||(H.parentNode[R]._isOutsideThisEl(t.target),!e&&Nt(t)),!s.dragoverBubble&&t.stopPropagation&&t.stopPropagation(),g=!0}function G(){K=I(H),tt=I(H,s.draggable),j({sortable:p,name:"change",toEl:a,newIndex:K,newDraggableIndex:tt,originalEvent:t})}},_ignoreWhileAnimating:null,_offMoveEvents:function(){p(document,"mousemove",this._onTouchMove),p(document,"touchmove",this._onTouchMove),p(document,"pointermove",this._onTouchMove),p(document,"dragover",Nt),p(document,"mousemove",Nt),p(document,"touchmove",Nt)},_offUpEvents:function(){var t=this.el.ownerDocument;p(t,"mouseup",this._onDrop),p(t,"touchend",this._onDrop),p(t,"pointerup",this._onDrop),p(t,"touchcancel",this._onDrop),p(document,"selectstart",this)},_onDrop:function(t){var e=this.el,n=this.options;K=I(H),tt=I(H,n.draggable),B("drop",this,{evt:t}),W=H&&H.parentNode,K=I(H),tt=I(H,n.draggable),At.eventCanceled||(pt=!1,bt=!1,vt=!1,clearInterval(this._loopId),clearTimeout(this._dragStartTimer),Yt(this.cloneId),Yt(this._dragStartId),this.nativeDraggable&&(p(document,"drop",this),p(e,"dragstart",this._onDragStart)),this._offMoveEvents(),this._offUpEvents(),c&&E(document.body,"user-select",""),E(H,"transform",""),t&&(ct&&(t.cancelable&&t.preventDefault(),!n.dropBubble&&t.stopPropagation()),V&&V.parentNode&&V.parentNode.removeChild(V),(U===W||nt&&"clone"!==nt.lastPutMode)&&q&&q.parentNode&&q.parentNode.removeChild(q),H&&(this.nativeDraggable&&p(H,"dragend",this),Ft(H),H.style["will-change"]="",ct&&!pt&&w(H,nt?nt.options.ghostClass:this.options.ghostClass,!1),w(H,this.options.chosenClass,!1),j({sortable:this,name:"unchoose",toEl:W,newIndex:null,newDraggableIndex:null,originalEvent:t}),U!==W?(K>=0&&(j({rootEl:W,name:"add",toEl:W,fromEl:U,originalEvent:t}),j({sortable:this,name:"remove",toEl:W,originalEvent:t}),j({rootEl:W,name:"sort",toEl:W,fromEl:U,originalEvent:t}),j({sortable:this,name:"sort",toEl:W,originalEvent:t})),nt&&nt.save()):K!==Z&&K>=0&&(j({sortable:this,name:"update",toEl:W,originalEvent:t}),j({sortable:this,name:"sort",toEl:W,originalEvent:t})),At.active&&(null!=K&&-1!==K||(K=Z,tt=Q),j({sortable:this,name:"end",toEl:W,originalEvent:t}),this.save())))),this._nulling()},_nulling:function(){B("nulling",this),U=H=W=V=z=q=G=J=ot=it=ct=K=tt=Z=Q=dt=ut=nt=et=At.dragged=At.ghost=At.clone=At.active=null,Et.forEach((function(t){t.checked=!0})),Et.length=rt=at=0},handleEvent:function(t){switch(t.type){case"drop":case"dragend":this._onDrop(t);break;case"dragenter":case"dragover":H&&(this._onDragOver(t),function(t){t.dataTransfer&&(t.dataTransfer.dropEffect="move");t.cancelable&&t.preventDefault()}(t));break;case"selectstart":t.preventDefault()}},toArray:function(){for(var t,e=[],n=this.el.children,o=0,i=n.length,r=this.options;ot.replace(ee,((t,e)=>e?e.toUpperCase():""))));function oe(t){null!==t.parentElement&&t.parentElement.removeChild(t)}function ie(t,e,n){const o=0===n?t.children[0]:t.children[n-1].nextSibling;t.insertBefore(e,o)}function re(t,e){this.$nextTick((()=>this.$emit(t.toLowerCase(),e)))}function ae(t){return e=>{null!==this.realList&&this["onDrag"+t](e),re.call(this,t,e)}}function le(t){return["transition-group","TransitionGroup"].includes(t)}function se(t,e,n){return t[n]||(e[n]?e[n]():void 0)}const ce=["Start","Add","Remove","Update","End"],de=["Choose","Unchoose","Sort","Filter","Clone"],ue=["Move",...ce,...de].map((t=>"on"+t));var he=null;const fe={name:"draggable",inheritAttrs:!1,props:{options:Object,list:{type:Array,required:!1,default:null},value:{type:Array,required:!1,default:null},noTransitionOnDrag:{type:Boolean,default:!1},clone:{type:Function,default:t=>t},element:{type:String,default:"div"},tag:{type:String,default:null},move:{type:Function,default:null},componentData:{type:Object,required:!1,default:null}},data:()=>({transitionMode:!1,noneFunctionalComponentMode:!1}),render(t){const e=this.$slots.default;this.transitionMode=function(t){if(!t||1!==t.length)return!1;const[{componentOptions:e}]=t;return!!e&&le(e.tag)}(e);const{children:n,headerOffset:o,footerOffset:i}=function(t,e,n){let o=0,i=0;const r=se(e,n,"header");r&&(o=r.length,t=t?[...r,...t]:[...r]);const a=se(e,n,"footer");return a&&(i=a.length,t=t?[...t,...a]:[...a]),{children:t,headerOffset:o,footerOffset:i}}(e,this.$slots,this.$scopedSlots);this.headerOffset=o,this.footerOffset=i;const r=function(t,e){let n=null;const o=(t,e)=>{n=function(t,e,n){return void 0===n||((t=t||{})[e]=n),t}(n,t,e)};if(o("attrs",Object.keys(t).filter((t=>"id"===t||t.startsWith("data-"))).reduce(((e,n)=>(e[n]=t[n],e)),{})),!e)return n;const{on:i,props:r,attrs:a}=e;return o("on",i),o("props",r),Object.assign(n.attrs,a),n}(this.$attrs,this.componentData);return t(this.getTag(),r,n)},created(){null!==this.list&&null!==this.value&&te.error("Value and list props are mutually exclusive! Please set one or another."),"div"!==this.element&&te.warn("Element props is deprecated please use tag props instead. See https://github.com/SortableJS/Vue.Draggable/blob/master/documentation/migrate.md#element-props"),void 0!==this.options&&te.warn("Options props is deprecated, add sortable options directly as vue.draggable item, or use v-bind. See https://github.com/SortableJS/Vue.Draggable/blob/master/documentation/migrate.md#options-props")},mounted(){if(this.noneFunctionalComponentMode=this.getTag().toLowerCase()!==this.$el.nodeName.toLowerCase()&&!this.getIsFunctional(),this.noneFunctionalComponentMode&&this.transitionMode)throw new Error(`Transition-group inside component is not supported. Please alter tag value or remove transition-group. Current tag value: ${this.getTag()}`);const t={};ce.forEach((e=>{t["on"+e]=ae.call(this,e)})),de.forEach((e=>{t["on"+e]=re.bind(this,e)}));const e=Object.keys(this.$attrs).reduce(((t,e)=>(t[ne(e)]=this.$attrs[e],t)),{}),n=Object.assign({},this.options,e,t,{onMove:(t,e)=>this.onDragMove(t,e)});!("draggable"in n)&&(n.draggable=">*"),this._sortable=new At(this.rootContainer,n),this.computeIndexes()},beforeDestroy(){void 0!==this._sortable&&this._sortable.destroy()},computed:{rootContainer(){return this.transitionMode?this.$el.children[0]:this.$el},realList(){return this.list?this.list:this.value}},watch:{options:{handler(t){this.updateOptions(t)},deep:!0},$attrs:{handler(t){this.updateOptions(t)},deep:!0},realList(){this.computeIndexes()}},methods:{getIsFunctional(){const{fnOptions:t}=this._vnode;return t&&t.functional},getTag(){return this.tag||this.element},updateOptions(t){for(var e in t){const n=ne(e);-1===ue.indexOf(n)&&this._sortable.option(n,t[e])}},getChildrenNodes(){if(this.noneFunctionalComponentMode)return this.$children[0].$slots.default;const t=this.$slots.default;return this.transitionMode?t[0].child.$slots.default:t},computeIndexes(){this.$nextTick((()=>{this.visibleIndexes=function(t,e,n,o){if(!t)return[];const i=t.map((t=>t.elm)),r=e.length-o,a=[...e].map(((t,e)=>e>=r?i.length:i.indexOf(t)));return n?a.filter((t=>-1!==t)):a}(this.getChildrenNodes(),this.rootContainer.children,this.transitionMode,this.footerOffset)}))},getUnderlyingVm(t){const e=function(t,e){return t.map((t=>t.elm)).indexOf(e)}(this.getChildrenNodes()||[],t);if(-1===e)return null;return{index:e,element:this.realList[e]}},getUnderlyingPotencialDraggableComponent:({__vue__:t})=>t&&t.$options&&le(t.$options._componentTag)?t.$parent:!("realList"in t)&&1===t.$children.length&&"realList"in t.$children[0]?t.$children[0]:t,emitChanges(t){this.$nextTick((()=>{this.$emit("change",t)}))},alterList(t){if(this.list)return void t(this.list);const e=[...this.value];t(e),this.$emit("input",e)},spliceList(){this.alterList((t=>t.splice(...arguments)))},updatePosition(t,e){this.alterList((n=>n.splice(e,0,n.splice(t,1)[0])))},getRelatedContextFromMoveEvent({to:t,related:e}){const n=this.getUnderlyingPotencialDraggableComponent(t);if(!n)return{component:n};const o=n.realList,i={list:o,component:n};if(t!==e&&o&&n.getUnderlyingVm){const t=n.getUnderlyingVm(e);if(t)return Object.assign(t,i)}return i},getVmIndex(t){const e=this.visibleIndexes,n=e.length;return t>n-1?n:e[t]},getComponent(){return this.$slots.default[0].componentInstance},resetTransitionData(t){if(!this.noTransitionOnDrag||!this.transitionMode)return;this.getChildrenNodes()[t].data=null;const e=this.getComponent();e.children=[],e.kept=void 0},onDragStart(t){this.context=this.getUnderlyingVm(t.item),t.item._underlying_vm_=this.clone(this.context.element),he=t.item},onDragAdd(t){const e=t.item._underlying_vm_;if(void 0===e)return;oe(t.item);const n=this.getVmIndex(t.newIndex);this.spliceList(n,0,e),this.computeIndexes();const o={element:e,newIndex:n};this.emitChanges({added:o})},onDragRemove(t){if(ie(this.rootContainer,t.item,t.oldIndex),"clone"===t.pullMode)return void oe(t.clone);const e=this.context.index;this.spliceList(e,1);const n={element:this.context.element,oldIndex:e};this.resetTransitionData(e),this.emitChanges({removed:n})},onDragUpdate(t){oe(t.item),ie(t.from,t.item,t.oldIndex);const e=this.context.index,n=this.getVmIndex(t.newIndex);this.updatePosition(e,n);const o={element:this.context.element,oldIndex:e,newIndex:n};this.emitChanges({moved:o})},updateProperty(t,e){t.hasOwnProperty(e)&&(t[e]+=this.headerOffset)},computeFutureIndex(t,e){if(!t.element)return 0;const n=[...e.to.children].filter((t=>"none"!==t.style.display)),o=n.indexOf(e.related),i=t.component.getVmIndex(o);return-1!==n.indexOf(he)||!e.willInsertAfter?i:i+1},onDragMove(t,e){const n=this.move;if(!n||!this.realList)return!0;const o=this.getRelatedContextFromMoveEvent(t),i=this.context,r=this.computeFutureIndex(o,t);Object.assign(i,{futureIndex:r});return n(Object.assign({},t,{relatedContext:o,draggedContext:i}),e)},onDragEnd(){this.computeIndexes(),he=null}}};"undefined"!=typeof window&&"Vue"in window&&window.Vue.component("draggable",fe);export{fe as default}; diff --git a/kirby/router.php b/kirby/router.php index 10386c7..ea8cc86 100644 --- a/kirby/router.php +++ b/kirby/router.php @@ -1,11 +1,12 @@ authentication = $props['authentication'] ?? null; + $this->data = $props['data'] ?? []; + $this->routes = $props['routes'] ?? []; + $this->debug = $props['debug'] ?? false; + + if ($collections = $props['collections'] ?? null) { + $this->collections = array_change_key_case($collections); + } + + if ($models = $props['models'] ?? null) { + $this->models = array_change_key_case($models); + } + + $this->setRequestData($props['requestData'] ?? null); + $this->setRequestMethod($props['requestMethod'] ?? null); + } + /** * Magic accessor for any given data * @@ -96,14 +115,6 @@ class Api return $this->data($method, ...$args); } - /** - * Creates a new API instance - */ - public function __construct(array $props) - { - $this->setProperties($props); - } - /** * Runs the authentication method * if set @@ -115,8 +126,6 @@ class Api /** * Returns the authentication callback - * - * @return \Closure|null */ public function authentication(): Closure|null { @@ -130,8 +139,11 @@ class Api * @throws \Kirby\Exception\NotFoundException * @throws \Exception */ - public function call(string|null $path = null, string $method = 'GET', array $requestData = []) - { + public function call( + string|null $path = null, + string $method = 'GET', + array $requestData = [] + ): mixed { $path = rtrim($path ?? '', '/'); $this->setRequestMethod($method); @@ -194,14 +206,34 @@ class Api return $output; } + /** + * Creates a new instance while + * merging initial and new properties + */ + public function clone(array $props = []): static + { + return new static(array_merge([ + 'autentication' => $this->authentication, + 'data' => $this->data, + 'routes' => $this->routes, + 'debug' => $this->debug, + 'collections' => $this->collections, + 'models' => $this->models, + 'requestData' => $this->requestData, + 'requestMethod' => $this->requestMethod + ], $props)); + } + /** * Setter and getter for an API collection * * @throws \Kirby\Exception\NotFoundException If no collection for `$name` exists * @throws \Exception */ - public function collection(string $name, array|BaseCollection|null $collection = null): Collection - { + public function collection( + string $name, + array|BaseCollection|null $collection = null + ): Collection { if (isset($this->collections[$name]) === false) { throw new NotFoundException(sprintf('The collection "%s" does not exist', $name)); } @@ -223,7 +255,7 @@ class Api * * @throws \Kirby\Exception\NotFoundException If no data for `$key` exists */ - public function data(string|null $key = null, ...$args) + public function data(string|null $key = null, ...$args): mixed { if ($key === null) { return $this->data; @@ -264,8 +296,10 @@ class Api * @param array models or collections * @return string|null key of match */ - protected function match(array $array, $object = null): string|null - { + protected function match( + array $array, + $object = null + ): string|null { foreach ($array as $definition => $model) { if ($object instanceof $model['type']) { return $definition; @@ -280,8 +314,10 @@ class Api * * @throws \Kirby\Exception\NotFoundException If no model for `$name` exists */ - public function model(string|null $name = null, $object = null): Model - { + public function model( + string|null $name = null, + $object = null + ): Model { // Try to auto-match object with API models $name ??= $this->match($this->models, $object); @@ -304,17 +340,12 @@ class Api * Getter for request data * Can either get all the data * or certain parts of it. - * - * @param string|null $type - * @param string|null $key - * @param mixed $default - * @return mixed */ public function requestData( string|null $type = null, string|null $key = null, - $default = null - ) { + mixed $default = null + ): mixed { if ($type === null) { return $this->requestData; } @@ -332,24 +363,30 @@ class Api /** * Returns the request body if available */ - public function requestBody(string|null $key = null, $default = null) - { + public function requestBody( + string|null $key = null, + mixed $default = null + ): mixed { return $this->requestData('body', $key, $default); } /** * Returns the files from the request if available */ - public function requestFiles(string|null $key = null, $default = null) - { + public function requestFiles( + string|null $key = null, + mixed $default = null + ): mixed { return $this->requestData('files', $key, $default); } /** * Returns all headers from the request if available */ - public function requestHeaders(string|null $key = null, $default = null) - { + public function requestHeaders( + string|null $key = null, + mixed $default = null + ): mixed { return $this->requestData('headers', $key, $default); } @@ -364,8 +401,10 @@ class Api /** * Returns the request query if available */ - public function requestQuery(string|null $key = null, $default = null) - { + public function requestQuery( + string|null $key = null, + mixed $default = null + ): mixed { return $this->requestData('query', $key, $default); } @@ -403,102 +442,14 @@ class Api return $this->routes; } - /** - * Setter for the authentication callback - * @return $this - */ - protected function setAuthentication(Closure|null $authentication = null): static - { - $this->authentication = $authentication; - return $this; - } - - /** - * Setter for the collections definition - * @return $this - */ - protected function setCollections(array|null $collections = null): static - { - if ($collections !== null) { - $this->collections = array_change_key_case($collections); - } - return $this; - } - - /** - * Setter for the injected data - * @return $this - */ - protected function setData(array|null $data = null): static - { - $this->data = $data ?? []; - return $this; - } - - /** - * Setter for the debug flag - * @return $this - */ - protected function setDebug(bool $debug = false): static - { - $this->debug = $debug; - return $this; - } - - /** - * Setter for the model definitions - * @return $this - */ - protected function setModels(array|null $models = null): static - { - if ($models !== null) { - $this->models = array_change_key_case($models); - } - - return $this; - } - - /** - * Setter for the request data - * @return $this - */ - protected function setRequestData(array|null $requestData = null): static - { - $defaults = [ - 'query' => [], - 'body' => [], - 'files' => [] - ]; - - $this->requestData = array_merge($defaults, (array)$requestData); - return $this; - } - - /** - * Setter for the request method - * @return $this - */ - protected function setRequestMethod(string|null $requestMethod = null): static - { - $this->requestMethod = $requestMethod ?? 'GET'; - return $this; - } - - /** - * Setter for the route definitions - * @return $this - */ - protected function setRoutes(array|null $routes = null): static - { - $this->routes = $routes ?? []; - return $this; - } - /** * Renders the API call */ - public function render(string $path, string $method = 'GET', array $requestData = []) - { + public function render( + string $path, + string $method = 'GET', + array $requestData = [] + ): mixed { try { $result = $this->call($path, $method, $requestData); } catch (Throwable $e) { @@ -619,6 +570,34 @@ class Api return $result; } + /** + * Setter for the request data + * @return $this + */ + protected function setRequestData( + array|null $requestData = [] + ): static { + $defaults = [ + 'query' => [], + 'body' => [], + 'files' => [] + ]; + + $this->requestData = array_merge($defaults, (array)$requestData); + return $this; + } + + /** + * Setter for the request method + * @return $this + */ + protected function setRequestMethod( + string|null $requestMethod = null + ): static { + $this->requestMethod = $requestMethod ?? 'GET'; + return $this; + } + /** * Upload helper method * @@ -627,8 +606,11 @@ class Api * * @throws \Exception If request has no files or there was an error with the upload */ - public function upload(Closure $callback, bool $single = false, bool $debug = false): array - { + public function upload( + Closure $callback, + bool $single = false, + bool $debug = false + ): array { $trials = 0; $uploads = []; $errors = []; @@ -650,10 +632,20 @@ class Api $uploadMaxFileSize = Str::toBytes(ini_get('upload_max_filesize')); if ($postMaxSize < $uploadMaxFileSize) { - throw new Exception(I18n::translate('upload.error.iniPostSize')); + throw new Exception( + I18n::translate( + 'upload.error.iniPostSize', + 'The uploaded file exceeds the post_max_size directive in php.ini' + ) + ); } - throw new Exception(I18n::translate('upload.error.noFiles')); + throw new Exception( + I18n::translate( + 'upload.error.noFiles', + 'No files were uploaded' + ) + ); } foreach ($files as $upload) { @@ -668,8 +660,10 @@ class Api try { if ($upload['error'] !== 0) { - $errorMessage = $errorMessages[$upload['error']] ?? I18n::translate('upload.error.default'); - throw new Exception($errorMessage); + throw new Exception( + $errorMessages[$upload['error']] ?? + I18n::translate('upload.error.default', 'The file could not be uploaded') + ); } // get the extension of the uploaded file @@ -696,7 +690,9 @@ class Api $debug === false && move_uploaded_file($upload['tmp_name'], $source) === false ) { - throw new Exception(I18n::translate('upload.error.cantMove')); + throw new Exception( + I18n::translate('upload.error.cantMove') + ); } $data = $callback($source, $filename); diff --git a/kirby/src/Api/Collection.php b/kirby/src/Api/Collection.php index cc71b24..af66235 100644 --- a/kirby/src/Api/Collection.php +++ b/kirby/src/Api/Collection.php @@ -4,6 +4,7 @@ namespace Kirby\Api; use Closure; use Exception; +use Kirby\Toolkit\Collection as BaseCollection; use Kirby\Toolkit\Str; /** @@ -20,23 +21,22 @@ use Kirby\Toolkit\Str; */ class Collection { - protected Api $api; - protected $data; - protected $model; - protected $select = null; - protected $view; + protected string|null $model; + protected array|null $select = null; + protected string|null $view; /** * Collection constructor * * @throws \Exception */ - public function __construct(Api $api, $data, array $schema) - { - $this->api = $api; - $this->data = $data; - $this->model = $schema['model'] ?? null; - $this->view = $schema['view'] ?? null; + public function __construct( + protected Api $api, + protected BaseCollection|array|null $data, + array $schema + ) { + $this->model = $schema['model'] ?? null; + $this->view = $schema['view'] ?? null; if ($data === null) { if (($schema['default'] ?? null) instanceof Closure === false) { diff --git a/kirby/src/Api/Model.php b/kirby/src/Api/Model.php index ba14cb2..24af00b 100644 --- a/kirby/src/Api/Model.php +++ b/kirby/src/Api/Model.php @@ -22,21 +22,20 @@ use Kirby\Toolkit\Str; */ class Model { - protected Api $api; - protected $data; - protected $fields; - protected $select; - protected $views; + protected array $fields; + protected array|null $select; + protected array $views; /** * Model constructor * * @throws \Exception */ - public function __construct(Api $api, $data, array $schema) - { - $this->api = $api; - $this->data = $data; + public function __construct( + protected Api $api, + protected object|array|string|null $data, + array $schema + ) { $this->fields = $schema['fields'] ?? []; $this->select = $schema['select'] ?? null; $this->views = $schema['views'] ?? []; @@ -60,7 +59,11 @@ class Model isset($schema['type']) === true && $this->data instanceof $schema['type'] === false ) { - throw new Exception(sprintf('Invalid model type "%s" expected: "%s"', get_class($this->data), $schema['type'])); + $class = match ($this->data) { + null => 'null', + default => get_class($this->data), + }; + throw new Exception(sprintf('Invalid model type "%s" expected: "%s"', $class, $schema['type'])); } } diff --git a/kirby/src/Blueprint/Collection.php b/kirby/src/Blueprint/Collection.php index a943b74..a8dc604 100644 --- a/kirby/src/Blueprint/Collection.php +++ b/kirby/src/Blueprint/Collection.php @@ -16,7 +16,7 @@ use TypeError; * @copyright Bastian Allgeier * @license https://opensource.org/licenses/MIT * - * // TODO: include in test coverage 3.10 + * // TODO: include in test coverage once blueprint refactoring is done * @codeCoverageIgnore */ class Collection extends BaseCollection @@ -37,6 +37,8 @@ class Collection extends BaseCollection * The Kirby Collection class only shows the key to * avoid huge tress with dump, but for the blueprint * collections this is really not useful + * + * @codeCoverageIgnore */ public function __debugInfo(): array { diff --git a/kirby/src/Blueprint/Config.php b/kirby/src/Blueprint/Config.php index 1cd9143..c7edb23 100644 --- a/kirby/src/Blueprint/Config.php +++ b/kirby/src/Blueprint/Config.php @@ -17,7 +17,7 @@ use Kirby\Filesystem\F; * @copyright Bastian Allgeier * @license https://opensource.org/licenses/MIT * - * // TODO: include in test coverage in 3.10 + * // TODO: include in test coverage once blueprint refactoring is done * @codeCoverageIgnore */ class Config diff --git a/kirby/src/Blueprint/Extension.php b/kirby/src/Blueprint/Extension.php index f422b1d..9d3d25f 100644 --- a/kirby/src/Blueprint/Extension.php +++ b/kirby/src/Blueprint/Extension.php @@ -11,7 +11,7 @@ namespace Kirby\Blueprint; * @copyright Bastian Allgeier * @license https://opensource.org/licenses/MIT * - * // TODO: include in test coverage in 3.10 + * // TODO: include in test coverage once blueprint refactoring is done * @codeCoverageIgnore */ class Extension diff --git a/kirby/src/Blueprint/Factory.php b/kirby/src/Blueprint/Factory.php index 148f1eb..756611c 100644 --- a/kirby/src/Blueprint/Factory.php +++ b/kirby/src/Blueprint/Factory.php @@ -16,7 +16,7 @@ use ReflectionUnionType; * @copyright Bastian Allgeier * @license https://opensource.org/licenses/MIT * - * // TODO: include in test coverage in 3.10 + * // TODO: include in test coverage once blueprint refactoring is done * @codeCoverageIgnore */ class Factory diff --git a/kirby/src/Blueprint/Node.php b/kirby/src/Blueprint/Node.php index 80f9ae6..96d4dca 100644 --- a/kirby/src/Blueprint/Node.php +++ b/kirby/src/Blueprint/Node.php @@ -13,7 +13,7 @@ use Kirby\Cms\ModelWithContent; * @copyright Bastian Allgeier * @license https://opensource.org/licenses/MIT * - * // TODO: include in test coverage in 3.10 + * // TODO: include in test coverage once blueprint refactoring is done * @codeCoverageIgnore */ class Node diff --git a/kirby/src/Blueprint/NodeI18n.php b/kirby/src/Blueprint/NodeI18n.php index 5278774..6dc06aa 100644 --- a/kirby/src/Blueprint/NodeI18n.php +++ b/kirby/src/Blueprint/NodeI18n.php @@ -14,7 +14,7 @@ use Kirby\Toolkit\I18n; * @copyright Bastian Allgeier * @license https://opensource.org/licenses/MIT * - * // TODO: include in test coverage in 3.10 + * // TODO: include in test coverage once blueprint refactoring is done * @codeCoverageIgnore */ class NodeI18n extends NodeProperty diff --git a/kirby/src/Blueprint/NodeIcon.php b/kirby/src/Blueprint/NodeIcon.php index c23d589..3fa672d 100644 --- a/kirby/src/Blueprint/NodeIcon.php +++ b/kirby/src/Blueprint/NodeIcon.php @@ -11,7 +11,7 @@ namespace Kirby\Blueprint; * @copyright Bastian Allgeier * @license https://opensource.org/licenses/MIT * - * // TODO: include in test coverage in 3.10 + * // TODO: include in test coverage once blueprint refactoring is done * @codeCoverageIgnore */ class NodeIcon extends NodeString diff --git a/kirby/src/Blueprint/NodeProperty.php b/kirby/src/Blueprint/NodeProperty.php index 9490435..6aef2db 100644 --- a/kirby/src/Blueprint/NodeProperty.php +++ b/kirby/src/Blueprint/NodeProperty.php @@ -13,7 +13,7 @@ use Kirby\Cms\ModelWithContent; * @copyright Bastian Allgeier * @license https://opensource.org/licenses/MIT * - * // TODO: include in test coverage in 3.10 + * // TODO: include in test coverage once blueprint refactoring is done * @codeCoverageIgnore */ abstract class NodeProperty diff --git a/kirby/src/Blueprint/NodeString.php b/kirby/src/Blueprint/NodeString.php index 7769c3a..1093e1f 100644 --- a/kirby/src/Blueprint/NodeString.php +++ b/kirby/src/Blueprint/NodeString.php @@ -13,7 +13,7 @@ use Kirby\Cms\ModelWithContent; * @copyright Bastian Allgeier * @license https://opensource.org/licenses/MIT * - * // TODO: include in test coverage in 3.10 + * // TODO: include in test coverage once blueprint refactoring is done * @codeCoverageIgnore */ class NodeString extends NodeProperty diff --git a/kirby/src/Blueprint/NodeText.php b/kirby/src/Blueprint/NodeText.php index feeccf6..bbc4cd7 100644 --- a/kirby/src/Blueprint/NodeText.php +++ b/kirby/src/Blueprint/NodeText.php @@ -14,12 +14,12 @@ use Kirby\Cms\ModelWithContent; * @copyright Bastian Allgeier * @license https://opensource.org/licenses/MIT * - * // TODO: include in test coverage in 3.10 + * // TODO: include in test coverage once blueprint refactoring is done * @codeCoverageIgnore */ class NodeText extends NodeI18n { - public function render(ModelWithContent $model): ?string + public function render(ModelWithContent $model): string|null { if ($text = parent::render($model)) { return $model->toSafeString($text); diff --git a/kirby/src/Cms/Api.php b/kirby/src/Cms/Api.php index c914e3b..49dcac9 100644 --- a/kirby/src/Cms/Api.php +++ b/kirby/src/Cms/Api.php @@ -18,10 +18,13 @@ use Kirby\Session\Session; */ class Api extends BaseApi { - /** - * @var App - */ - protected $kirby; + protected App $kirby; + + public function __construct(array $props) + { + $this->kirby = $props['kirby']; + parent::__construct($props); + } /** * Execute an API call for the given path, @@ -31,7 +34,7 @@ class Api extends BaseApi string|null $path = null, string $method = 'GET', array $requestData = [] - ) { + ): mixed { $this->setRequestMethod($method); $this->setRequestData($requestData); @@ -46,21 +49,37 @@ class Api extends BaseApi return parent::call($path, $method, $requestData); } + /** + * Creates a new instance while + * merging initial and new properties + */ + public function clone(array $props = []): static + { + return parent::clone(array_merge([ + 'kirby' => $this->kirby + ], $props)); + } + /** * @throws \Kirby\Exception\NotFoundException if the field type cannot be found or the field cannot be loaded */ - public function fieldApi($model, string $name, string|null $path = null) - { + public function fieldApi( + ModelWithContent $model, + string $name, + string|null $path = null + ): mixed { $field = Form::for($model)->field($name); - $fieldApi = new static( - array_merge($this->propertyData, [ - 'data' => array_merge($this->data(), ['field' => $field]), - 'routes' => $field->api(), - ]), - ); + $fieldApi = $this->clone([ + 'data' => [...$this->data(), 'field' => $field], + 'routes' => $field->api(), + ]); - return $fieldApi->call($path, $this->requestMethod(), $this->requestData()); + return $fieldApi->call( + $path, + $this->requestMethod(), + $this->requestData() + ); } /** @@ -70,11 +89,24 @@ class Api extends BaseApi * @param string $path Path to file's parent model * @throws \Kirby\Exception\NotFoundException if the file cannot be found */ - public function file(string $path, string $filename): File|null - { + public function file( + string $path, + string $filename + ): File|null { return Find::file($path, $filename); } + /** + * Returns the all readable files for the parent + * + * @param string $path Path to file's parent model + * @throws \Kirby\Exception\NotFoundException if the file cannot be found + */ + public function files(string $path): Files + { + return $this->parent($path)->files()->filter('isAccessible', true); + } + /** * Returns the model's object for the given path * @@ -82,7 +114,7 @@ class Api extends BaseApi * @throws \Kirby\Exception\InvalidArgumentException if the model type is invalid * @throws \Kirby\Exception\NotFoundException if the model cannot be found */ - public function parent(string $path): Model|null + public function parent(string $path): ModelWithContent|null { return Find::parent($path); } @@ -100,7 +132,9 @@ class Api extends BaseApi */ public function language(): string|null { - return $this->requestQuery('language') ?? $this->requestHeaders('x-language'); + return + $this->requestQuery('language') ?? + $this->requestHeaders('x-language'); } /** @@ -119,11 +153,12 @@ class Api extends BaseApi * parent. The subpages can be filtered * by status (draft, listed, unlisted, published, all) */ - public function pages(string|null $parentId = null, string|null $status = null): Pages - { + public function pages( + string|null $parentId = null, + string|null $status = null + ): Pages { $parent = $parentId === null ? $this->site() : $this->page($parentId); - - return match ($status) { + $pages = match ($status) { 'all' => $parent->childrenAndDrafts(), 'draft', 'drafts' => $parent->drafts(), 'listed' => $parent->children()->listed(), @@ -131,6 +166,8 @@ class Api extends BaseApi 'published' => $parent->children(), default => $parent->children() }; + + return $pages->filter('isAccessible', true); } /** @@ -148,6 +185,30 @@ class Api extends BaseApi return $pages->query($this->requestBody()); } + /** + * @throws \Kirby\Exception\NotFoundException if the section type cannot be found or the section cannot be loaded + */ + public function sectionApi( + ModelWithContent $model, + string $name, + string|null $path = null + ): mixed { + if (!$section = $model->blueprint()?->section($name)) { + throw new NotFoundException('The section "' . $name . '" could not be found'); + } + + $sectionApi = $this->clone([ + 'data' => [...$this->data(), 'section' => $section], + 'routes' => $section->api(), + ]); + + return $sectionApi->call( + $path, + $this->requestMethod(), + $this->requestData() + ); + } + /** * Returns the current Session instance * @@ -160,17 +221,6 @@ class Api extends BaseApi ], $options)); } - /** - * Setter for the parent Kirby instance - * - * @return $this - */ - protected function setKirby(App $kirby): static - { - $this->kirby = $kirby; - return $this; - } - /** * Returns the site object */ diff --git a/kirby/src/Cms/App.php b/kirby/src/Cms/App.php index 44b8fba..cdc8343 100644 --- a/kirby/src/Cms/App.php +++ b/kirby/src/Cms/App.php @@ -3,7 +3,9 @@ namespace Kirby\Cms; use Closure; +use Generator; use Kirby\Data\Data; +use Kirby\Email\Email as BaseEmail; use Kirby\Exception\ErrorPageException; use Kirby\Exception\Exception; use Kirby\Exception\InvalidArgumentException; @@ -14,17 +16,21 @@ use Kirby\Filesystem\F; use Kirby\Http\Environment; use Kirby\Http\Request; use Kirby\Http\Response; +use Kirby\Http\Route; use Kirby\Http\Router; use Kirby\Http\Uri; use Kirby\Http\Visitor; use Kirby\Session\AutoSession; +use Kirby\Session\Session; use Kirby\Template\Snippet; +use Kirby\Template\Template; use Kirby\Text\KirbyTag; use Kirby\Text\KirbyTags; use Kirby\Toolkit\A; use Kirby\Toolkit\Config; use Kirby\Toolkit\Controller; -use Kirby\Toolkit\Properties; +use Kirby\Toolkit\LazyValue; +use Kirby\Toolkit\Locale; use Kirby\Toolkit\Str; use Kirby\Uuid\Uuid; use Throwable; @@ -49,45 +55,43 @@ class App use AppPlugins; use AppTranslations; use AppUsers; - use Properties; public const CLASS_ALIAS = 'kirby'; - protected static $instance; - protected static $version; + protected static App|null $instance = null; + protected static string|null $version = null; - public $data = []; + public array $data = []; - protected $api; - protected $collections; - protected $core; - protected $defaultLanguage; - protected $environment; - protected $language; - protected $languages; - protected $locks; - protected $multilang; - protected $nonce; - protected $options; - protected $path; - protected $request; - protected $response; - protected $roles; - protected $roots; - protected $routes; - protected $router; - protected $sessionHandler; - protected $site; - protected $system; - protected $urls; - protected $user; - protected $users; - protected $visitor; + protected Api|null $api = null; + protected Collections|null $collections = null; + protected Core $core; + protected Language|null $defaultLanguage = null; + protected Environment|null $environment = null; + protected Language|null $language = null; + protected Languages|null $languages = null; + protected ContentLocks|null $locks = null; + protected bool|null $multilang = null; + protected string|null $nonce = null; + protected array $options; + protected string|null $path = null; + protected Request|null $request = null; + protected Responder|null $response = null; + protected Roles|null $roles = null; + protected Ingredients $roots; + protected array|null $routes = null; + protected Router|null $router = null; + protected AutoSession|null $sessionHandler = null; + protected Site|null $site = null; + protected System|null $system = null; + protected Ingredients $urls; + protected Visitor|null $visitor = null; + + protected array $propertyData; /** * Creates a new App instance * - * @param array $props * @param bool $setInstance If false, the instance won't be set globally */ public function __construct(array $props = [], bool $setInstance = true) @@ -109,6 +113,8 @@ class App $this->handleErrors(); } + $this->propertyData = $props; + // a custom request setup must come before defining the path $this->setRequest($props['request'] ?? null); @@ -120,17 +126,15 @@ class App $this->bakeUrls($props['urls'] ?? []); // configurable properties - $this->setOptionalProperties($props, [ - 'languages', - 'roles', - 'site', - 'user', - 'users' - ]); + $this->setLanguages($props['languages'] ?? null); + $this->setRoles($props['roles'] ?? null); + $this->setSite($props['site'] ?? null); + $this->setUser($props['user'] ?? null); + $this->setUsers($props['users'] ?? null); // set the singleton if (static::$instance === null || $setInstance === true) { - Model::$kirby = static::$instance = $this; + static::$instance = ModelWithContent::$kirby = Model::$kirby = $this; } // setup the I18n class with the translation loader @@ -156,7 +160,7 @@ class App /** * Improved `var_dump` output * - * @return array + * @codeCoverageIgnore */ public function __debugInfo(): array { @@ -167,7 +171,7 @@ class App 'roots' => $this->roots(), 'site' => $this->site(), 'urls' => $this->urls(), - 'version' => $this->version(), + 'version' => static::version(), ]; } @@ -175,9 +179,8 @@ class App * Returns the Api instance * * @internal - * @return \Kirby\Cms\Api */ - public function api() + public function api(): Api { if ($this->api !== null) { return $this->api; @@ -209,8 +212,12 @@ class App * @param \Kirby\Cms\Event|null $originalEvent Event object (internal use) * @return mixed Resulting value as modified by the hooks */ - public function apply(string $name, array $args, string $modify, ?Event $originalEvent = null) - { + public function apply( + string $name, + array $args, + string $modify, + Event|null $originalEvent = null + ): mixed { $event = $originalEvent ?? new Event($name, $args); if ($functions = $this->extension('hooks', $name)) { @@ -243,13 +250,13 @@ class App * * @return $this */ - protected function bakeOptions() + protected function bakeOptions(): static { // convert the old plugin option syntax to the new one foreach ($this->options as $key => $value) { // detect option keys with the `vendor.plugin.option` format if (preg_match('/^([a-z0-9-]+\.[a-z0-9-]+)\.(.*)$/i', $key, $matches) === 1) { - list(, $plugin, $option) = $matches; + [, $plugin, $option] = $matches; // verify that it's really a plugin option if (isset(static::$plugins[str_replace('.', '/', $plugin)]) !== true) { @@ -276,10 +283,9 @@ class App /** * Sets the directory structure * - * @param array|null $roots * @return $this */ - protected function bakeRoots(array $roots = null) + protected function bakeRoots(array|null $roots = null): static { $roots = array_merge($this->core->roots(), (array)$roots); $this->roots = Ingredients::bake($roots); @@ -289,10 +295,9 @@ class App /** * Sets the Url structure * - * @param array|null $urls * @return $this */ - protected function bakeUrls(array $urls = null) + protected function bakeUrls(array|null $urls = null): static { $urls = array_merge($this->core->urls(), (array)$urls); $this->urls = Ingredients::bake($urls); @@ -301,9 +306,6 @@ class App /** * Returns all available blueprints for this installation - * - * @param string $type - * @return array */ public function blueprints(string $type = 'pages'): array { @@ -328,12 +330,8 @@ class App /** * Calls any Kirby route - * - * @param string|null $path - * @param string|null $method - * @return mixed */ - public function call(string $path = null, string $method = null) + public function call(string|null $path = null, string|null $method = null): mixed { $path ??= $this->path(); $method ??= $this->request()->method(); @@ -344,11 +342,9 @@ class App * Creates an instance with the same * initial properties * - * @param array $props * @param bool $setInstance If false, the instance won't be set globally - * @return static */ - public function clone(array $props = [], bool $setInstance = true) + public function clone(array $props = [], bool $setInstance = true): static { $props = array_replace_recursive($this->propertyData, $props); @@ -371,8 +367,9 @@ class App return $this->collections()->get($name, array_merge($options, [ 'kirby' => $this, 'site' => $site = $this->site(), - 'pages' => $site->children(), - 'users' => $this->users() + 'pages' => new LazyValue(fn () => $site->children()), + 'users' => new LazyValue(fn () => $this->users()) + ])); } @@ -388,10 +385,8 @@ class App * Returns a core component * * @internal - * @param string $name - * @return mixed */ - public function component($name) + public function component(string $name): mixed { return $this->extensions['components'][$name] ?? null; } @@ -400,7 +395,6 @@ class App * Returns the content extension * * @internal - * @return string */ public function contentExtension(): string { @@ -411,7 +405,6 @@ class App * Returns files that should be ignored when scanning folders * * @internal - * @return array */ public function contentIgnore(): array { @@ -424,9 +417,8 @@ class App * * @param mixed $model Object to pass to the salt callback if configured * @param string $value Model data to include in the generated token - * @return string */ - public function contentToken($model, string $value): string + public function contentToken(mixed $model, string $value): string { if (method_exists($model, 'root') === true) { $default = $model->root(); @@ -446,15 +438,12 @@ class App /** * Calls a page controller by name * and with the given arguments - * - * @internal - * @param string $name - * @param array $arguments - * @param string $contentType - * @return array */ - public function controller(string $name, array $arguments = [], string $contentType = 'html'): array - { + public function controller( + string $name, + array $arguments = [], + string $contentType = 'html' + ): array { $name = basename(strtolower($name)); if ($controller = $this->controllerLookup($name, $contentType)) { @@ -480,8 +469,10 @@ class App /** * Try to find a controller by name */ - protected function controllerLookup(string $name, string $contentType = 'html'): Controller|null - { + protected function controllerLookup( + string $name, + string $contentType = 'html' + ): Controller|null { if ($contentType !== null && $contentType !== 'html') { $name .= '.' . $contentType; } @@ -505,10 +496,8 @@ class App /** * Get access to object that lists * all parts of Kirby core - * - * @return \Kirby\Cms\Core */ - public function core() + public function core(): Core { return $this->core; } @@ -520,7 +509,7 @@ class App * @param string|null $check Pass a token here to compare it to the one in the session * @return string|bool Either the token or a boolean check result */ - public function csrf(string|null $check = null) + public function csrf(string|null $check = null): string|bool { $session = $this->session(); @@ -550,6 +539,14 @@ class App return false; } + /** + * Returns the current language, if set by `static::setCurrentLanguage` + */ + public function currentLanguage(): Language|null + { + return $this->language ??= $this->defaultLanguage(); + } + /** * Returns the default language object */ @@ -603,12 +600,8 @@ class App /** * Returns the Email singleton - * - * @param mixed $preset - * @param array $props - * @return \Kirby\Email\Email */ - public function email($preset = [], array $props = []) + public function email(mixed $preset = [], array $props = []): BaseEmail { $debug = $props['debug'] ?? false; $props = (new Email($preset, $props))->toArray(); @@ -619,31 +612,27 @@ class App /** * Returns the environment object with access * to the detected host, base url and dedicated options - * - * @return \Kirby\Http\Environment */ - public function environment() + public function environment(): Environment { - return $this->environment ?? new Environment(); + return $this->environment ??= new Environment(); } /** * Finds any file in the content directory - * - * @param string $path - * @param mixed $parent - * @param bool $drafts - * @return \Kirby\Cms\File|null */ - public function file(string $path, $parent = null, bool $drafts = true) - { + public function file( + string $path, + mixed $parent = null, + bool $drafts = true + ): File|null { // find by global UUID if (Uuid::is($path, 'file') === true) { // prefer files of parent, when parent given return Uuid::for($path, $parent?->files())->model(); } - $parent = $parent ?? $this->site(); + $parent ??= $this->site(); $id = dirname($path); $filename = basename($path); @@ -675,14 +664,11 @@ class App * specified by the path * * Example: - * - * - * @param string|null $path - * @return \Kirby\Cms\File|null + * image('some/page/myimage.jpg') ?> * * @todo merge with App::file() */ - public function image(string|null $path = null) + public function image(string|null $path = null): File|null { if ($path === null) { return $this->site()->page()->image(); @@ -707,13 +693,13 @@ class App /** * Returns the current App instance * - * @param \Kirby\Cms\App|null $instance * @param bool $lazy If `true`, the instance is only returned if already existing - * @return static|null * @psalm-return ($lazy is false ? static : static|null) */ - public static function instance(self $instance = null, bool $lazy = false) - { + public static function instance( + self|null $instance = null, + bool $lazy = false + ): static|null { if ($instance !== null) { return static::$instance = $instance; } @@ -730,10 +716,8 @@ class App * tries to convert it into a valid response * * @internal - * @param mixed $input - * @return \Kirby\Http\Response */ - public function io($input) + public function io(mixed $input): Response { // use the current response configuration $response = $this->response(); @@ -798,7 +782,7 @@ class App if ($input instanceof Page) { try { $html = $input->render(); - } catch (ErrorPageException $e) { + } catch (ErrorPageException|NotFoundException $e) { return $this->io($e); } @@ -836,13 +820,13 @@ class App * @internal * @param string|array $type Tag type or array with all tag arguments * (the key of the first element becomes the type) - * @param string|null $value - * @param array $attr - * @param array $data - * @return string */ - public function kirbytag($type, string|null $value = null, array $attr = [], array $data = []): string - { + public function kirbytag( + string|array $type, + string|null $value = null, + array $attr = [], + array $data = [] + ): string { if (is_array($type) === true) { $kirbytag = $type; $type = key($kirbytag); @@ -856,9 +840,9 @@ class App } } - $data['kirby'] = $data['kirby'] ?? $this; - $data['site'] = $data['site'] ?? $data['kirby']->site(); - $data['parent'] = $data['parent'] ?? $data['site']->page(); + $data['kirby'] ??= $this; + $data['site'] ??= $data['kirby']->site(); + $data['parent'] ??= $data['site']->page(); return (new KirbyTag($type, $value, $attr, $data, $this->options))->render(); } @@ -867,11 +851,8 @@ class App * KirbyTags Parser * * @internal - * @param string|null $text - * @param array $data - * @return string */ - public function kirbytags(string $text = null, array $data = []): string + public function kirbytags(string|null $text = null, array $data = []): string { $data['kirby'] ??= $this; $data['site'] ??= $data['kirby']->site(); @@ -890,11 +871,8 @@ class App * Parses KirbyTags first and Markdown afterwards * * @internal - * @param string|null $text - * @param array $options - * @return string */ - public function kirbytext(string $text = null, array $options = []): string + public function kirbytext(string|null $text = null, array $options = []): string { $text = $this->apply('kirbytext:before', compact('text'), 'text'); $text = $this->kirbytags($text, $options); @@ -910,40 +888,28 @@ class App } /** - * Returns the current language - * - * @param string|null $code - * @return \Kirby\Cms\Language|null + * Returns the language by code or shortcut (`default`, `current`). + * Passing `null` is an alias for passing `current` */ - public function language(string $code = null) + public function language(string|null $code = null): Language|null { if ($this->multilang() === false) { return null; } - if ($code === 'default') { - return $this->defaultLanguage(); - } - - // if requesting a non-default language, - // find it but don't cache it - if ($code !== null) { - return $this->languages()->find($code); - } - - // otherwise return language set by `AppTranslation::setCurrentLanguage` - // or default language - return $this->language ??= $this->defaultLanguage(); + return match ($code ?? 'current') { + 'default' => $this->defaultLanguage(), + 'current' => $this->currentLanguage(), + default => $this->languages()->find($code) + }; } /** * Returns the current language code * * @internal - * @param string|null $languageCode - * @return string|null */ - public function languageCode(string $languageCode = null): string|null + public function languageCode(string|null $languageCode = null): string|null { return $this->language($languageCode)?->code(); } @@ -953,6 +919,11 @@ class App */ public function languages(bool $clone = true): Languages { + if ($clone === false) { + $this->multilang = null; + $this->defaultLanguage = null; + } + if ($this->languages !== null) { return $clone === true ? clone $this->languages : $this->languages; } @@ -962,37 +933,26 @@ class App /** * Access Kirby's part loader - * - * @return \Kirby\Cms\Loader */ - public function load() + public function load(): Loader { return new Loader($this); } /** * Returns the app's locks object - * - * @return \Kirby\Cms\ContentLocks */ public function locks(): ContentLocks { - if ($this->locks !== null) { - return $this->locks; - } - - return $this->locks = new ContentLocks(); + return $this->locks ??= new ContentLocks(); } /** * Parses Markdown * * @internal - * @param string|null $text - * @param array $options - * @return string */ - public function markdown(string $text = null, array $options = null): string + public function markdown(string|null $text = null, array|null $options = null): string { // merge global options with local options $options = array_merge( @@ -1004,25 +964,41 @@ class App } /** - * Check for a multilang setup + * Yields all models (site, pages, files and users) of this site + * @since 4.0.0 * - * @return bool + * @return \Generator|\Kirby\Cms\ModelWithContent[] + */ + public function models(): Generator + { + $site = $this->site(); + + yield from $site->files(); + yield $site; + + foreach ($site->index(true) as $page) { + yield from $page->files(); + yield $page; + } + + foreach ($this->users() as $user) { + yield from $user->files(); + yield $user; + } + } + + /** + * Check for a multilang setup */ public function multilang(): bool { - if ($this->multilang !== null) { - return $this->multilang; - } - - return $this->multilang = $this->languages()->count() !== 0; + return $this->multilang ??= $this->languages()->count() !== 0; } /** * Returns the nonce, which is used * in the panel for inline scripts * @since 3.3.0 - * - * @return string */ public function nonce(): string { @@ -1031,20 +1007,14 @@ class App /** * Load a specific configuration option - * - * @param string $key - * @param mixed $default - * @return mixed */ - public function option(string $key, $default = null) + public function option(string $key, mixed $default = null): mixed { return A::get($this->options, $key, $default); } /** * Returns all configuration options - * - * @return array */ public function options(): array { @@ -1053,8 +1023,6 @@ class App /** * Load all options from files in site/config - * - * @return array */ protected function optionsFromConfig(): array { @@ -1072,9 +1040,6 @@ class App /** * Load all options for the current * server environment - * - * @param array $props - * @return array */ protected function optionsFromEnvironment(array $props = []): array { @@ -1113,9 +1078,6 @@ class App /** * Inject options from Kirby instance props - * - * @param array $options - * @return array */ protected function optionsFromProps(array $options = []): array { @@ -1127,12 +1089,13 @@ class App /** * Merge last-minute options from ready callback - * - * @return array */ protected function optionsFromReadyCallback(): array { - if (isset($this->options['ready']) === true && is_callable($this->options['ready']) === true) { + if ( + isset($this->options['ready']) === true && + is_callable($this->options['ready']) === true + ) { // fetch last-minute options from the callback $options = (array)$this->options['ready']($this); @@ -1170,24 +1133,17 @@ class App /** * Returns any page from the content folder - * - * @param string|null $id - * @param \Kirby\Cms\Page|\Kirby\Cms\Site|null $parent - * @param bool $drafts - * @return \Kirby\Cms\Page|null */ - public function page(string|null $id = null, $parent = null, bool $drafts = true) - { + public function page( + string|null $id = null, + Page|Site|null $parent = null, + bool $drafts = true + ): Page|null { if ($id === null) { return null; } - // find by global UUID - if (Uuid::is($id, 'page') === true) { - return Uuid::for($id, $parent?->childrenAndDrafts())->model(); - } - - $parent = $parent ?? $this->site(); + $parent ??= $this->site(); if ($page = $parent->find($id)) { /** @@ -1206,8 +1162,6 @@ class App /** * Returns the request path - * - * @return string */ public function path(): string { @@ -1225,13 +1179,11 @@ class App /** * Returns the Response object for the * current request - * - * @param string|null $path - * @param string|null $method - * @return \Kirby\Http\Response */ - public function render(string $path = null, string $method = null) - { + public function render( + string|null $path = null, + string|null $method = null + ): Response|null { if (($_ENV['KIRBY_RENDER'] ?? true) === false) { return null; } @@ -1241,10 +1193,8 @@ class App /** * Returns the Request singleton - * - * @return \Kirby\Http\Request */ - public function request() + public function request(): Request { if ($this->request !== null) { return $this->request; @@ -1262,13 +1212,12 @@ class App * Path resolver for the router * * @internal - * @param string|null $path - * @param string|null $language - * @return mixed * @throws \Kirby\Exception\NotFoundException if the home page cannot be found */ - public function resolve(string $path = null, string $language = null) - { + public function resolve( + string|null $path = null, + string|null $language = null + ): mixed { // set the current translation $this->setCurrentTranslation($language); @@ -1316,6 +1265,12 @@ class App // only try to return a representation // when the page has been found if ($page) { + // if extension is the default content type, + // redirect to page URL without extension + if ($extension === 'html') { + return Response::redirect($page->url(), 301); + } + try { $response = $this->response(); $output = $page->render([], $extension); @@ -1346,29 +1301,22 @@ class App /** * Response configuration - * - * @return \Kirby\Cms\Responder */ - public function response() + public function response(): Responder { return $this->response ??= new Responder(); } /** * Returns all user roles - * - * @return \Kirby\Cms\Roles */ - public function roles() + public function roles(): Roles { return $this->roles ??= Roles::load($this->root('roles')); } /** * Returns a system root - * - * @param string $type - * @return string|null */ public function root(string $type = 'index'): string|null { @@ -1377,20 +1325,16 @@ class App /** * Returns the directory structure - * - * @return \Kirby\Cms\Ingredients */ - public function roots() + public function roots(): Ingredients { return $this->roots; } /** * Returns the currently active route - * - * @return \Kirby\Http\Route|null */ - public function route() + public function route(): Route|null { return $this->router()->route(); } @@ -1399,10 +1343,13 @@ class App * Returns the Router singleton * * @internal - * @return \Kirby\Http\Router */ - public function router() + public function router(): Router { + if ($this->router !== null) { + return $this->router; + } + $routes = $this->routes(); if ($this->multilang() === true) { @@ -1422,14 +1369,13 @@ class App } ]; - return $this->router ??= new Router($routes, $hooks); + return $this->router = new Router($routes, $hooks); } /** * Returns all defined routes * * @internal - * @return array */ public function routes(): array { @@ -1448,9 +1394,8 @@ class App * Returns the current session object * * @param array $options Additional options, see the session component - * @return \Kirby\Session\Session */ - public function session(array $options = []) + public function session(array $options = []): Session { $session = $this->sessionHandler()->get($options); @@ -1466,22 +1411,45 @@ class App /** * Returns the session handler - * - * @return \Kirby\Session\AutoSession */ - public function sessionHandler() + public function sessionHandler(): AutoSession { - $this->sessionHandler = $this->sessionHandler ?? new AutoSession($this->root('sessions'), $this->option('session', [])); - return $this->sessionHandler; + return $this->sessionHandler ??= new AutoSession( + ($this->component('session::store'))($this), + $this->option('session', []) + ); + } + + /** + * Load and set the current language if it exists + * Otherwise fall back to the default language + * + * @internal + */ + public function setCurrentLanguage( + string|null $languageCode = null + ): Language|null { + if ($this->multilang() === false) { + Locale::set($this->option('locale', 'en_US.utf-8')); + return $this->language = null; + } + + $this->language = $this->language($languageCode) ?? $this->defaultLanguage(); + + Locale::set($this->language->locale()); + + // add language slug rules to Str class + Str::$language = $this->language->rules(); + + return $this->language; } /** * Create your own set of languages * - * @param array|null $languages * @return $this */ - protected function setLanguages(array $languages = null) + protected function setLanguages(array|null $languages = null): static { if ($languages !== null) { $objects = []; @@ -1500,10 +1468,9 @@ class App * Sets the request path that is * used for the router * - * @param string|null $path * @return $this */ - protected function setPath(string $path = null) + protected function setPath(string|null $path = null): static { $this->path = $path !== null ? trim($path, '/') : null; return $this; @@ -1512,10 +1479,9 @@ class App /** * Sets the request * - * @param array|null $request * @return $this */ - protected function setRequest(array $request = null) + protected function setRequest(array|null $request = null): static { if ($request !== null) { $this->request = new Request($request); @@ -1527,15 +1493,12 @@ class App /** * Create your own set of roles * - * @param array|null $roles * @return $this */ - protected function setRoles(array $roles = null) + protected function setRoles(array|null $roles = null): static { if ($roles !== null) { - $this->roles = Roles::factory($roles, [ - 'kirby' => $this - ]); + $this->roles = Roles::factory($roles); } return $this; @@ -1544,15 +1507,12 @@ class App /** * Sets a custom Site object * - * @param \Kirby\Cms\Site|array|null $site * @return $this */ - protected function setSite($site = null) + protected function setSite(Site|array|null $site = null): static { if (is_array($site) === true) { - $site = new Site($site + [ - 'kirby' => $this - ]); + $site = new Site($site); } $this->site = $site; @@ -1561,15 +1521,12 @@ class App /** * Initializes and returns the Site object - * - * @return \Kirby\Cms\Site */ - public function site() + public function site(): Site { return $this->site ??= new Site([ 'errorPageId' => $this->options['error'] ?? 'error', 'homePageId' => $this->options['home'] ?? 'home', - 'kirby' => $this, 'url' => $this->url('index'), ]); } @@ -1578,10 +1535,8 @@ class App * Applies the smartypants rule on the text * * @internal - * @param string|null $text - * @return string */ - public function smartypants(string $text = null): string + public function smartypants(string|null $text = null): string { $options = $this->option('smartypants', []); @@ -1612,8 +1567,12 @@ class App * @param bool $return On `false`, directly echo the snippet * @psalm-return ($return is true ? string : null) */ - public function snippet(string|array|null $name, $data = [], bool $return = true, bool $slots = false): Snippet|string|null - { + public function snippet( + string|array|null $name, + array|object $data = [], + bool $return = true, + bool $slots = false + ): Snippet|string|null { if (is_object($data) === true) { $data = ['item' => $data]; } @@ -1635,10 +1594,8 @@ class App /** * System check class - * - * @return \Kirby\Cms\System */ - public function system() + public function system(): System { return $this->system ??= new System($this); } @@ -1648,23 +1605,17 @@ class App * and return the Template object * * @internal - * @return \Kirby\Template\Template - * @param string $name - * @param string $type - * @param string $defaultType */ - public function template(string $name, string $type = 'html', string $defaultType = 'html') - { + public function template( + string $name, + string $type = 'html', + string $defaultType = 'html' + ): Template { return ($this->component('template'))($this, $name, $type, $defaultType); } /** * Thumbnail creator - * - * @param string $src - * @param string $dst - * @param array $options - * @return string */ public function thumb(string $src, string $dst, array $options = []): string { @@ -1677,10 +1628,12 @@ class App * @param string $name Full event name * @param array $args Associative array of named event arguments * @param \Kirby\Cms\Event|null $originalEvent Event object (internal use) - * @return void */ - public function trigger(string $name, array $args = [], ?Event $originalEvent = null) - { + public function trigger( + string $name, + array $args = [], + Event|null $originalEvent = null + ): void { $event = $originalEvent ?? new Event($name, $args); if ($functions = $this->extension('hooks', $name)) { @@ -1719,13 +1672,13 @@ class App /** * Returns a system url * - * @param string $type * @param bool $object If set to `true`, the URL is converted to an object - * @return string|\Kirby\Http\Uri|null * @psalm-return ($object is false ? string|null : \Kirby\Http\Uri) */ - public function url(string $type = 'index', bool $object = false) - { + public function url( + string $type = 'index', + bool $object = false + ): string|Uri|null { $url = $this->urls->__get($type); if ($object === true) { @@ -1745,10 +1698,8 @@ class App /** * Returns the url structure - * - * @return \Kirby\Cms\Ingredients */ - public function urls() + public function urls(): Ingredients { return $this->urls; } @@ -1757,7 +1708,6 @@ class App * Returns the current version number from * the composer.json (Keep that up to date! :)) * - * @return string|null * @throws \Kirby\Exception\LogicException if the Kirby version cannot be detected */ public static function version(): string|null @@ -1771,8 +1721,6 @@ class App /** * Creates a hash of the version number - * - * @return string */ public static function versionHash(): string { @@ -1781,10 +1729,8 @@ class App /** * Returns the visitor object - * - * @return \Kirby\Http\Visitor */ - public function visitor() + public function visitor(): Visitor { return $this->visitor ??= new Visitor(); } diff --git a/kirby/src/Cms/AppCaches.php b/kirby/src/Cms/AppCaches.php index 0ae164f..6c1595c 100644 --- a/kirby/src/Cms/AppCaches.php +++ b/kirby/src/Cms/AppCaches.php @@ -17,15 +17,12 @@ use Kirby\Exception\InvalidArgumentException; */ trait AppCaches { - protected $caches = []; + protected array $caches = []; /** * Returns a cache instance by key - * - * @param string $key - * @return \Kirby\Cache\Cache */ - public function cache(string $key) + public function cache(string $key): Cache { if (isset($this->caches[$key]) === true) { return $this->caches[$key]; @@ -67,9 +64,6 @@ trait AppCaches /** * Returns the cache options by key - * - * @param string $key - * @return array */ protected function cacheOptions(string $key): array { @@ -106,9 +100,6 @@ trait AppCaches * Takes care of converting prefixed plugin cache setups * to the right cache key, while leaving regular cache * setups untouched. - * - * @param string $key - * @return string */ protected function cacheOptionsKey(string $key): string { diff --git a/kirby/src/Cms/AppErrors.php b/kirby/src/Cms/AppErrors.php index f61d02b..677a9c3 100644 --- a/kirby/src/Cms/AppErrors.php +++ b/kirby/src/Cms/AppErrors.php @@ -10,6 +10,7 @@ use Kirby\Toolkit\I18n; use Throwable; use Whoops\Handler\CallbackHandler; use Whoops\Handler\Handler; +use Whoops\Handler\HandlerInterface; use Whoops\Handler\PlainTextHandler; use Whoops\Handler\PrettyPageHandler; use Whoops\Run as Whoops; @@ -26,16 +27,21 @@ use Whoops\Run as Whoops; trait AppErrors { /** - * Whoops instance cache + * Allows to disable Whoops globally in CI; + * can be overridden by explicitly setting + * the `whoops` option to `true` or `false` * - * @var \Whoops\Run + * @internal */ - protected $whoops; + public static bool $enableWhoops = true; + + /** + * Whoops instance cache + */ + protected Whoops $whoops; /** * Registers the PHP error handler for CLI usage - * - * @return void */ protected function handleCliErrors(): void { @@ -45,11 +51,20 @@ trait AppErrors /** * Registers the PHP error handler * based on the environment - * - * @return void */ protected function handleErrors(): void { + // no matter the environment, exit early if + // Whoops was disabled globally + // (but continue if the option was explicitly + // set to `true` in the config) + if ( + static::$enableWhoops === false && + $this->option('whoops') !== true + ) { + return; + } + if ($this->environment()->cli() === true) { $this->handleCliErrors(); return; @@ -65,8 +80,6 @@ trait AppErrors /** * Registers the PHP error handler for HTML output - * - * @return void */ protected function handleHtmlErrors(): void { @@ -76,7 +89,7 @@ trait AppErrors if ($this->option('whoops', true) !== false) { $handler = new PrettyPageHandler(); $handler->setPageTitle('Kirby CMS Debugger'); - $handler->setResourcesPath(dirname(__DIR__, 2) . '/assets'); + $handler->addResourcePath(dirname(__DIR__, 2) . '/assets'); $handler->addCustomCss('whoops.css'); if ($editor = $this->option('editor')) { @@ -114,8 +127,6 @@ trait AppErrors /** * Registers the PHP error handler for JSON output - * - * @return void */ protected function handleJsonErrors(): void { @@ -162,11 +173,8 @@ trait AppErrors /** * Enables Whoops with the specified handler - * - * @param Callable|\Whoops\Handler\HandlerInterface $handler - * @return void */ - protected function setWhoopsHandler($handler): void + protected function setWhoopsHandler(callable|HandlerInterface $handler): void { $whoops = $this->whoops(); $whoops->clearHandlers(); @@ -182,16 +190,25 @@ trait AppErrors protected function getAdditionalWhoopsHandler(): CallbackHandler { return new CallbackHandler(function ($exception, $inspector, $run) { - $this->trigger('system.exception', compact('exception')); - error_log($exception); + $isLogged = true; + + // allow hook to modify whether the exception should be logged + $isLogged = $this->apply( + 'system.exception', + compact('exception', 'isLogged'), + 'isLogged' + ); + + if ($isLogged !== false) { + error_log($exception); + } + return Handler::DONE; }); } /** * Clears the Whoops handlers and disables Whoops - * - * @return void */ protected function unsetWhoopsHandler(): void { @@ -202,15 +219,9 @@ trait AppErrors /** * Returns the Whoops error handler instance - * - * @return \Whoops\Run */ - protected function whoops() + protected function whoops(): Whoops { - if ($this->whoops !== null) { - return $this->whoops; - } - - return $this->whoops = new Whoops(); + return $this->whoops ??= new Whoops(); } } diff --git a/kirby/src/Cms/AppPlugins.php b/kirby/src/Cms/AppPlugins.php index e0210d1..645c652 100644 --- a/kirby/src/Cms/AppPlugins.php +++ b/kirby/src/Cms/AppPlugins.php @@ -3,6 +3,7 @@ namespace Kirby\Cms; use Closure; +use Kirby\Content\Field; use Kirby\Exception\DuplicateException; use Kirby\Filesystem\Asset; use Kirby\Filesystem\Dir; @@ -10,7 +11,6 @@ use Kirby\Filesystem\F; use Kirby\Filesystem\Mime; use Kirby\Form\Field as FormField; use Kirby\Image\Image; -use Kirby\Panel\Panel; use Kirby\Text\KirbyTag; use Kirby\Toolkit\A; use Kirby\Toolkit\Collection as ToolkitCollection; @@ -29,17 +29,13 @@ trait AppPlugins { /** * A list of all registered plugins - * - * @var array */ - protected static $plugins = []; + protected static array $plugins = []; /** * The extension registry - * - * @var array */ - protected $extensions = [ + protected array $extensions = [ // load options first to make them available for the rest 'options' => [], @@ -77,6 +73,8 @@ trait AppPlugins 'sections' => [], 'siteMethods' => [], 'snippets' => [], + 'structureMethods' => [], + 'structureObjectMethods' => [], 'tags' => [], 'templates' => [], 'thirdParty' => [], @@ -90,21 +88,19 @@ trait AppPlugins /** * Flag when plugins have been loaded * to not load them again - * - * @var bool */ - protected $pluginsAreLoaded = false; + protected bool $pluginsAreLoaded = false; /** * Register all given extensions * * @internal - * @param array $extensions * @param \Kirby\Cms\Plugin $plugin|null The plugin which defined those extensions - * @return array */ - public function extend(array $extensions, Plugin $plugin = null): array - { + public function extend( + array $extensions, + Plugin|null $plugin = null + ): array { foreach ($this->extensions as $type => $registered) { if (isset($extensions[$type]) === true) { $this->{'extend' . $type}($extensions[$type], $plugin); @@ -116,11 +112,8 @@ trait AppPlugins /** * Registers API extensions - * - * @param array|bool $api - * @return array */ - protected function extendApi($api): array + protected function extendApi(array|bool $api): array { if (is_array($api) === true) { if (($api['routes'] ?? []) instanceof Closure) { @@ -135,9 +128,6 @@ trait AppPlugins /** * Registers additional custom Panel areas - * - * @param array $areas - * @return array */ protected function extendAreas(array $areas): array { @@ -151,9 +141,6 @@ trait AppPlugins /** * Registers additional asset methods - * - * @param array $methods - * @return array */ protected function extendAssetMethods(array $methods): array { @@ -162,9 +149,6 @@ trait AppPlugins /** * Registers additional authentication challenges - * - * @param array $challenges - * @return array */ protected function extendAuthChallenges(array $challenges): array { @@ -173,9 +157,6 @@ trait AppPlugins /** * Registers additional block methods - * - * @param array $methods - * @return array */ protected function extendBlockMethods(array $methods): array { @@ -184,9 +165,6 @@ trait AppPlugins /** * Registers additional block models - * - * @param array $models - * @return array */ protected function extendBlockModels(array $models): array { @@ -195,9 +173,6 @@ trait AppPlugins /** * Registers additional blocks methods - * - * @param array $methods - * @return array */ protected function extendBlocksMethods(array $methods): array { @@ -206,9 +181,6 @@ trait AppPlugins /** * Registers additional blueprints - * - * @param array $blueprints - * @return array */ protected function extendBlueprints(array $blueprints): array { @@ -217,9 +189,6 @@ trait AppPlugins /** * Registers additional cache types - * - * @param array $cacheTypes - * @return array */ protected function extendCacheTypes(array $cacheTypes): array { @@ -228,9 +197,6 @@ trait AppPlugins /** * Registers additional CLI commands - * - * @param array $commands - * @return array */ protected function extendCommands(array $commands): array { @@ -239,9 +205,6 @@ trait AppPlugins /** * Registers additional collection filters - * - * @param array $filters - * @return array */ protected function extendCollectionFilters(array $filters): array { @@ -250,9 +213,6 @@ trait AppPlugins /** * Registers additional collection methods - * - * @param array $methods - * @return array */ protected function extendCollectionMethods(array $methods): array { @@ -261,9 +221,6 @@ trait AppPlugins /** * Registers additional collections - * - * @param array $collections - * @return array */ protected function extendCollections(array $collections): array { @@ -272,9 +229,6 @@ trait AppPlugins /** * Registers core components - * - * @param array $components - * @return array */ protected function extendComponents(array $components): array { @@ -283,9 +237,6 @@ trait AppPlugins /** * Registers additional controllers - * - * @param array $controllers - * @return array */ protected function extendControllers(array $controllers): array { @@ -294,9 +245,6 @@ trait AppPlugins /** * Registers additional file methods - * - * @param array $methods - * @return array */ protected function extendFileMethods(array $methods): array { @@ -305,9 +253,6 @@ trait AppPlugins /** * Registers additional custom file types and mimes - * - * @param array $fileTypes - * @return array */ protected function extendFileTypes(array $fileTypes): array { @@ -358,9 +303,6 @@ trait AppPlugins /** * Registers additional files methods - * - * @param array $methods - * @return array */ protected function extendFilesMethods(array $methods): array { @@ -369,9 +311,6 @@ trait AppPlugins /** * Registers additional field methods - * - * @param array $methods - * @return array */ protected function extendFieldMethods(array $methods): array { @@ -380,9 +319,6 @@ trait AppPlugins /** * Registers Panel fields - * - * @param array $fields - * @return array */ protected function extendFields(array $fields): array { @@ -391,9 +327,6 @@ trait AppPlugins /** * Registers hooks - * - * @param array $hooks - * @return array */ protected function extendHooks(array $hooks): array { @@ -414,20 +347,14 @@ trait AppPlugins /** * Registers markdown component - * - * @param Closure $markdown - * @return Closure */ - protected function extendMarkdown(Closure $markdown) + protected function extendMarkdown(Closure $markdown): Closure { return $this->extensions['markdown'] = $markdown; } /** * Registers additional layout methods - * - * @param array $methods - * @return array */ protected function extendLayoutMethods(array $methods): array { @@ -436,9 +363,6 @@ trait AppPlugins /** * Registers additional layout column methods - * - * @param array $methods - * @return array */ protected function extendLayoutColumnMethods(array $methods): array { @@ -447,9 +371,6 @@ trait AppPlugins /** * Registers additional layouts methods - * - * @param array $methods - * @return array */ protected function extendLayoutsMethods(array $methods): array { @@ -458,13 +379,11 @@ trait AppPlugins /** * Registers additional options - * - * @param array $options - * @param \Kirby\Cms\Plugin|null $plugin - * @return array */ - protected function extendOptions(array $options, Plugin $plugin = null): array - { + protected function extendOptions( + array $options, + Plugin|null $plugin = null + ): array { if ($plugin !== null) { $options = [$plugin->prefix() => $options]; } @@ -474,9 +393,6 @@ trait AppPlugins /** * Registers additional page methods - * - * @param array $methods - * @return array */ protected function extendPageMethods(array $methods): array { @@ -485,9 +401,6 @@ trait AppPlugins /** * Registers additional pages methods - * - * @param array $methods - * @return array */ protected function extendPagesMethods(array $methods): array { @@ -496,9 +409,6 @@ trait AppPlugins /** * Registers additional page models - * - * @param array $models - * @return array */ protected function extendPageModels(array $models): array { @@ -507,9 +417,6 @@ trait AppPlugins /** * Registers pages - * - * @param array $pages - * @return array */ protected function extendPages(array $pages): array { @@ -518,13 +425,11 @@ trait AppPlugins /** * Registers additional permissions - * - * @param array $permissions - * @param \Kirby\Cms\Plugin|null $plugin - * @return array */ - protected function extendPermissions(array $permissions, Plugin $plugin = null): array - { + protected function extendPermissions( + array $permissions, + Plugin|null $plugin = null + ): array { if ($plugin !== null) { $permissions = [$plugin->prefix() => $permissions]; } @@ -534,11 +439,8 @@ trait AppPlugins /** * Registers additional routes - * - * @param array|\Closure $routes - * @return array */ - protected function extendRoutes($routes): array + protected function extendRoutes(array|Closure $routes): array { if ($routes instanceof Closure) { $routes = $routes($this); @@ -549,9 +451,6 @@ trait AppPlugins /** * Registers Panel sections - * - * @param array $sections - * @return array */ protected function extendSections(array $sections): array { @@ -560,9 +459,6 @@ trait AppPlugins /** * Registers additional site methods - * - * @param array $methods - * @return array */ protected function extendSiteMethods(array $methods): array { @@ -571,31 +467,38 @@ trait AppPlugins /** * Registers SmartyPants component - * - * @param \Closure $smartypants - * @return \Closure */ - protected function extendSmartypants(Closure $smartypants) + protected function extendSmartypants(Closure $smartypants): Closure { return $this->extensions['smartypants'] = $smartypants; } /** * Registers additional snippets - * - * @param array $snippets - * @return array */ protected function extendSnippets(array $snippets): array { return $this->extensions['snippets'] = array_merge($this->extensions['snippets'], $snippets); } + /** + * Registers additional structure methods + */ + protected function extendStructureMethods(array $methods): array + { + return $this->extensions['structureMethods'] = Structure::$methods = array_merge(Structure::$methods, $methods); + } + + /** + * Registers additional structure object methods + */ + protected function extendStructureObjectMethods(array $methods): array + { + return $this->extensions['structureObjectMethods'] = StructureObject::$methods = array_merge(StructureObject::$methods, $methods); + } + /** * Registers additional KirbyTags - * - * @param array $tags - * @return array */ protected function extendTags(array $tags): array { @@ -604,9 +507,6 @@ trait AppPlugins /** * Registers additional templates - * - * @param array $templates - * @return array */ protected function extendTemplates(array $templates): array { @@ -615,9 +515,6 @@ trait AppPlugins /** * Registers translations - * - * @param array $translations - * @return array */ protected function extendTranslations(array $translations): array { @@ -628,9 +525,6 @@ trait AppPlugins * Add third party extensions to the registry * so they can be used as plugins for plugins * for example. - * - * @param array $extensions - * @return array */ protected function extendThirdParty(array $extensions): array { @@ -639,9 +533,6 @@ trait AppPlugins /** * Registers additional user methods - * - * @param array $methods - * @return array */ protected function extendUserMethods(array $methods): array { @@ -650,9 +541,6 @@ trait AppPlugins /** * Registers additional user models - * - * @param array $models - * @return array */ protected function extendUserModels(array $models): array { @@ -661,9 +549,6 @@ trait AppPlugins /** * Registers additional users methods - * - * @param array $methods - * @return array */ protected function extendUsersMethods(array $methods): array { @@ -672,9 +557,6 @@ trait AppPlugins /** * Registers additional custom validators - * - * @param array $validators - * @return array */ protected function extendValidators(array $validators): array { @@ -687,11 +569,12 @@ trait AppPlugins * @internal * @param string $type i.e. `'hooks'` * @param string $name i.e. `'page.delete:before'` - * @param mixed $fallback - * @return mixed */ - public function extension(string $type, string $name, $fallback = null) - { + public function extension( + string $type, + string $name, + mixed $fallback = null + ): mixed { return $this->extensions($type)[$name] ?? $fallback; } @@ -699,10 +582,8 @@ trait AppPlugins * Returns the extensions registry * * @internal - * @param string|null $type - * @return array */ - public function extensions(string $type = null) + public function extensions(string|null $type = null): array { if ($type === null) { return $this->extensions; @@ -716,7 +597,7 @@ trait AppPlugins * This is only used for models for now, but * could be extended later */ - protected function extensionsFromFolders() + protected function extensionsFromFolders(): void { $models = []; @@ -739,10 +620,8 @@ trait AppPlugins * Register extensions that could be located in * the options array. I.e. hooks and routes can be * setup from the config. - * - * @return void */ - protected function extensionsFromOptions() + protected function extensionsFromOptions(): void { // register routes and hooks from options $this->extend([ @@ -754,10 +633,8 @@ trait AppPlugins /** * Apply all plugin extensions - * - * @return void */ - protected function extensionsFromPlugins() + protected function extensionsFromPlugins(): void { // register all their extensions foreach ($this->plugins() as $plugin) { @@ -771,22 +648,22 @@ trait AppPlugins /** * Apply all passed extensions - * - * @param array $props - * @return void */ - protected function extensionsFromProps(array $props) + protected function extensionsFromProps(array $props): void { $this->extend($props); } /** * Apply all default extensions - * - * @return void */ - protected function extensionsFromSystem() + protected function extensionsFromSystem(): void { + // Always start with fresh fields and sections + // from the core and add plugins on top of that + FormField::$types = []; + Section::$types = []; + // mixins FormField::$mixins = $this->core->fieldMixins(); Section::$mixins = $this->core->sectionMixins(); @@ -802,8 +679,8 @@ trait AppPlugins $this->extendCacheTypes($this->core->cacheTypes()); $this->extendComponents($this->core->components()); $this->extendBlueprints($this->core->blueprints()); - $this->extendFields($this->core->fields()); $this->extendFieldMethods($this->core->fieldMethods()); + $this->extendFields($this->core->fields()); $this->extendSections($this->core->sections()); $this->extendSnippets($this->core->snippets()); $this->extendTags($this->core->kirbyTags()); @@ -813,9 +690,6 @@ trait AppPlugins /** * Checks if a native component was extended * @since 3.7.0 - * - * @param string $component - * @return bool */ public function isNativeComponent(string $component): bool { @@ -825,11 +699,8 @@ trait AppPlugins /** * Returns the native implementation * of a core component - * - * @param string $component - * @return \Closure|false */ - public function nativeComponent(string $component) + public function nativeComponent(string $component): Closure|false { return $this->core->components()[$component] ?? false; } @@ -837,22 +708,30 @@ trait AppPlugins /** * Kirby plugin factory and getter * - * @param string $name * @param array|null $extends If null is passed it will be used as getter. Otherwise as factory. - * @return \Kirby\Cms\Plugin|null * @throws \Kirby\Exception\DuplicateException */ - public static function plugin(string $name, array $extends = null) - { + public static function plugin( + string $name, + array|null $extends = null, + array $info = [], + string|null $root = null, + string|null $version = null + ): Plugin|null { if ($extends === null) { return static::$plugins[$name] ?? null; } - // get the correct root for the plugin - $extends['root'] = $extends['root'] ?? dirname(debug_backtrace()[0]['file']); + $plugin = new Plugin( + name: $name, + extends: $extends, + info: $info, + // TODO: Remove fallback to $extends in v7 + root: $root ?? $extends['root'] ?? dirname(debug_backtrace()[0]['file']), + version: $version + ); - $plugin = new Plugin($name, $extends); - $name = $plugin->name(); + $name = $plugin->name(); if (isset(static::$plugins[$name]) === true) { throw new DuplicateException('The plugin "' . $name . '" has already been registered'); @@ -867,9 +746,8 @@ trait AppPlugins * * @internal * @param array|null $plugins Can be used to overwrite the plugins registry - * @return array */ - public function plugins(array $plugins = null): array + public function plugins(array|null $plugins = null): array { // overwrite the existing plugins registry if ($plugins !== null) { @@ -922,7 +800,11 @@ trait AppPlugins // register as anonymous plugin (without actual extensions) // to be picked up by the Panel\Document class when // rendering the Panel view - static::plugin('plugins/' . $dirname, ['root' => $dir]); + static::plugin( + name: 'plugins/' . $dirname, + extends: [], + root: $dir + ); } else { continue; } diff --git a/kirby/src/Cms/AppTranslations.php b/kirby/src/Cms/AppTranslations.php index 74f332c..1e24f23 100644 --- a/kirby/src/Cms/AppTranslations.php +++ b/kirby/src/Cms/AppTranslations.php @@ -3,7 +3,6 @@ namespace Kirby\Cms; use Kirby\Toolkit\I18n; -use Kirby\Toolkit\Locale; use Kirby\Toolkit\Str; /** @@ -17,12 +16,10 @@ use Kirby\Toolkit\Str; */ trait AppTranslations { - protected $translations; + protected Translations|null $translations = null; /** * Setup internationalization - * - * @return void */ protected function i18n(): void { @@ -88,8 +85,6 @@ trait AppTranslations * Returns the language code that will be used * for the Panel if no user is logged in or if * no language is configured for the user - * - * @return string */ public function panelLanguage(): string { @@ -108,42 +103,12 @@ trait AppTranslations return $this->option('panel.language', $defaultCode); } - /** - * Load and set the current language if it exists - * Otherwise fall back to the default language - * - * @internal - * @param string|null $languageCode - * @return \Kirby\Cms\Language|null - */ - public function setCurrentLanguage(string $languageCode = null) - { - if ($this->multilang() === false) { - Locale::set($this->option('locale', 'en_US.utf-8')); - return $this->language = null; - } - - $this->language = $this->language($languageCode); - $this->language ??= $this->defaultLanguage(); - - if ($this->language) { - Locale::set($this->language->locale()); - } - - // add language slug rules to Str class - Str::$language = $this->language->rules(); - - return $this->language; - } - /** * Set the current translation * * @internal - * @param string|null $translationCode - * @return void */ - public function setCurrentTranslation(string $translationCode = null): void + public function setCurrentTranslation(string|null $translationCode = null): void { I18n::$locale = $translationCode ?? 'en'; } @@ -152,11 +117,10 @@ trait AppTranslations * Load a specific translation by locale * * @param string|null $locale Locale name or `null` for the current locale - * @return \Kirby\Cms\Translation */ - public function translation(string|null $locale = null) + public function translation(string|null $locale = null): Translation { - $locale = $locale ?? I18n::locale(); + $locale ??= I18n::locale(); $locale = basename($locale); // prefer loading them from the translations collection @@ -180,10 +144,8 @@ trait AppTranslations /** * Returns all available translations - * - * @return \Kirby\Cms\Translations */ - public function translations() + public function translations(): Translations { if ($this->translations instanceof Translations) { return $this->translations; @@ -207,8 +169,6 @@ trait AppTranslations } } - $this->translations = Translations::load($this->root('i18n:translations'), $translations); - - return $this->translations; + return $this->translations = Translations::load($this->root('i18n:translations'), $translations); } } diff --git a/kirby/src/Cms/AppUsers.php b/kirby/src/Cms/AppUsers.php index a370b3a..6f2f7be 100644 --- a/kirby/src/Cms/AppUsers.php +++ b/kirby/src/Cms/AppUsers.php @@ -16,20 +16,15 @@ use Throwable; */ trait AppUsers { - /** - * Cache for the auth auth layer - * - * @var Auth - */ - protected $auth; + protected Auth|null $auth = null; + protected User|string|null $user = null; + protected Users|null $users = null; /** * Returns the Authentication layer class - * * @internal - * @return \Kirby\Cms\Auth */ - public function auth() + public function auth(): Auth { return $this->auth ??= new Auth($this); } @@ -48,8 +43,10 @@ trait AppUsers * if called with callback: Return value from the callback * @throws \Throwable */ - public function impersonate(string|null $who = null, Closure|null $callback = null) - { + public function impersonate( + string|null $who = null, + Closure|null $callback = null + ): mixed { $auth = $this->auth(); $userBefore = $auth->currentUserFromImpersonation(); @@ -73,10 +70,9 @@ trait AppUsers /** * Set the currently active user id * - * @param \Kirby\Cms\User|string $user - * @return \Kirby\Cms\App + * @return $this */ - protected function setUser($user = null) + protected function setUser(User|string|null $user = null): static { $this->user = $user; return $this; @@ -85,15 +81,12 @@ trait AppUsers /** * Create your own set of app users * - * @param array|null $users - * @return \Kirby\Cms\App + * @return $this */ - protected function setUsers(array $users = null) + protected function setUsers(array|null $users = null): static { if ($users !== null) { - $this->users = Users::factory($users, [ - 'kirby' => $this - ]); + $this->users = Users::factory($users); } return $this; @@ -103,14 +96,14 @@ trait AppUsers * Returns a specific user by id * or the current user if no id is given * - * @param string|null $id * @param bool $allowImpersonation If set to false, only the actually * logged in user will be returned * (when `$id` is passed as `null`) - * @return \Kirby\Cms\User|null */ - public function user(string|null $id = null, bool $allowImpersonation = true) - { + public function user( + string|null $id = null, + bool $allowImpersonation = true + ): User|null { if ($id !== null) { return $this->users()->find($id); } @@ -128,15 +121,11 @@ trait AppUsers /** * Returns all users - * - * @return \Kirby\Cms\Users */ - public function users() + public function users(): Users { - if ($this->users instanceof Users) { - return $this->users; - } - - return $this->users = Users::load($this->root('accounts'), ['kirby' => $this]); + return $this->users ??= Users::load( + $this->root('accounts'), + ); } } diff --git a/kirby/src/Cms/Auth.php b/kirby/src/Cms/Auth.php index 6f9ed82..689f567 100644 --- a/kirby/src/Cms/Auth.php +++ b/kirby/src/Cms/Auth.php @@ -32,55 +32,37 @@ class Auth /** * Available auth challenge classes * from the core and plugins - * - * @var array */ - public static $challenges = []; + public static array $challenges = []; /** * Currently impersonated user - * - * @var \Kirby\Cms\User|null */ - protected $impersonate; - - /** - * Kirby instance - * - * @var \Kirby\Cms\App - */ - protected $kirby; + protected User|null $impersonate = null; /** * Cache of the auth status object - * - * @var \Kirby\Cms\Auth\Status */ - protected $status; + protected Status|null $status = null; /** * Instance of the currently logged in user or * `false` if the user was not yet determined - * - * @var \Kirby\Cms\User|null|false */ - protected $user = false; + protected User|false|null $user = false; /** * Exception that was thrown while * determining the current user - * - * @var \Throwable */ - protected $userException; + protected Throwable|null $userException = null; /** - * @param \Kirby\Cms\App $kirby * @codeCoverageIgnore */ - public function __construct(App $kirby) - { - $this->kirby = $kirby; + public function __construct( + protected App $kirby + ) { } /** @@ -88,17 +70,18 @@ class Auth * (one-time auth code) * @since 3.5.0 * - * @param string $email * @param bool $long If `true`, a long session will be created - * @param string $mode Either 'login' or 'password-reset' - * @return \Kirby\Cms\Auth\Status + * @param 'login'|'password-reset'|'2fa' $mode Purpose of the code * * @throws \Kirby\Exception\LogicException If there is no suitable authentication challenge (only in debug mode) * @throws \Kirby\Exception\NotFoundException If the user does not exist (only in debug mode) * @throws \Kirby\Exception\PermissionException If the rate limit is exceeded */ - public function createChallenge(string $email, bool $long = false, string $mode = 'login') - { + public function createChallenge( + string $email, + bool $long = false, + string $mode = 'login' + ): Status { $email = Idn::decodeEmail($email); $session = $this->kirby->session([ @@ -145,7 +128,10 @@ class Auth $session->set('kirby.challenge.type', $challenge); if ($code !== null) { - $session->set('kirby.challenge.code', password_hash($code, PASSWORD_DEFAULT)); + $session->set( + 'kirby.challenge.code', + password_hash($code, PASSWORD_DEFAULT) + ); } break; @@ -179,10 +165,8 @@ class Auth /** * Returns the csrf token if it exists and if it is valid - * - * @return string|false */ - public function csrf() + public function csrf(): string|false { // get the csrf from the header $fromHeader = $this->kirby->request()->csrf(); @@ -201,8 +185,6 @@ class Auth /** * Returns either predefined csrf or the one from session * @since 3.6.0 - * - * @return string */ public function csrfFromSession(): string { @@ -217,11 +199,10 @@ class Auth * valid credentials * * @param \Kirby\Http\Request\Auth\BasicAuth|null $auth - * @return \Kirby\Cms\User|null * @throws \Kirby\Exception\InvalidArgumentException if the authorization header is invalid * @throws \Kirby\Exception\PermissionException if basic authentication is not allowed */ - public function currentUserFromBasicAuth(BasicAuth $auth = null) + public function currentUserFromBasicAuth(BasicAuth|null $auth = null): User|null { if ($this->kirby->option('api.basicAuth', false) !== true) { throw new PermissionException('Basic authentication is not activated'); @@ -240,8 +221,8 @@ class Auth } } - $request = $this->kirby->request(); - $auth = $auth ?? $request->auth(); + $request = $this->kirby->request(); + $auth ??= $request->auth(); if (!$auth || $auth->type() !== 'basic') { throw new InvalidArgumentException('Invalid authorization header'); @@ -257,10 +238,8 @@ class Auth /** * Returns the currently impersonated user - * - * @return \Kirby\Cms\User|null */ - public function currentUserFromImpersonation() + public function currentUserFromImpersonation(): User|null { return $this->impersonate; } @@ -269,12 +248,10 @@ class Auth * Returns the logged in user by checking * the current session and finding a valid * valid user id in there - * - * @param \Kirby\Session\Session|array|null $session - * @return \Kirby\Cms\User|null */ - public function currentUserFromSession($session = null) - { + public function currentUserFromSession( + Session|array|null $session = null + ): User|null { $session = $this->session($session); $id = $session->data()->get('kirby.userId'); @@ -318,12 +295,12 @@ class Auth * Returns the list of enabled challenges in the * configured order * @since 3.5.1 - * - * @return array */ public function enabledChallenges(): array { - return A::wrap($this->kirby->option('auth.challenges', ['email'])); + return A::wrap( + $this->kirby->option('auth.challenges', ['totp', 'email']) + ); } /** @@ -333,10 +310,9 @@ class Auth * `null` to use the actual user again, * `'kirby'` for a virtual admin user or * `'nobody'` to disable the actual user - * @return \Kirby\Cms\User|null * @throws \Kirby\Exception\NotFoundException if the given user cannot be found */ - public function impersonate(string|null $who = null) + public function impersonate(string|null $who = null): User|null { // clear the status cache $this->status = null; @@ -360,8 +336,6 @@ class Auth /** * Returns the hashed ip of the visitor * which is used to track invalid logins - * - * @return string */ public function ipHash(): string { @@ -373,9 +347,6 @@ class Auth /** * Check if logins are blocked for the current ip or email - * - * @param string $email - * @return bool */ public function isBlocked(string $email): bool { @@ -450,16 +421,12 @@ class Auth /** * Sets a user object as the current user in the cache * @internal - * - * @param \Kirby\Cms\User $user - * @return void */ public function setUser(User $user): void { // stop impersonating $this->impersonate = null; - - $this->user = $user; + $this->user = $user; // clear the status cache $this->status = null; @@ -469,13 +436,13 @@ class Auth * Returns the authentication status object * @since 3.5.1 * - * @param \Kirby\Session\Session|array|null $session * @param bool $allowImpersonation If set to false, only the actually * logged in user will be returned - * @return \Kirby\Cms\Auth\Status */ - public function status($session = null, bool $allowImpersonation = true) - { + public function status( + Session|array|null $session = null, + bool $allowImpersonation = true + ): Status { // try to return from cache if ($this->status && $session === null && $allowImpersonation === true) { return $this->status; @@ -588,8 +555,6 @@ class Auth /** * Returns the absolute path to the logins log - * - * @return string */ public function logfile(): string { @@ -598,8 +563,6 @@ class Auth /** * Read all tracked logins - * - * @return array */ public function log(): array { @@ -612,8 +575,8 @@ class Auth } // ensure that the category arrays are defined - $log['by-ip'] = $log['by-ip'] ?? []; - $log['by-email'] = $log['by-email'] ?? []; + $log['by-ip'] ??= []; + $log['by-email'] ??= []; // remove all elements on the top level with different keys (old structure) $log = array_intersect_key($log, array_flip(['by-ip', 'by-email'])); @@ -642,8 +605,6 @@ class Auth /** * Logout the current user - * - * @return void */ public function logout(): void { @@ -668,8 +629,6 @@ class Auth /** * Clears the cached user data after logout * @internal - * - * @return void */ public function flush(): void { @@ -681,12 +640,12 @@ class Auth /** * Tracks a login * - * @param string|null $email * @param bool $triggerHook If `false`, no user.login:failed hook is triggered - * @return bool */ - public function track(string|null $email, bool $triggerHook = true): bool - { + public function track( + string|null $email, + bool $triggerHook = true + ): bool { if ($triggerHook === true) { $this->kirby->trigger('user.login:failed', compact('email')); } @@ -730,7 +689,6 @@ class Auth * @param bool $allowImpersonation If set to false, 'impersonate' won't * be returned as authentication type * even if an impersonation is active - * @return string */ public function type(bool $allowImpersonation = true): string { @@ -759,15 +717,15 @@ class Auth /** * Validates the currently logged in user * - * @param \Kirby\Session\Session|array|null $session * @param bool $allowImpersonation If set to false, only the actually * logged in user will be returned - * @return \Kirby\Cms\User|null * * @throws \Throwable If an authentication error occurred */ - public function user($session = null, bool $allowImpersonation = true) - { + public function user( + Session|array|null $session = null, + bool $allowImpersonation = true + ): User|null { if ($allowImpersonation === true && $this->impersonate !== null) { return $this->impersonate; } @@ -820,7 +778,7 @@ class Auth public function verifyChallenge( #[SensitiveParameter] string $code - ) { + ): User { try { $session = $this->kirby->session(); @@ -887,9 +845,11 @@ class Auth throw new PermissionException(['key' => 'access.code']); } - throw new LogicException('Invalid authentication challenge: ' . $challenge); + throw new LogicException( + 'Invalid authentication challenge: ' . $challenge + ); } catch (Throwable $e) { - $details = $e instanceof \Kirby\Exception\Exception ? $e->getDetails() : []; + $details = $e instanceof Exception ? $e->getDetails() : []; if ( empty($email) === false && @@ -925,8 +885,10 @@ class Auth * @throws \Throwable Either the passed `$exception` or the `$fallback` * (no exception if debugging is disabled and no fallback was passed) */ - protected function fail(Throwable $exception, Throwable $fallback = null): void - { + protected function fail( + Throwable $exception, + Throwable|null $fallback = null + ): void { $debug = $this->kirby->option('auth.debug', 'log'); // throw the original exception only in debug mode @@ -948,11 +910,8 @@ class Auth /** * Creates a session object from the passed options - * - * @param \Kirby\Session\Session|array|null $session - * @return \Kirby\Session\Session */ - protected function session($session = null) + protected function session(Session|array|null $session = null): Session { // use passed session options or session object if set if (is_array($session) === true) { diff --git a/kirby/src/Cms/Auth/Challenge.php b/kirby/src/Cms/Auth/Challenge.php index 1897dfa..cee81b9 100644 --- a/kirby/src/Cms/Auth/Challenge.php +++ b/kirby/src/Cms/Auth/Challenge.php @@ -22,8 +22,7 @@ abstract class Challenge * for the passed user and purpose * * @param \Kirby\Cms\User $user User the code will be generated for - * @param string $mode Purpose of the code ('login', 'reset' or '2fa') - * @return bool + * @param 'login'|'password-reset'|'2fa' $mode Purpose of the code */ abstract public static function isAvailable(User $user, string $mode): bool; @@ -33,7 +32,7 @@ abstract class Challenge * * @param \Kirby\Cms\User $user User to generate the code for * @param array $options Details of the challenge request: - * - 'mode': Purpose of the code ('login', 'reset' or '2fa') + * - 'mode': Purpose of the code ('login', 'password-reset' or '2fa') * - 'timeout': Number of seconds the code will be valid for * @return string|null The generated and sent code or `null` in case * there was no code to generate by this algorithm @@ -47,7 +46,6 @@ abstract class Challenge * * @param \Kirby\Cms\User $user User to check the code for * @param string $code Code to verify - * @return bool */ public static function verify( User $user, diff --git a/kirby/src/Cms/Auth/EmailChallenge.php b/kirby/src/Cms/Auth/EmailChallenge.php index e0bcfbe..e2bef6f 100644 --- a/kirby/src/Cms/Auth/EmailChallenge.php +++ b/kirby/src/Cms/Auth/EmailChallenge.php @@ -23,8 +23,7 @@ class EmailChallenge extends Challenge * for the passed user and purpose * * @param \Kirby\Cms\User $user User the code will be generated for - * @param string $mode Purpose of the code ('login', 'reset' or '2fa') - * @return bool + * @param 'login'|'password-reset'|'2fa' $mode Purpose of the code */ public static function isAvailable(User $user, string $mode): bool { @@ -37,7 +36,7 @@ class EmailChallenge extends Challenge * * @param \Kirby\Cms\User $user User to generate the code for * @param array $options Details of the challenge request: - * - 'mode': Purpose of the code ('login', 'reset' or '2fa') + * - 'mode': Purpose of the code ('login', 'password-reset' or '2fa') * - 'timeout': Number of seconds the code will be valid for * @return string The generated and sent code */ diff --git a/kirby/src/Cms/Auth/Status.php b/kirby/src/Cms/Auth/Status.php index adf8bb5..ec43107 100644 --- a/kirby/src/Cms/Auth/Status.php +++ b/kirby/src/Cms/Auth/Status.php @@ -3,6 +3,7 @@ namespace Kirby\Cms\Auth; use Kirby\Cms\App; +use Kirby\Cms\User; use Kirby\Exception\InvalidArgumentException; use Kirby\Toolkit\Properties; @@ -19,44 +20,32 @@ use Kirby\Toolkit\Properties; */ class Status { - use Properties; - /** * Type of the active challenge - * - * @var string|null */ - protected $challenge = null; + protected string|null $challenge = null; /** * Challenge type to use as a fallback * when $challenge is `null` - * - * @var string|null */ - protected $challengeFallback = null; + protected string|null $challengeFallback = null; /** * Email address of the current/pending user - * - * @var string|null */ - protected $email = null; + protected string|null $email; /** * Kirby instance for user lookup - * - * @var \Kirby\Cms\App */ - protected $kirby; + protected App $kirby; /** * Authentication status: * `active|impersonated|pending|inactive` - * - * @var string */ - protected $status; + protected string $status; /** * Class constructor @@ -65,13 +54,24 @@ class Status */ public function __construct(array $props) { - $this->setProperties($props); + if (in_array($props['status'], ['active', 'impersonated', 'pending', 'inactive']) !== true) { + throw new InvalidArgumentException([ + 'data' => [ + 'argument' => '$props[\'status\']', + 'method' => 'Status::__construct' + ] + ]); + } + + $this->kirby = $props['kirby']; + $this->challenge = $props['challenge'] ?? null; + $this->challengeFallback = $props['challengeFallback'] ?? null; + $this->email = $props['email'] ?? null; + $this->status = $props['status']; } /** * Returns the authentication status - * - * @return string */ public function __toString(): string { @@ -84,7 +84,6 @@ class Status * @param bool $automaticFallback If set to `false`, no faked challenge is returned; * WARNING: never send the resulting `null` value to the * user to avoid leaking whether the pending user exists - * @return string|null */ public function challenge(bool $automaticFallback = true): string|null { @@ -100,10 +99,23 @@ class Status return $this->challenge ?? $this->challengeFallback; } + /** + * Creates a new instance while + * merging initial and new properties + */ + public function clone(array $props = []): static + { + return new static(array_replace_recursive([ + 'kirby' => $this->kirby, + 'challenge' => $this->challenge, + 'challengeFallback' => $this->challengeFallback, + 'email' => $this->email, + 'status' => $this->status, + ], $props)); + } + /** * Returns the email address of the current/pending user - * - * @return string|null */ public function email(): string|null { @@ -122,8 +134,6 @@ class Status /** * Returns an array with all public status data - * - * @return array */ public function toArray(): array { @@ -136,10 +146,8 @@ class Status /** * Returns the currently logged in user - * - * @return \Kirby\Cms\User */ - public function user() + public function user(): User|null { // for security, only return the user if they are // already logged in @@ -149,71 +157,4 @@ class Status return $this->kirby->user($this->email()); } - - /** - * Sets the type of the active challenge - * - * @param string|null $challenge - * @return $this - */ - protected function setChallenge(string|null $challenge = null) - { - $this->challenge = $challenge; - return $this; - } - - /** - * Sets the challenge type to use as - * a fallback when $challenge is `null` - * - * @param string|null $challengeFallback - * @return $this - */ - protected function setChallengeFallback(string|null $challengeFallback = null) - { - $this->challengeFallback = $challengeFallback; - return $this; - } - - /** - * Sets the email address of the current/pending user - * - * @param string|null $email - * @return $this - */ - protected function setEmail(string|null $email = null) - { - $this->email = $email; - return $this; - } - - /** - * Sets the Kirby instance for user lookup - * - * @param \Kirby\Cms\App $kirby - * @return $this - */ - protected function setKirby(App $kirby) - { - $this->kirby = $kirby; - return $this; - } - - /** - * Sets the authentication status - * - * @param string $status `active|impersonated|pending|inactive` - * @return $this - */ - protected function setStatus(string $status) - { - if (in_array($status, ['active', 'impersonated', 'pending', 'inactive']) !== true) { - throw new InvalidArgumentException([ - 'data' => ['argument' => '$props[\'status\']', 'method' => 'Status::__construct'] - ]); - } - - $this->status = $status; - return $this; - } } diff --git a/kirby/src/Cms/Auth/TotpChallenge.php b/kirby/src/Cms/Auth/TotpChallenge.php new file mode 100644 index 0000000..67142db --- /dev/null +++ b/kirby/src/Cms/Auth/TotpChallenge.php @@ -0,0 +1,65 @@ + + * @link https://getkirby.com + * @copyright Bastian Allgeier + * @license https://getkirby.com/license + */ +class TotpChallenge extends Challenge +{ + /** + * Checks whether the challenge is available + * for the passed user and purpose + * + * @param \Kirby\Cms\User $user User the code will be generated for + * @param 'login'|'password-reset'|'2fa' $mode Purpose of the code + */ + public static function isAvailable(User $user, string $mode): bool + { + // user needs to have a TOTP secret set up + return $user->secret('totp') !== null; + } + + /** + * Generates a random one-time auth code and returns that code + * for later verification + * + * @param \Kirby\Cms\User $user User to generate the code for + * @param array $options Details of the challenge request: + * - 'mode': Purpose of the code ('login', 'password-reset' or '2fa') + * - 'timeout': Number of seconds the code will be valid for + * @todo set return type to `null` once support for PHP 8.1 is dropped + */ + public static function create(User $user, array $options): string|null + { + // the user's app will generate the code, we only verify it + return null; + } + + /** + * Verifies the provided code against the created one + * + * @param \Kirby\Cms\User $user User to check the code for + * @param string $code Code to verify + */ + public static function verify(User $user, string $code): bool + { + // verify if code is current, previous or next TOTP code + $secret = $user->secret('totp'); + $totp = new Totp($secret); + return $totp->verify($code); + } +} diff --git a/kirby/src/Cms/Block.php b/kirby/src/Cms/Block.php index 81dbe75..f83c547 100644 --- a/kirby/src/Cms/Block.php +++ b/kirby/src/Cms/Block.php @@ -2,6 +2,8 @@ namespace Kirby\Cms; +use Kirby\Content\Content; +use Kirby\Content\Field; use Kirby\Exception\InvalidArgumentException; use Kirby\Toolkit\Str; use Throwable; @@ -24,36 +26,19 @@ class Block extends Item public const ITEMS_CLASS = Blocks::class; - /** - * @var \Kirby\Cms\Content - */ - protected $content; - - /** - * @var bool - */ - protected $isHidden; - /** * Registry with all block models - * - * @var array */ - public static $models = []; + public static array $models = []; - /** - * @var string - */ - protected $type; + protected Content $content; + protected bool $isHidden; + protected string $type; /** * Proxy for content fields - * - * @param string $method - * @param array $args - * @return \Kirby\Cms\Field */ - public function __call(string $method, array $args = []) + public function __call(string $method, array $args = []): mixed { // block methods if ($this->hasMethod($method)) { @@ -66,7 +51,6 @@ class Block extends Item /** * Creates a new block object * - * @param array $params * @throws \Kirby\Exception\InvalidArgumentException */ public function __construct(array $params) @@ -90,18 +74,15 @@ class Block extends Item $params['content'] = []; } - $this->content = $params['content']; $this->isHidden = $params['isHidden'] ?? false; $this->type = $params['type']; // create the content object - $this->content = new Content($this->content, $this->parent); + $this->content = new Content($params['content'], $this->parent); } /** * Converts the object to a string - * - * @return string */ public function __toString(): string { @@ -110,18 +91,14 @@ class Block extends Item /** * Returns the content object - * - * @return \Kirby\Cms\Content */ - public function content() + public function content(): Content { return $this->content; } /** * Controller for the block snippet - * - * @return array */ public function controller(): array { @@ -140,28 +117,26 @@ class Block extends Item * Converts the block to HTML and then * uses the Str::excerpt method to create * a non-formatted, shortened excerpt from it - * - * @param mixed ...$args - * @return string */ - public function excerpt(...$args) + public function excerpt(mixed ...$args): string { return Str::excerpt($this->toHtml(), ...$args); } /** * Constructs a block object with registering blocks models - * - * @param array $params - * @return static - * @throws \Kirby\Exception\InvalidArgumentException * @internal + * + * @throws \Kirby\Exception\InvalidArgumentException */ - public static function factory(array $params) + public static function factory(array $params): static { $type = $params['type'] ?? null; - if (empty($type) === false && $class = (static::$models[$type] ?? null)) { + if ( + empty($type) === false && + $class = (static::$models[$type] ?? null) + ) { $object = new $class($params); if ($object instanceof self) { @@ -170,7 +145,7 @@ class Block extends Item } // default model for blocks - if ($class = (static::$models['Kirby\Cms\Block'] ?? null)) { + if ($class = (static::$models['default'] ?? null)) { $object = new $class($params); if ($object instanceof self) { @@ -183,8 +158,6 @@ class Block extends Item /** * Checks if the block is empty - * - * @return bool */ public function isEmpty(): bool { @@ -194,8 +167,6 @@ class Block extends Item /** * Checks if the block is hidden * from being rendered in the frontend - * - * @return bool */ public function isHidden(): bool { @@ -204,8 +175,6 @@ class Block extends Item /** * Checks if the block is not empty - * - * @return bool */ public function isNotEmpty(): bool { @@ -214,18 +183,14 @@ class Block extends Item /** * Returns the sibling collection that filtered by block status - * - * @return \Kirby\Cms\Collection */ - protected function siblingsCollection() + protected function siblingsCollection(): Blocks { return $this->siblings->filter('isHidden', $this->isHidden()); } /** * Returns the block type - * - * @return string */ public function type(): string { @@ -235,8 +200,6 @@ class Block extends Item /** * The result is being sent to the editor * via the API in the panel - * - * @return array */ public function toArray(): array { @@ -253,24 +216,24 @@ class Block extends Item * and then places that inside a field * object. This can be used further * with all available field methods - * - * @return \Kirby\Cms\Field */ - public function toField() + public function toField(): Field { return new Field($this->parent(), $this->id(), $this->toHtml()); } /** * Converts the block to HTML - * - * @return string */ public function toHtml(): string { try { $kirby = $this->parent()->kirby(); - return (string)$kirby->snippet('blocks/' . $this->type(), $this->controller(), true); + return (string)$kirby->snippet( + 'blocks/' . $this->type(), + $this->controller(), + true + ); } catch (Throwable $e) { if ($kirby->option('debug') === true) { return '

      Block error: "' . $e->getMessage() . '" in block type: "' . $this->type() . '"

      '; diff --git a/kirby/src/Cms/BlockConverter.php b/kirby/src/Cms/BlockConverter.php index a71f69e..6065fe2 100644 --- a/kirby/src/Cms/BlockConverter.php +++ b/kirby/src/Cms/BlockConverter.php @@ -64,8 +64,8 @@ class BlockConverter foreach ($blocks as $index => $block) { if (in_array($block['type'], ['ul', 'ol']) === true) { - $prev = $blocks[$index-1] ?? null; - $next = $blocks[$index+1] ?? null; + $prev = $blocks[$index - 1] ?? null; + $next = $blocks[$index + 1] ?? null; // new list starts here if (!$prev || $prev['type'] !== $block['type']) { diff --git a/kirby/src/Cms/Blocks.php b/kirby/src/Cms/Blocks.php index af17d99..5e0c0c9 100644 --- a/kirby/src/Cms/Blocks.php +++ b/kirby/src/Cms/Blocks.php @@ -27,16 +27,12 @@ class Blocks extends Items /** * All registered blocks methods - * - * @var array */ - public static $methods = []; + public static array $methods = []; /** * Return HTML when the collection is * converted to a string - * - * @return string */ public function __toString(): string { @@ -47,11 +43,8 @@ class Blocks extends Items * Converts the blocks to HTML and then * uses the Str::excerpt method to create * a non-formatted, shortened excerpt from it - * - * @param mixed ...$args - * @return string */ - public function excerpt(...$args) + public function excerpt(mixed ...$args): string { return Str::excerpt($this->toHtml(), ...$args); } @@ -59,13 +52,11 @@ class Blocks extends Items /** * Wrapper around the factory to * catch blocks from layouts - * - * @param array $items - * @param array $params - * @return \Kirby\Cms\Blocks */ - public static function factory(array $items = null, array $params = []) - { + public static function factory( + array|null $items = null, + array $params = [] + ): static { $items = static::extractFromLayouts($items); // @deprecated old editor format @@ -79,9 +70,6 @@ class Blocks extends Items /** * Pull out blocks from layouts - * - * @param array $input - * @return array */ protected static function extractFromLayouts(array $input): array { @@ -115,9 +103,6 @@ class Blocks extends Items /** * Checks if a given block type exists in the collection * @since 3.6.0 - * - * @param string $type - * @return bool */ public function hasType(string $type): bool { @@ -126,11 +111,8 @@ class Blocks extends Items /** * Parse and sanitize various block formats - * - * @param array|string $input - * @return array */ - public static function parse($input): array + public static function parse(array|string|null $input): array { if (empty($input) === false && is_array($input) === false) { try { @@ -175,16 +157,10 @@ class Blocks extends Items /** * Convert all blocks to HTML - * - * @return string */ public function toHtml(): string { - $html = []; - - foreach ($this->data as $block) { - $html[] = $block->toHtml(); - } + $html = A::map($this->data, fn ($block) => $block->toHtml()); return implode($html); } diff --git a/kirby/src/Cms/Blueprint.php b/kirby/src/Cms/Blueprint.php index 41a3f58..ff97590 100644 --- a/kirby/src/Cms/Blueprint.php +++ b/kirby/src/Cms/Blueprint.php @@ -34,14 +34,12 @@ class Blueprint protected $sections = []; protected $tabs = []; + protected array|null $fileTemplates = null; + /** * Magic getter/caller for any blueprint prop - * - * @param string $key - * @param array|null $arguments - * @return mixed */ - public function __call(string $key, array $arguments = null) + public function __call(string $key, array|null $arguments = null): mixed { return $this->props[$key] ?? null; } @@ -49,7 +47,6 @@ class Blueprint /** * Creates a new blueprint object with the given props * - * @param array $props * @throws \Kirby\Exception\InvalidArgumentException If the blueprint model is missing */ public function __construct(array $props) @@ -68,7 +65,7 @@ class Blueprint unset($props['model']); // extend the blueprint in general - $props = $this->extend($props); + $props = static::extend($props); // apply any blueprint preset $props = $this->preset($props); @@ -77,7 +74,8 @@ class Blueprint $props['name'] ??= 'default'; // normalize and translate the title - $props['title'] = $this->i18n($props['title'] ?? ucfirst($props['name'])); + $props['title'] ??= ucfirst($props['name']); + $props['title'] = $this->i18n($props['title']); // convert all shortcuts $props = $this->convertFieldsToSections('main', $props); @@ -93,23 +91,130 @@ class Blueprint /** * Improved `var_dump` output * - * @return array + * @codeCoverageIgnore */ public function __debugInfo(): array { return $this->props ?? []; } + /** + * Gathers what file templates are allowed in + * this model based on the blueprint + */ + public function acceptedFileTemplates(string|null $inSection = null): array + { + // get cached results for the current file model + // (except when collecting for a specific section) + if ($inSection === null && $this->fileTemplates !== null) { + return $this->fileTemplates; // @codeCoverageIgnore + } + + $templates = []; + + // collect all allowed file templates from blueprint… + foreach ($this->sections() as $section) { + // if collecting for a specific section, skip all others + if ($inSection !== null && $section->name() !== $inSection) { + continue; + } + + $templates = match ($section->type()) { + 'files' => [...$templates, $section->template() ?? 'default'], + 'fields' => [ + ...$templates, + ...$this->acceptedFileTemplatesFromFields($section->fields()) + ], + default => $templates + }; + } + + // no caching for when collecting for specific section + if ($inSection !== null) { + return $templates; // @codeCoverageIgnore + } + + return $this->fileTemplates = $templates; + } + + /** + * Gathers the allowed file templates from model's fields + */ + protected function acceptedFileTemplatesFromFields(array $fields): array + { + $templates = []; + + foreach ($fields as $field) { + // fields with uploads settings + if (isset($field['uploads']) === true && is_array($field['uploads']) === true) { + $templates = [ + ...$templates, + ...$this->acceptedFileTemplatesFromFieldUploads($field['uploads']) + ]; + continue; + } + + // structure and object fields + if (isset($field['fields']) === true && is_array($field['fields']) === true) { + $templates = [ + ...$templates, + ...$this->acceptedFileTemplatesFromFields($field['fields']), + ]; + continue; + } + + // layout and blocks fields + if (isset($field['fieldsets']) === true && is_array($field['fieldsets']) === true) { + $templates = [ + ...$templates, + ...$this->acceptedFileTemplatesFromFieldsets($field['fieldsets']) + ]; + continue; + } + } + + return $templates; + } + + /** + * Gathers the allowed file templates from fieldsets + */ + protected function acceptedFileTemplatesFromFieldsets(array $fieldsets): array + { + $templates = []; + + foreach ($fieldsets as $fieldset) { + foreach (($fieldset['tabs'] ?? []) as $tab) { + $templates = array_merge($templates, $this->acceptedFileTemplatesFromFields($tab['fields'] ?? [])); + } + } + + return $templates; + } + + /** + * Extracts templates from field uploads settings + */ + protected function acceptedFileTemplatesFromFieldUploads(array $uploads): array + { + // only if the `uploads` parent is this model + if ($target = $uploads['parent'] ?? null) { + if ($this->model->id() !== $target) { + return []; + } + } + + return [($uploads['template'] ?? 'default')]; + } + /** * Converts all column definitions, that * are not wrapped in a tab, into a generic tab - * - * @param string $tabName - * @param array $props - * @return array */ - protected function convertColumnsToTabs(string $tabName, array $props): array - { + protected function convertColumnsToTabs( + string $tabName, + array $props + ): array { if (isset($props['columns']) === false) { return $props; } @@ -130,13 +235,11 @@ class Blueprint * Converts all field definitions, that are not * wrapped in a fields section into a generic * fields section. - * - * @param string $tabName - * @param array $props - * @return array */ - protected function convertFieldsToSections(string $tabName, array $props): array - { + protected function convertFieldsToSections( + string $tabName, + array $props + ): array { if (isset($props['fields']) === false) { return $props; } @@ -157,13 +260,11 @@ class Blueprint /** * Converts all sections that are not wrapped in * columns, into a single generic column. - * - * @param string $tabName - * @param array $props - * @return array */ - protected function convertSectionsToColumns(string $tabName, array $props): array - { + protected function convertSectionsToColumns( + string $tabName, + array $props + ): array { if (isset($props['sections']) === false) { return $props; } @@ -187,7 +288,6 @@ class Blueprint * props is just a string * * @param array|string $props - * @return array */ public static function extend($props): array { @@ -197,38 +297,32 @@ class Blueprint ]; } - $extends = $props['extends'] ?? null; - - if ($extends === null) { - return $props; - } - - foreach (A::wrap($extends) as $extend) { - try { - $mixin = static::find($extend); - $mixin = static::extend($mixin); - $props = A::merge($mixin, $props, A::MERGE_REPLACE); - } catch (Exception) { - // keep the props unextended if the snippet wasn't found + if ($extends = $props['extends'] ?? null) { + foreach (A::wrap($extends) as $extend) { + try { + $mixin = static::find($extend); + $mixin = static::extend($mixin); + $props = A::merge($mixin, $props, A::MERGE_REPLACE); + } catch (Exception) { + // keep the props unextended if the snippet wasn't found + } } - } - // remove the extends flag - unset($props['extends']); + // remove the extends flag + unset($props['extends']); + } return $props; } /** * Create a new blueprint for a model - * - * @param string $name - * @param string|null $fallback - * @param \Kirby\Cms\Model $model - * @return static|null */ - public static function factory(string $name, string $fallback = null, Model $model) - { + public static function factory( + string $name, + string|null $fallback, + ModelWithContent $model + ): static|null { try { $props = static::load($name); } catch (Exception) { @@ -247,9 +341,6 @@ class Blueprint /** * Returns a single field definition by name - * - * @param string $name - * @return array|null */ public function field(string $name): array|null { @@ -258,8 +349,6 @@ class Blueprint /** * Returns all field definitions - * - * @return array */ public function fields(): array { @@ -269,8 +358,6 @@ class Blueprint /** * Find a blueprint by name * - * @param string $name - * @return array * @throws \Kirby\Exception\NotFoundException If the blueprint cannot be found */ public static function find(string $name): array @@ -283,8 +370,10 @@ class Blueprint $root = $kirby->root('blueprints'); $file = $root . '/' . $name . '.yml'; - // first try to find a site blueprint, - // then check in the plugin extensions + // first try to find the blueprint in the `site/blueprints` root, + // then check in the plugin extensions which includes some default + // core blueprints (e.g. page, file, site and block defaults) + // as well as blueprints provided by plugins if (F::exists($file, $root) !== true) { $file = $kirby->extension('blueprints', $name); } @@ -298,6 +387,7 @@ class Blueprint if (is_string($file) === true && F::exists($file) === true) { return static::$loaded[$name] = Data::read($file); } + if (is_array($file) === true) { return static::$loaded[$name] = $file; } @@ -311,20 +401,14 @@ class Blueprint /** * Used to translate any label, heading, etc. - * - * @param mixed $value - * @param mixed $fallback - * @return mixed */ - protected function i18n($value, $fallback = null) + protected function i18n(mixed $value, mixed $fallback = null): mixed { - return I18n::translate($value, $fallback ?? $value); + return I18n::translate($value, $fallback) ?? $value; } /** * Checks if this is the default blueprint - * - * @return bool */ public function isDefault(): bool { @@ -333,44 +417,33 @@ class Blueprint /** * Loads a blueprint from file or array - * - * @param string $name - * @return array */ public static function load(string $name): array { $props = static::find($name); - $normalize = function ($props) use ($name) { - // inject the filename as name if no name is set - $props['name'] ??= $name; + // inject the filename as name if no name is set + $props['name'] ??= $name; - // normalize the title - $title = $props['title'] ?? ucfirst($props['name']); + // normalize the title + $title = $props['title'] ?? ucfirst($props['name']); - // translate the title - $props['title'] = I18n::translate($title, $title); + // translate the title + $props['title'] = I18n::translate($title) ?? $title; - return $props; - }; - - return $normalize($props); + return $props; } /** * Returns the parent model - * - * @return \Kirby\Cms\Model */ - public function model() + public function model(): ModelWithContent { return $this->model; } /** * Returns the blueprint name - * - * @return string */ public function name(): string { @@ -379,10 +452,6 @@ class Blueprint /** * Normalizes all required props in a column setup - * - * @param string $tabName - * @param array $columns - * @return array */ protected function normalizeColumns(string $tabName, array $columns): array { @@ -393,7 +462,10 @@ class Blueprint continue; } - $columnProps = $this->convertFieldsToSections($tabName . '-col-' . $columnKey, $columnProps); + $columnProps = $this->convertFieldsToSections( + $tabName . '-col-' . $columnKey, + $columnProps + ); // inject getting started info, if the sections are empty if (empty($columnProps['sections']) === true) { @@ -406,19 +478,19 @@ class Blueprint ]; } - $columns[$columnKey] = array_merge($columnProps, [ + $columns[$columnKey] = [ + ...$columnProps, 'width' => $columnProps['width'] ?? '1/1', - 'sections' => $this->normalizeSections($tabName, $columnProps['sections'] ?? []) - ]); + 'sections' => $this->normalizeSections( + $tabName, + $columnProps['sections'] ?? [] + ) + ]; } return $columns; } - /** - * @param array $items - * @return string - */ public static function helpList(array $items): string { $md = []; @@ -433,11 +505,9 @@ class Blueprint /** * Normalize field props for a single field * - * @param array|string $props - * @return array * @throws \Kirby\Exception\InvalidArgumentException If the filed name is missing or the field type is invalid */ - public static function fieldProps($props): array + public static function fieldProps(array|string $props): array { $props = static::extend($props); @@ -476,20 +546,17 @@ class Blueprint } // add some useful defaults - return array_merge($props, [ + return [ + ...$props, 'label' => $props['label'] ?? ucfirst($name), 'name' => $name, 'type' => $type, 'width' => $props['width'] ?? '1/1', - ]); + ]; } /** * Creates an error field with the given error message - * - * @param string $name - * @param string $message - * @return array */ public static function fieldError(string $name, string $message): array { @@ -505,9 +572,6 @@ class Blueprint /** * Normalizes all fields and adds automatic labels, * types and widths. - * - * @param array $fields - * @return array */ public static function fieldsProps($fields): array { @@ -547,11 +611,16 @@ class Blueprint // resolve field groups if ($fieldProps['type'] === 'group') { - if (empty($fieldProps['fields']) === false && is_array($fieldProps['fields']) === true) { + if ( + empty($fieldProps['fields']) === false && + is_array($fieldProps['fields']) === true + ) { $index = array_search($fieldName, array_keys($fields)); - $before = array_slice($fields, 0, $index); - $after = array_slice($fields, $index + 1); - $fields = array_merge($before, $fieldProps['fields'] ?? [], $after); + $fields = [ + ...array_slice($fields, 0, $index), + ...$fieldProps['fields'] ?? [], + ...array_slice($fields, $index + 1) + ]; } else { unset($fields[$fieldName]); } @@ -566,14 +635,12 @@ class Blueprint /** * Normalizes blueprint options. This must be used in the * constructor of an extended class, if you want to make use of it. - * - * @param array|true|false|null|string $options - * @param array $defaults - * @param array $aliases - * @return array */ - protected function normalizeOptions($options, array $defaults, array $aliases = []): array - { + protected function normalizeOptions( + array|string|bool|null $options, + array $defaults, + array $aliases = [] + ): array { // return defaults when options are not defined or set to true if ($options === true) { return $defaults; @@ -585,7 +652,7 @@ class Blueprint } // extend options if possible - $options = $this->extend($options); + $options = static::extend($options); foreach ($options as $key => $value) { $alias = $aliases[$key] ?? null; @@ -596,18 +663,16 @@ class Blueprint } } - return array_merge($defaults, $options); + return [...$defaults, ...$options]; } /** * Normalizes all required keys in sections - * - * @param string $tabName - * @param array $sections - * @return array */ - protected function normalizeSections(string $tabName, array $sections): array - { + protected function normalizeSections( + string $tabName, + array $sections + ): array { foreach ($sections as $sectionName => $sectionProps) { // unset / remove section if its property is false if ($sectionProps === false) { @@ -621,26 +686,27 @@ class Blueprint } // inject all section extensions - $sectionProps = $this->extend($sectionProps); + $sectionProps = static::extend($sectionProps); - $sections[$sectionName] = $sectionProps = array_merge($sectionProps, [ + $sections[$sectionName] = $sectionProps = [ + ...$sectionProps, 'name' => $sectionName, 'type' => $type = $sectionProps['type'] ?? $sectionName - ]); + ]; if (empty($type) === true || is_string($type) === false) { $sections[$sectionName] = [ 'name' => $sectionName, 'label' => 'Invalid section type for section "' . $sectionName . '"', 'type' => 'info', - 'text' => 'The following section types are available: ' . $this->helpList(array_keys(Section::$types)) + 'text' => 'The following section types are available: ' . static::helpList(array_keys(Section::$types)) ]; } elseif (isset(Section::$types[$type]) === false) { $sections[$sectionName] = [ 'name' => $sectionName, 'label' => 'Invalid section type ("' . $type . '")', 'type' => 'info', - 'text' => 'The following section types are available: ' . $this->helpList(array_keys(Section::$types)) + 'text' => 'The following section types are available: ' . static::helpList(array_keys(Section::$types)) ]; } @@ -676,16 +742,13 @@ class Blueprint } // store all normalized sections - $this->sections = array_merge($this->sections, $sections); + $this->sections = [...$this->sections, ...$sections]; return $sections; } /** * Normalizes all required keys in tabs - * - * @param array $tabs - * @return array */ protected function normalizeTabs($tabs): array { @@ -701,7 +764,7 @@ class Blueprint } // inject all tab extensions - $tabProps = $this->extend($tabProps); + $tabProps = static::extend($tabProps); // inject a preset if available $tabProps = $this->preset($tabProps); @@ -709,13 +772,14 @@ class Blueprint $tabProps = $this->convertFieldsToSections($tabName, $tabProps); $tabProps = $this->convertSectionsToColumns($tabName, $tabProps); - $tabs[$tabName] = array_merge($tabProps, [ + $tabs[$tabName] = [ + ...$tabProps, 'columns' => $this->normalizeColumns($tabName, $tabProps['columns'] ?? []), 'icon' => $tabProps['icon'] ?? null, 'label' => $this->i18n($tabProps['label'] ?? ucfirst($tabName)), 'link' => $this->model->panel()->url(true) . '/?tab=' . $tabName, 'name' => $tabName, - ]); + ]; } return $this->tabs = $tabs; @@ -723,9 +787,6 @@ class Blueprint /** * Injects a blueprint preset - * - * @param array $props - * @return array */ protected function preset(array $props): array { @@ -748,16 +809,17 @@ class Blueprint /** * Returns a single section by name - * - * @param string $name - * @return \Kirby\Cms\Section|null */ - public function section(string $name) + public function section(string $name): Section|null { if (empty($this->sections[$name]) === true) { return null; } + if ($this->sections[$name] instanceof Section) { + return $this->sections[$name]; //@codeCoverageIgnore + } + // get all props $props = $this->sections[$name]; @@ -765,27 +827,25 @@ class Blueprint $props['model'] = $this->model(); // create a new section object - return new Section($props['type'], $props); + return $this->sections[$name] = new Section($props['type'], $props); } /** * Returns all sections - * - * @return array */ public function sections(): array { return A::map( $this->sections, - fn ($section) => $this->section($section['name']) + fn ($section) => match (true) { + $section instanceof Section => $section, + default => $this->section($section['name']) + } ); } /** * Returns a single tab by name - * - * @param string|null $name - * @return array|null */ public function tab(string|null $name = null): array|null { @@ -798,8 +858,6 @@ class Blueprint /** * Returns all tabs - * - * @return array */ public function tabs(): array { @@ -808,8 +866,6 @@ class Blueprint /** * Returns the blueprint title - * - * @return string */ public function title(): string { @@ -818,8 +874,6 @@ class Blueprint /** * Converts the blueprint object to a plain array - * - * @return array */ public function toArray(): array { diff --git a/kirby/src/Cms/Collection.php b/kirby/src/Cms/Collection.php index 7b525de..251b228 100644 --- a/kirby/src/Cms/Collection.php +++ b/kirby/src/Cms/Collection.php @@ -72,9 +72,7 @@ class Collection extends BaseCollection * child classes can override it again to add validation * and custom behavior depending on the object type * - * @param string $id * @param object $object - * @return void */ public function __set(string $id, $object): void { @@ -142,7 +140,6 @@ class Collection extends BaseCollection /** * Find a single element by an attribute and its value * - * @param string $attribute * @param mixed $value * @return mixed|null */ @@ -162,11 +159,11 @@ class Collection extends BaseCollection * with an item for each group and a collection for each group. * * @param string|Closure $field - * @param bool $i Ignore upper/lowercase for group names + * @param bool $caseInsensitive Ignore upper/lowercase for group names * @return \Kirby\Cms\Collection * @throws \Kirby\Exception\Exception */ - public function group($field, bool $i = true) + public function group($field, bool $caseInsensitive = true) { if (is_string($field) === true) { $groups = new Collection([], $this->parent()); @@ -179,8 +176,12 @@ class Collection extends BaseCollection throw new InvalidArgumentException('Invalid grouping value for key: ' . $key); } + $value = (string)$value; + // ignore upper/lowercase for group names - $value = $i === true ? Str::lower($value) : (string)$value; + if ($caseInsensitive === true) { + $value = Str::lower($value); + } if (isset($groups->data[$value]) === false) { // create a new entry for the group if it does not exist yet @@ -194,7 +195,7 @@ class Collection extends BaseCollection return $groups; } - return parent::group($field, $i); + return parent::group($field, $caseInsensitive); } /** @@ -202,7 +203,6 @@ class Collection extends BaseCollection * is in the collection * * @param string|object $key - * @return bool */ public function has($key): bool { @@ -219,7 +219,6 @@ class Collection extends BaseCollection * or ids and then search accordingly. * * @param string|object $needle - * @return int|false */ public function indexOf($needle): int|false { @@ -261,20 +260,31 @@ class Collection extends BaseCollection * Add pagination and return a sliced set of data. * * @param mixed ...$arguments - * @return \Kirby\Cms\Collection + * @return $this|static */ public function paginate(...$arguments) { $this->pagination = Pagination::for($this, ...$arguments); // slice and clone the collection according to the pagination - return $this->slice($this->pagination->offset(), $this->pagination->limit()); + return $this->slice( + $this->pagination->offset(), + $this->pagination->limit() + ); + } + + /** + * Get the pagination object + * + * @return \Kirby\Cms\Pagination|null + */ + public function pagination() + { + return $this->pagination; } /** * Returns the parent model - * - * @return \Kirby\Cms\Model */ public function parent() { @@ -311,7 +321,6 @@ class Collection extends BaseCollection * offset, limit, search and paginate on the collection. * Any part of the query is optional. * - * @param array $arguments * @return static */ public function query(array $arguments = []) @@ -354,13 +363,11 @@ class Collection extends BaseCollection /** * Searches the collection - * - * @param string|null $query - * @param array $params - * @return self */ - public function search(string $query = null, $params = []) - { + public function search( + string|null $query = null, + string|array $params = [] + ): static { return Search::collection($this, $query, $params); } @@ -368,11 +375,8 @@ class Collection extends BaseCollection * Converts all objects in the collection * to an array. This can also take a callback * function to further modify the array result. - * - * @param \Closure|null $map - * @return array */ - public function toArray(Closure $map = null): array + public function toArray(Closure|null $map = null): array { return parent::toArray($map ?? fn ($object) => $object->toArray()); } diff --git a/kirby/src/Cms/Collections.php b/kirby/src/Cms/Collections.php index 7e0b80c..beafe8d 100644 --- a/kirby/src/Cms/Collections.php +++ b/kirby/src/Cms/Collections.php @@ -52,8 +52,8 @@ class Collections * Loads a collection by name if registered * * @return \Kirby\Toolkit\Collection|null - * @todo 4.0 Add deprecation warning when anything else than a Collection is returned - * @todo 5.0 Add return type declaration + * @todo 5.0 Add deprecation warning when anything else than a Collection is returned + * @todo 6.0 Add PHP return type declaration for `Toolkit\Collection` */ public function get(string $name, array $data = []) { @@ -61,11 +61,9 @@ class Collections $this->collections[$name] ??= $this->load($name); // if not yet cached - if ( - isset($this->cache[$name]) === false || - $this->cache[$name]['data'] !== $data - ) { + if (($this->cache[$name]['data'] ?? null) !== $data) { $controller = new Controller($this->collections[$name]); + $this->cache[$name] = [ 'result' => $controller->call(null, $data), 'data' => $data @@ -82,9 +80,6 @@ class Collections /** * Checks if a collection exists - * - * @param string $name - * @return bool */ public function has(string $name): bool { @@ -104,11 +99,9 @@ class Collections * Loads collection from php file in a * given directory or from plugin extension. * - * @param string $name - * @return mixed * @throws \Kirby\Exception\NotFoundException */ - public function load(string $name) + public function load(string $name): mixed { $kirby = App::instance(); @@ -126,10 +119,7 @@ class Collections // fallback to collections from plugins $collections = $kirby->extensions('collections'); - if (isset($collections[$name]) === true) { - return $collections[$name]; - } - - throw new NotFoundException('The collection cannot be found'); + return $collections[$name] ?? + throw new NotFoundException('The collection cannot be found'); } } diff --git a/kirby/src/Cms/ContentLock.php b/kirby/src/Cms/ContentLock.php index 35cfa0c..1186a3f 100644 --- a/kirby/src/Cms/ContentLock.php +++ b/kirby/src/Cms/ContentLock.php @@ -2,9 +2,9 @@ namespace Kirby\Cms; +use Kirby\Exception\AuthException; use Kirby\Exception\DuplicateException; use Kirby\Exception\LogicException; -use Kirby\Exception\PermissionException; /** * Takes care of content lock and unlock information @@ -17,33 +17,16 @@ use Kirby\Exception\PermissionException; */ class ContentLock { - /** - * Lock data - * - * @var array - */ - protected $data; + protected array $data; - /** - * The model to manage locking/unlocking for - * - * @var ModelWithContent - */ - protected $model; - - /** - * @param \Kirby\Cms\ModelWithContent $model - */ - public function __construct(ModelWithContent $model) - { - $this->model = $model; - $this->data = $this->kirby()->locks()->get($model); + public function __construct( + protected ModelWithContent $model + ) { + $this->data = $this->kirby()->locks()->get($model); } /** * Clears the lock unconditionally - * - * @return bool */ protected function clearLock(): bool { @@ -61,7 +44,6 @@ class ContentLock /** * Sets lock with the current user * - * @return bool * @throws \Kirby\Exception\DuplicateException */ public function create(): bool @@ -86,10 +68,8 @@ class ContentLock /** * Returns either `false` or array with `user`, `email`, * `time` and `unlockable` keys - * - * @return array|bool */ - public function get() + public function get(): array|bool { $data = $this->data['lock'] ?? []; @@ -114,8 +94,6 @@ class ContentLock /** * Returns if the model is locked by another user - * - * @return bool */ public function isLocked(): bool { @@ -130,8 +108,6 @@ class ContentLock /** * Returns if the current user's lock has been removed by another user - * - * @return bool */ public function isUnlocked(): bool { @@ -142,8 +118,6 @@ class ContentLock /** * Returns the app instance - * - * @return \Kirby\Cms\App */ protected function kirby(): App { @@ -153,7 +127,6 @@ class ContentLock /** * Removes lock of current user * - * @return bool * @throws \Kirby\Exception\LogicException */ public function remove(): bool @@ -176,8 +149,6 @@ class ContentLock /** * Removes unlock information for current user - * - * @return bool */ public function resolve(): bool { @@ -199,7 +170,7 @@ class ContentLock * Returns the state for the * form buttons in the frontend */ - public function state(): ?string + public function state(): string|null { return match (true) { $this->isUnlocked() => 'unlock', @@ -211,8 +182,6 @@ class ContentLock /** * Returns a usable lock array * for the frontend - * - * @return array */ public function toArray(): array { @@ -224,8 +193,6 @@ class ContentLock /** * Removes current lock and adds lock user to unlock data - * - * @return bool */ public function unlock(): bool { @@ -245,12 +212,11 @@ class ContentLock * Returns currently authenticated user; * throws exception if none is authenticated * - * @return \Kirby\Cms\User * @throws \Kirby\Exception\PermissionException */ protected function user(): User { return $this->kirby()->user() ?? - throw new PermissionException('No user authenticated.'); + throw new AuthException('No user authenticated.'); } } diff --git a/kirby/src/Cms/ContentLocks.php b/kirby/src/Cms/ContentLocks.php index 2d08d9f..bdcc6f4 100644 --- a/kirby/src/Cms/ContentLocks.php +++ b/kirby/src/Cms/ContentLocks.php @@ -22,18 +22,14 @@ class ContentLocks * Data from the `.lock` files * that have been read so far * cached by `.lock` file path - * - * @var array */ - protected $data = []; + protected array $data = []; /** * PHP file handles for all currently * open `.lock` files - * - * @var array */ - protected $handles = []; + protected array $handles = []; /** * Closes the open file handles @@ -50,11 +46,9 @@ class ContentLocks /** * Removes the file lock and closes the file handle * - * @param string $file - * @return void * @throws \Kirby\Exception\Exception */ - protected function closeHandle(string $file) + protected function closeHandle(string $file): void { if (isset($this->handles[$file]) === false) { return; @@ -72,20 +66,15 @@ class ContentLocks /** * Returns the path to a model's lock file - * - * @param \Kirby\Cms\ModelWithContent $model - * @return string */ public static function file(ModelWithContent $model): string { - return $model->contentFileDirectory() . '/.lock'; + $root = $model::CLASS_ALIAS === 'file' ? dirname($model->root()) : $model->root(); + return $root . '/.lock'; } /** * Returns the lock/unlock data for the specified model - * - * @param \Kirby\Cms\ModelWithContent $model - * @return array */ public function get(ModelWithContent $model): array { @@ -121,7 +110,6 @@ class ContentLocks /** * Returns the file handle to a `.lock` file * - * @param string $file * @param bool $create Whether to create the file if it does not exist * @return resource|null File handle * @throws \Kirby\Exception\Exception @@ -155,9 +143,6 @@ class ContentLocks /** * Returns model ID used as the key for the data array; * prepended with a slash because the $site otherwise won't have an ID - * - * @param \Kirby\Cms\ModelWithContent $model - * @return string */ public static function id(ModelWithContent $model): string { @@ -167,9 +152,6 @@ class ContentLocks /** * Sets and writes the lock/unlock data for the specified model * - * @param \Kirby\Cms\ModelWithContent $model - * @param array $data - * @return bool * @throws \Kirby\Exception\Exception */ public function set(ModelWithContent $model, array $data): bool @@ -191,7 +173,7 @@ class ContentLocks ) { unset($this->data[$file][$id]); - // there is empty unlock data, but still lock data + // there is empty unlock data, but still lock data } elseif ( isset($data['unlock']) === true && count($data['unlock']) === 0 diff --git a/kirby/src/Cms/Core.php b/kirby/src/Cms/Core.php index 231b422..11ca635 100644 --- a/kirby/src/Cms/Core.php +++ b/kirby/src/Cms/Core.php @@ -2,6 +2,15 @@ namespace Kirby\Cms; +use Kirby\Cache\ApcuCache; +use Kirby\Cache\FileCache; +use Kirby\Cache\MemCached; +use Kirby\Cache\MemoryCache; +use Kirby\Cms\Auth\EmailChallenge; +use Kirby\Cms\Auth\TotpChallenge; +use Kirby\Form\Field\BlocksField; +use Kirby\Form\Field\LayoutField; + /** * The Core class lists all parts of Kirby * that need to be loaded or initalized in order @@ -21,14 +30,17 @@ namespace Kirby\Cms; */ class Core { + /** + * Optional override for the auto-detected index root + */ + public static string|null $indexRoot = null; + protected array $cache = []; - protected App $kirby; protected string $root; - public function __construct(App $kirby) + public function __construct(protected App $kirby) { - $this->kirby = $kirby; - $this->root = dirname(__DIR__, 2) . '/config'; + $this->root = dirname(__DIR__, 2) . '/config'; } /** @@ -52,9 +64,11 @@ class Core return [ 'account' => $this->root . '/areas/account.php', 'installation' => $this->root . '/areas/installation.php', + 'lab' => $this->root . '/areas/lab.php', 'languages' => $this->root . '/areas/languages.php', 'login' => $this->root . '/areas/login.php', 'logout' => $this->root . '/areas/logout.php', + 'search' => $this->root . '/areas/search.php', 'site' => $this->root . '/areas/site.php', 'system' => $this->root . '/areas/system.php', 'users' => $this->root . '/areas/users.php', @@ -67,7 +81,8 @@ class Core public function authChallenges(): array { return [ - 'email' => 'Kirby\Cms\Auth\EmailChallenge' + 'email' => EmailChallenge::class, + 'totp' => TotpChallenge::class, ]; } @@ -86,9 +101,9 @@ class Core } /** - * Returns a list of all paths to core blueprints + * Returns a list of paths to core blueprints or + * the blueprint in array form * - * They are located in `/kirby/config/blueprints`. * Block blueprints are located in `/kirby/config/blocks` */ public function blueprints(): array @@ -108,13 +123,21 @@ class Core 'blocks/video' => $this->root . '/blocks/video/video.yml', // file blueprints - 'files/default' => $this->root . '/blueprints/files/default.yml', + 'files/default' => ['title' => 'File'], // page blueprints - 'pages/default' => $this->root . '/blueprints/pages/default.yml', + 'pages/default' => ['title' => 'Page'], // site blueprints - 'site' => $this->root . '/blueprints/site.yml' + 'site' => [ + 'title' => 'Site', + 'sections' => [ + 'pages' => [ + 'headline' => ['*' => 'pages'], + 'type' => 'pages' + ] + ] + ] ]; } @@ -135,10 +158,10 @@ class Core public function cacheTypes(): array { return [ - 'apcu' => 'Kirby\Cache\ApcuCache', - 'file' => 'Kirby\Cache\FileCache', - 'memcached' => 'Kirby\Cache\MemCached', - 'memory' => 'Kirby\Cache\MemoryCache', + 'apcu' => ApcuCache::class, + 'file' => FileCache::class, + 'memcached' => MemCached::class, + 'memory' => MemoryCache::class, ]; } @@ -216,8 +239,9 @@ class Core public function fields(): array { return [ - 'blocks' => 'Kirby\Form\Field\BlocksField', + 'blocks' => BlocksField::class, 'checkboxes' => $this->root . '/fields/checkboxes.php', + 'color' => $this->root . '/fields/color.php', 'date' => $this->root . '/fields/date.php', 'email' => $this->root . '/fields/email.php', 'files' => $this->root . '/fields/files.php', @@ -225,8 +249,9 @@ class Core 'headline' => $this->root . '/fields/headline.php', 'hidden' => $this->root . '/fields/hidden.php', 'info' => $this->root . '/fields/info.php', - 'layout' => 'Kirby\Form\Field\LayoutField', + 'layout' => LayoutField::class, 'line' => $this->root . '/fields/line.php', + 'link' => $this->root . '/fields/link.php', 'list' => $this->root . '/fields/list.php', 'multiselect' => $this->root . '/fields/multiselect.php', 'number' => $this->root . '/fields/number.php', @@ -296,7 +321,7 @@ class Core 'i18n:translations' => fn (array $roots) => $roots['i18n'] . '/translations', 'i18n:rules' => fn (array $roots) => $roots['i18n'] . '/rules', - 'index' => fn (array $roots) => dirname(__DIR__, 3), + 'index' => fn (array $roots) => static::$indexRoot ?? dirname(__DIR__, 3), 'assets' => fn (array $roots) => $roots['index'] . '/assets', 'content' => fn (array $roots) => $roots['index'] . '/content', 'media' => fn (array $roots) => $roots['index'] . '/media', diff --git a/kirby/src/Cms/Email.php b/kirby/src/Cms/Email.php index 6362584..18f06a2 100644 --- a/kirby/src/Cms/Email.php +++ b/kirby/src/Cms/Email.php @@ -4,6 +4,7 @@ namespace Kirby\Cms; use Kirby\Exception\InvalidArgumentException; use Kirby\Exception\NotFoundException; +use Kirby\Template\Template; /** * Wrapper around our Email package, which @@ -21,18 +22,14 @@ class Email { /** * Options configured through the `email` CMS option - * - * @var array */ - protected $options; + protected array $options; /** * Props for the email object; will be passed to the * Kirby\Email\Email class - * - * @var array */ - protected $props; + protected array $props; /** * Class constructor @@ -40,9 +37,9 @@ class Email * @param string|array $preset Preset name from the config or a simple props array * @param array $props Props array to override the $preset */ - public function __construct($preset = [], array $props = []) + public function __construct(string|array $preset = [], array $props = []) { - $this->options = App::instance()->option('email'); + $this->options = App::instance()->option('email', []); // build a prop array based on preset and props $preset = $this->preset($preset); @@ -71,10 +68,9 @@ class Email * prop arrays in case a preset is not needed * * @param string|array $preset Preset name or simple prop array - * @return array * @throws \Kirby\Exception\NotFoundException */ - protected function preset($preset): array + protected function preset(string|array $preset): array { // only passed props, not preset name if (is_array($preset) === true) { @@ -96,7 +92,6 @@ class Email * Renders the email template(s) and sets the body props * to the result * - * @return void * @throws \Kirby\Exception\NotFoundException */ protected function template(): void @@ -118,7 +113,7 @@ class Email $this->props['body']['text'] = $text->render($data); } - // fallback to single email text template + // fallback to single email text template } elseif ($text->exists()) { $this->props['body'] = $text->render($data); } else { @@ -129,20 +124,14 @@ class Email /** * Returns an email template by name and type - * - * @param string $name Template name - * @param string|null $type `html` or `text` - * @return \Kirby\Template\Template */ - protected function getTemplate(string $name, string $type = null) + protected function getTemplate(string $name, string|null $type = null): Template { return App::instance()->template('emails/' . $name, $type, 'text'); } /** * Returns the prop array - * - * @return array */ public function toArray(): array { @@ -154,11 +143,10 @@ class Email * supports simple strings, file objects or collections/arrays of either * * @param string $prop Prop to transform - * @return void */ protected function transformFile(string $prop): void { - $this->props[$prop] = $this->transformModel($prop, 'Kirby\Cms\File', 'root'); + $this->props[$prop] = $this->transformModel($prop, File::class, 'root'); } /** @@ -171,8 +159,12 @@ class Email * returns a simple value-only array if not given * @return array Simple key-value or just value array with the transformed prop data */ - protected function transformModel(string $prop, string $class, string $contentValue, string $contentKey = null): array - { + protected function transformModel( + string $prop, + string $class, + string $contentValue, + string|null $contentKey = null + ): array { $value = $this->props[$prop] ?? []; // ensure consistent input by making everything an iterable value @@ -212,11 +204,12 @@ class Email * * @param string $addressProp Prop with the email address * @param string $nameProp Prop with the name corresponding to the $addressProp - * @return void */ - protected function transformUserSingle(string $addressProp, string $nameProp): void - { - $result = $this->transformModel($addressProp, 'Kirby\Cms\User', 'name', 'email'); + protected function transformUserSingle( + string $addressProp, + string $nameProp + ): void { + $result = $this->transformModel($addressProp, User::class, 'name', 'email'); $address = array_keys($result)[0] ?? null; $name = $result[$address] ?? null; @@ -239,10 +232,9 @@ class Email * supports simple strings, user objects or collections/arrays of either * * @param string $prop Prop to transform - * @return void */ protected function transformUserMultiple(string $prop): void { - $this->props[$prop] = $this->transformModel($prop, 'Kirby\Cms\User', 'name', 'email'); + $this->props[$prop] = $this->transformModel($prop, User::class, 'name', 'email'); } } diff --git a/kirby/src/Cms/Event.php b/kirby/src/Cms/Event.php index 9e57854..f4dd6f1 100644 --- a/kirby/src/Cms/Event.php +++ b/kirby/src/Cms/Event.php @@ -24,41 +24,31 @@ class Event /** * The full event name * (e.g. `page.create:after`) - * - * @var string */ - protected $name; + protected string $name; /** * The event type * (e.g. `page` in `page.create:after`) - * - * @var string */ - protected $type; + protected string $type; /** * The event action * (e.g. `create` in `page.create:after`) - * - * @var string|null */ - protected $action; + protected string|null $action; /** * The event state * (e.g. `after` in `page.create:after`) - * - * @var string|null */ - protected $state; + protected string|null $state; /** * The event arguments - * - * @var array */ - protected $arguments = []; + protected array $arguments = []; /** * Class constructor @@ -83,20 +73,15 @@ class Event /** * Magic caller for event arguments - * - * @param string $method - * @param array $arguments - * @return mixed */ - public function __call(string $method, array $arguments = []) + public function __call(string $method, array $arguments = []): mixed { return $this->argument($method); } /** * Improved `var_dump` output - * - * @return array + * @codeCoverageIgnore */ public function __debugInfo(): array { @@ -106,8 +91,6 @@ class Event /** * Makes it possible to simply echo * or stringify the entire object - * - * @return string */ public function __toString(): string { @@ -117,8 +100,6 @@ class Event /** * Returns the action of the event (e.g. `create`) * or `null` if the event name does not include an action - * - * @return string|null */ public function action(): string|null { @@ -127,19 +108,14 @@ class Event /** * Returns a specific event argument - * - * @param string $name - * @return mixed */ - public function argument(string $name) + public function argument(string $name): mixed { return $this->arguments[$name] ?? null; } /** * Returns the arguments of the event - * - * @return array */ public function arguments(): array { @@ -151,10 +127,8 @@ class Event * the hook's return value * * @param object|null $bind Optional object to bind to the hook function - * @param \Closure $hook - * @return mixed */ - public function call(object|null $bind, Closure $hook) + public function call(object|null $bind, Closure $hook): mixed { // collect the list of possible hook arguments $data = $this->arguments(); @@ -167,8 +141,6 @@ class Event /** * Returns the full name of the event - * - * @return string */ public function name(): string { @@ -178,13 +150,16 @@ class Event /** * Returns the full list of possible wildcard * event names based on the current event name - * - * @return array */ public function nameWildcards(): array { - // if the event is already a wildcard event, no further variation is possible - if ($this->type === '*' || $this->action === '*' || $this->state === '*') { + // if the event is already a wildcard event, + // no further variation is possible + if ( + $this->type === '*' || + $this->action === '*' || + $this->state === '*' + ) { return []; } @@ -228,8 +203,6 @@ class Event /** * Returns the state of the event (e.g. `after`) - * - * @return string|null */ public function state(): string|null { @@ -238,8 +211,6 @@ class Event /** * Returns the event data as array - * - * @return array */ public function toArray(): array { @@ -251,8 +222,6 @@ class Event /** * Returns the event name as string - * - * @return string */ public function toString(): string { @@ -261,8 +230,6 @@ class Event /** * Returns the type of the event (e.g. `page`) - * - * @return string */ public function type(): string { @@ -273,9 +240,6 @@ class Event * Updates a given argument with a new value * * @internal - * @param string $name - * @param mixed $value - * @return void * @throws \Kirby\Exception\InvalidArgumentException */ public function updateArgument(string $name, $value): void diff --git a/kirby/src/Cms/Fieldset.php b/kirby/src/Cms/Fieldset.php index ff577f5..20338f7 100644 --- a/kirby/src/Cms/Fieldset.php +++ b/kirby/src/Cms/Fieldset.php @@ -21,24 +21,21 @@ class Fieldset extends Item { public const ITEMS_CLASS = Fieldsets::class; - protected $disabled; - protected $editable; - protected $fields = []; - protected $icon; - protected $label; - protected $model; - protected $name; - protected $preview; - protected $tabs; - protected $translate; - protected $type; - protected $unset; - protected $wysiwyg; + protected bool $disabled; + protected bool $editable; + protected array $fields = []; + protected string|null $icon; + protected string|null $label; + protected string|null $name; + protected string|bool|null $preview; + protected array $tabs; + protected bool $translate; + protected string $type; + protected bool $unset; + protected bool $wysiwyg; /** * Creates a new Fieldset object - * - * @param array $params */ public function __construct(array $params = []) { @@ -50,17 +47,17 @@ class Fieldset extends Item parent::__construct($params); - $this->disabled = $params['disabled'] ?? false; - $this->editable = $params['editable'] ?? true; - $this->icon = $params['icon'] ?? null; - $this->model = $this->parent; - $this->name = $this->createName($params['title'] ?? $params['name'] ?? Str::ucfirst($this->type)); - $this->label = $this->createLabel($params['label'] ?? null); - $this->preview = $params['preview'] ?? null; - $this->tabs = $this->createTabs($params); - $this->translate = $params['translate'] ?? true; - $this->unset = $params['unset'] ?? false; - $this->wysiwyg = $params['wysiwyg'] ?? false; + $this->disabled = $params['disabled'] ?? false; + $this->editable = $params['editable'] ?? true; + $this->icon = $params['icon'] ?? null; + $params['title'] ??= $params['name'] ?? Str::ucfirst($this->type); + $this->name = $this->createName($params['title']); + $this->label = $this->createLabel($params['label'] ?? null); + $this->preview = $params['preview'] ?? null; + $this->tabs = $this->createTabs($params); + $this->translate = $params['translate'] ?? true; + $this->unset = $params['unset'] ?? false; + $this->wysiwyg = $params['wysiwyg'] ?? false; if ( $this->translate === false && @@ -73,10 +70,6 @@ class Fieldset extends Item } } - /** - * @param array $fields - * @return array - */ protected function createFields(array $fields = []): array { $fields = Blueprint::fieldsProps($fields); @@ -88,28 +81,16 @@ class Fieldset extends Item return $fields; } - /** - * @param array|string $name - * @return string|null - */ - protected function createName($name): string|null + protected function createName(array|string $name): string|null { return I18n::translate($name, $name); } - /** - * @param array|string $label - * @return string|null - */ - protected function createLabel($label = null): string|null + protected function createLabel(array|string|null $label = null): string|null { return I18n::translate($label, $label); } - /** - * @param array $params - * @return array - */ protected function createTabs(array $params = []): array { $tabs = $params['tabs'] ?? []; @@ -133,9 +114,10 @@ class Fieldset extends Item $tab = Blueprint::extend($tab); - $tab['fields'] = $this->createFields($tab['fields'] ?? []); - $tab['label'] = $this->createLabel($tab['label'] ?? Str::ucfirst($name)); - $tab['name'] = $name; + $tab['fields'] = $this->createFields($tab['fields'] ?? []); + $tab['label'] ??= Str::ucfirst($name); + $tab['label'] = $this->createLabel($tab['label']); + $tab['name'] = $name; $tabs[$name] = $tab; } @@ -143,17 +125,11 @@ class Fieldset extends Item return $tabs; } - /** - * @return bool - */ public function disabled(): bool { return $this->disabled; } - /** - * @return bool - */ public function editable(): bool { if ($this->editable === false) { @@ -167,9 +143,6 @@ class Fieldset extends Item return true; } - /** - * @return array - */ public function fields(): array { return $this->fields; @@ -177,88 +150,57 @@ class Fieldset extends Item /** * Creates a form for the given fields - * - * @param array $fields - * @param array $input - * @return \Kirby\Form\Form */ - public function form(array $fields, array $input = []) + public function form(array $fields, array $input = []): Form { return new Form([ 'fields' => $fields, - 'model' => $this->model, + 'model' => $this->parent, 'strict' => true, 'values' => $input, ]); } - /** - * @return string|null - */ public function icon(): string|null { return $this->icon; } - /** - * @return string|null - */ public function label(): string|null { return $this->label; } - /** - * @return \Kirby\Cms\ModelWithContent - */ - public function model() + public function model(): ModelWithContent { - return $this->model; + return $this->parent; } - /** - * @return string - */ public function name(): string { return $this->name; } - /** - * @return string|bool - */ - public function preview() + public function preview(): string|bool|null { return $this->preview; } - /** - * @return array - */ public function tabs(): array { return $this->tabs; } - /** - * @return bool - */ public function translate(): bool { return $this->translate; } - /** - * @return string - */ public function type(): string { return $this->type; } - /** - * @return array - */ public function toArray(): array { return [ @@ -276,17 +218,11 @@ class Fieldset extends Item ]; } - /** - * @return bool - */ public function unset(): bool { return $this->unset; } - /** - * @return bool - */ public function wysiwyg(): bool { return $this->wysiwyg; diff --git a/kirby/src/Cms/Fieldsets.php b/kirby/src/Cms/Fieldsets.php index 4326c29..4f5f5ce 100644 --- a/kirby/src/Cms/Fieldsets.php +++ b/kirby/src/Cms/Fieldsets.php @@ -23,15 +23,13 @@ class Fieldsets extends Items /** * All registered fieldsets methods - * - * @var array */ - public static $methods = []; + public static array $methods = []; - protected static function createFieldsets($params) + protected static function createFieldsets(array $params): array { $fieldsets = []; - $groups = []; + $groups = []; foreach ($params as $type => $fieldset) { if (is_int($type) === true && is_string($fieldset)) { @@ -75,8 +73,10 @@ class Fieldsets extends Items ]; } - public static function factory(array $items = null, array $params = []) - { + public static function factory( + array|null $items = null, + array $params = [] + ): static { $items ??= App::instance()->option('blocks.fieldsets', [ 'code' => 'blocks/code', 'gallery' => 'blocks/gallery', @@ -92,7 +92,10 @@ class Fieldsets extends Items $result = static::createFieldsets($items); - return parent::factory($result['fieldsets'], ['groups' => $result['groups']] + $params); + return parent::factory( + $result['fieldsets'], + ['groups' => $result['groups']] + $params + ); } public function groups(): array diff --git a/kirby/src/Cms/File.php b/kirby/src/Cms/File.php index 9bc1ae0..aa0d7d3 100644 --- a/kirby/src/Cms/File.php +++ b/kirby/src/Cms/File.php @@ -2,10 +2,12 @@ namespace Kirby\Cms; +use Exception; +use IntlDateFormatter; +use Kirby\Exception\InvalidArgumentException; use Kirby\Filesystem\F; use Kirby\Filesystem\IsFile; use Kirby\Panel\File as Panel; -use Kirby\Toolkit\A; use Kirby\Toolkit\Str; /** @@ -38,61 +40,65 @@ class File extends ModelWithContent public const CLASS_ALIAS = 'file'; - /** - * Cache for the initialized blueprint object - * - * @var \Kirby\Cms\FileBlueprint - */ - protected $blueprint; - - /** - * @var string - */ - protected $filename; - - /** - * @var string - */ - protected $id; - /** * All registered file methods - * - * @var array + * @todo Remove when support for PHP 8.2 is dropped */ - public static $methods = []; + public static array $methods = []; + + /** + * Cache for the initialized blueprint object + */ + protected FileBlueprint|null $blueprint = null; + + protected string $filename; + + protected string $id; /** * The parent object - * - * @var \Kirby\Cms\Model */ - protected $parent; + protected Page|Site|User|null $parent = null; /** * The absolute path to the file */ - protected string|null $root = null; + protected string|null $root; - /** - * @var string - */ - protected $template; + protected string|null $template; /** * The public file Url */ - protected string|null $url = null; + protected string|null $url; + + /** + * Creates a new File object + */ + public function __construct(array $props) + { + parent::__construct($props); + + if (isset($props['filename'], $props['parent']) === false) { + throw new InvalidArgumentException('The filename and parent are required'); + } + + $this->filename = $props['filename']; + $this->parent = $props['parent']; + $this->template = $props['template'] ?? null; + // Always set the root to null, to invoke + // auto root detection + $this->root = null; + $this->url = $props['url'] ?? null; + + $this->setBlueprint($props['blueprint'] ?? null); + } /** * Magic caller for file methods * and content fields. (in this order) - * - * @param string $method - * @param array $arguments - * @return mixed */ - public function __call(string $method, array $arguments = []) + public function __call(string $method, array $arguments = []): mixed { // public property access if (isset($this->$method) === true) { @@ -113,25 +119,8 @@ class File extends ModelWithContent return $this->content()->get($method); } - /** - * Creates a new File object - * - * @param array $props - */ - public function __construct(array $props) - { - // set filename as the most important prop first - // TODO: refactor later to avoid redundant prop setting - $this->setProperty('filename', $props['filename'] ?? null, true); - - // set other properties - $this->setProperties($props); - } - /** * Improved `var_dump` output - * - * @return array */ public function __debugInfo(): array { @@ -143,10 +132,7 @@ class File extends ModelWithContent /** * Returns the url to api endpoint - * * @internal - * @param bool $relative - * @return string */ public function apiUrl(bool $relative = false): string { @@ -155,27 +141,93 @@ class File extends ModelWithContent /** * Returns the FileBlueprint object for the file - * - * @return \Kirby\Cms\FileBlueprint */ - public function blueprint() + public function blueprint(): FileBlueprint { - if ($this->blueprint instanceof FileBlueprint) { - return $this->blueprint; + return $this->blueprint ??= FileBlueprint::factory( + 'files/' . $this->template(), + 'files/default', + $this + ); + } + + /** + * Returns an array with all blueprints that are available for the file + * by comparing files sections and files fields of the parent model + */ + public function blueprints(string|null $inSection = null): array + { + // get cached results for the current file model + // (except when collecting for a specific section) + if ($inSection === null && $this->blueprints !== null) { + return $this->blueprints; // @codeCoverageIgnore } - return $this->blueprint = FileBlueprint::factory('files/' . $this->template(), 'files/default', $this); + // always include the current template as option + $templates = [ + $this->template() ?? 'default', + ...$this->parent()->blueprint()->acceptedFileTemplates($inSection) + ]; + + // make sure every template is only included once + $templates = array_unique(array_filter($templates)); + + // load the blueprint details for each collected template name + $blueprints = []; + + foreach ($templates as $template) { + // default template doesn't need to exist as file + // to be included in the list + if ($template === 'default') { + $blueprints[$template] = [ + 'name' => 'default', + 'title' => '– (default)', + ]; + continue; + } + + if ($blueprint = FileBlueprint::factory('files/' . $template, null, $this)) { + try { + // ensure that file matches `accept` option, + // if not remove template from available list + $this->match($blueprint->accept()); + + $blueprints[$template] = [ + 'name' => $name = Str::after($blueprint->name(), '/'), + 'title' => $blueprint->title() . ' (' . $name . ')', + ]; + } catch (Exception) { + // skip when `accept` doesn't match + } + } + } + + $blueprints = array_values($blueprints); + + // sort blueprints alphabetically while + // making sure the default blueprint is on top of list + usort($blueprints, fn ($a, $b) => match (true) { + $a['name'] === 'default' => -1, + $b['name'] === 'default' => 1, + default => strnatcmp($a['title'], $b['title']) + }); + + // no caching for when collecting for specific section + if ($inSection !== null) { + return $blueprints; // @codeCoverageIgnore + } + + return $this->blueprints = $blueprints; } /** * Store the template in addition to the * other content. - * * @internal */ public function contentFileData( array $data, - string $languageCode = null + string|null $languageCode = null ): array { // only add the template in, if the $data array // doesn't explicitly unsets it @@ -192,42 +244,41 @@ class File extends ModelWithContent /** * Returns the directory in which * the content file is located - * * @internal - * @return string + * @deprecated 4.0.0 + * @todo Remove in v5 + * @codeCoverageIgnore */ public function contentFileDirectory(): string { + Helpers::deprecated('The internal $model->contentFileDirectory() method has been deprecated. Please let us know via a GitHub issue if you need this method and tell us your use case.', 'model-content-file'); return dirname($this->root()); } /** * Filename for the content file - * * @internal - * @return string + * @deprecated 4.0.0 + * @todo Remove in v5 + * @codeCoverageIgnore */ public function contentFileName(): string { + Helpers::deprecated('The internal $model->contentFileName() method has been deprecated. Please let us know via a GitHub issue if you need this method and tell us your use case.', 'model-content-file'); return $this->filename(); } /** * Constructs a File object - * * @internal - * @param mixed $props - * @return static */ - public static function factory($props) + public static function factory(array $props): static { return new static($props); } /** * Returns the filename with extension - * - * @return string */ public function filename(): string { @@ -236,19 +287,14 @@ class File extends ModelWithContent /** * Returns the parent Files collection - * - * @return \Kirby\Cms\Files */ - public function files() + public function files(): Files { return $this->siblingsCollection(); } /** * Converts the file to html - * - * @param array $attr - * @return string */ public function html(array $attr = []): string { @@ -260,55 +306,88 @@ class File extends ModelWithContent /** * Returns the id - * - * @return string */ public function id(): string { - if ($this->id !== null) { - return $this->id; - } - if ( $this->parent() instanceof Page || $this->parent() instanceof User ) { - return $this->id = $this->parent()->id() . '/' . $this->filename(); + return $this->id ??= $this->parent()->id() . '/' . $this->filename(); } - return $this->id = $this->filename(); + return $this->id ??= $this->filename(); } /** * Compares the current object with the given file object - * - * @param \Kirby\Cms\File $file - * @return bool */ public function is(File $file): bool { return $this->id() === $file->id(); } + /** + * Checks if the files is accessible. + * This permission depends on the `read` option until v5 + */ + public function isAccessible(): bool + { + // TODO: remove this check when `read` option deprecated in v5 + if ($this->isReadable() === false) { + return false; + } + + static $accessible = []; + $role = $this->kirby()->user()?->role()->id() ?? '__none__'; + $template = $this->template() ?? '__none__'; + $accessible[$role] ??= []; + + return $accessible[$role][$template] ??= $this->permissions()->can('access'); + } + + /** + * Check if the file can be listable by the current user + * This permission depends on the `read` option until v5 + */ + public function isListable(): bool + { + // TODO: remove this check when `read` option deprecated in v5 + if ($this->isReadable() === false) { + return false; + } + + // not accessible also means not listable + if ($this->isAccessible() === false) { + return false; + } + + static $listable = []; + $role = $this->kirby()->user()?->role()->id() ?? '__none__'; + $template = $this->template() ?? '__none__'; + $listable[$role] ??= []; + + return $listable[$role][$template] ??= $this->permissions()->can('list'); + } + /** * Check if the file can be read by the current user * - * @return bool + * @todo Deprecate `read` option in v5 and make the necessary changes for `access` and `list` options. */ public function isReadable(): bool { - static $readable = []; + static $readable = []; + $role = $this->kirby()->user()?->role()->id() ?? '__none__'; + $template = $this->template() ?? '__none__'; + $readable[$role] ??= []; - $template = $this->template(); - - return $readable[$template] ??= $this->permissions()->can('read'); + return $readable[$role][$template] ??= $this->permissions()->can('read'); } /** * Creates a unique media hash - * * @internal - * @return string */ public function mediaHash(): string { @@ -317,9 +396,7 @@ class File extends ModelWithContent /** * Returns the absolute path to the file in the public media folder - * * @internal - * @return string */ public function mediaRoot(): string { @@ -328,9 +405,7 @@ class File extends ModelWithContent /** * Creates a non-guessable token string for this file - * * @internal - * @return string */ public function mediaToken(): string { @@ -340,9 +415,7 @@ class File extends ModelWithContent /** * Returns the absolute Url to the file in the public media folder - * * @internal - * @return string */ public function mediaUrl(): string { @@ -352,17 +425,16 @@ class File extends ModelWithContent /** * Get the file's last modification time. * - * @param string|\IntlDateFormatter|null $format * @param string|null $handler date, intl or strftime - * @param string|null $languageCode - * @return mixed */ - public function modified($format = null, string $handler = null, string $languageCode = null) - { + public function modified( + string|IntlDateFormatter|null $format = null, + string|null $handler = null, + string|null $languageCode = null + ): string|int|false { $file = $this->modifiedFile(); $content = $this->modifiedContent($languageCode); $modified = max($file, $content); - $handler ??= $this->kirby()->option('date.handler', 'date'); return Str::date($modified, $format, $handler); } @@ -370,20 +442,15 @@ class File extends ModelWithContent /** * Timestamp of the last modification * of the content file - * - * @param string|null $languageCode - * @return int */ - protected function modifiedContent(string $languageCode = null): int + protected function modifiedContent(string|null $languageCode = null): int { - return F::modified($this->contentFile($languageCode)); + return $this->storage()->modified('published', $languageCode) ?? 0; } /** * Timestamp of the last modification * of the source file - * - * @return int */ protected function modifiedFile(): int { @@ -392,10 +459,8 @@ class File extends ModelWithContent /** * Returns the parent Page object - * - * @return \Kirby\Cms\Page|null */ - public function page() + public function page(): Page|null { if ($this->parent() instanceof Page) { return $this->parent(); @@ -406,29 +471,23 @@ class File extends ModelWithContent /** * Returns the panel info object - * - * @return \Kirby\Panel\File */ - public function panel() + public function panel(): Panel { return new Panel($this); } /** - * Returns the parent Model object - * - * @return \Kirby\Cms\Model + * Returns the parent object */ - public function parent() + public function parent(): Page|Site|User { return $this->parent ??= $this->kirby()->site(); } /** * Returns the parent id if a parent exists - * * @internal - * @return string */ public function parentId(): string { @@ -437,13 +496,14 @@ class File extends ModelWithContent /** * Returns a collection of all parent pages - * - * @return \Kirby\Cms\Pages */ - public function parents() + public function parents(): Pages { if ($this->parent() instanceof Page) { - return $this->parent()->parents()->prepend($this->parent()->id(), $this->parent()); + return $this->parent()->parents()->prepend( + $this->parent()->id(), + $this->parent() + ); } return new Pages(); @@ -460,18 +520,14 @@ class File extends ModelWithContent /** * Returns the permissions object for this file - * - * @return \Kirby\Cms\FilePermissions */ - public function permissions() + public function permissions(): FilePermissions { return new FilePermissions($this); } /** * Returns the absolute root to the file - * - * @return string|null */ public function root(): string|null { @@ -481,10 +537,8 @@ class File extends ModelWithContent /** * Returns the FileRules class to * validate any important action. - * - * @return \Kirby\Cms\FileRules */ - protected function rules() + protected function rules(): FileRules { return new FileRules(); } @@ -492,10 +546,9 @@ class File extends ModelWithContent /** * Sets the Blueprint object * - * @param array|null $blueprint * @return $this */ - protected function setBlueprint(array $blueprint = null) + protected function setBlueprint(array|null $blueprint = null): static { if ($blueprint !== null) { $blueprint['model'] = $this; @@ -505,82 +558,19 @@ class File extends ModelWithContent return $this; } - /** - * Sets the filename - * - * @param string $filename - * @return $this - */ - protected function setFilename(string $filename) - { - $this->filename = $filename; - return $this; - } - - /** - * Sets the parent model object - * - * @param \Kirby\Cms\Model $parent - * @return $this - */ - protected function setParent(Model $parent) - { - $this->parent = $parent; - return $this; - } - - /** - * Always set the root to null, to invoke - * auto root detection - * - * @param string|null $root - * @return $this - */ - protected function setRoot(string $root = null) - { - $this->root = null; - return $this; - } - - /** - * @param string|null $template - * @return $this - */ - protected function setTemplate(string $template = null) - { - $this->template = $template; - return $this; - } - - /** - * Sets the url - * - * @param string|null $url - * @return $this - */ - protected function setUrl(string $url = null) - { - $this->url = $url; - return $this; - } - /** * Returns the parent Files collection * @internal - * - * @return \Kirby\Cms\Files */ - protected function siblingsCollection() + protected function siblingsCollection(): Files { return $this->parent()->files(); } /** * Returns the parent Site object - * - * @return \Kirby\Cms\Site */ - public function site() + public function site(): Site { if ($this->parent() instanceof Site) { return $this->parent(); @@ -591,21 +581,16 @@ class File extends ModelWithContent /** * Returns the final template - * - * @return string|null */ public function template(): string|null { - return $this->template ??= $this->content()->get('template')->value(); + return $this->template ??= $this->content('default')->get('template')->value(); } /** * Returns siblings with the same template - * - * @param bool $self - * @return \Kirby\Cms\Files */ - public function templateSiblings(bool $self = true) + public function templateSiblings(bool $self = true): Files { return $this->siblings($self)->filter('template', $this->template()); } @@ -614,18 +599,17 @@ class File extends ModelWithContent * Extended info for the array export * by injecting the information from * the asset. - * - * @return array */ public function toArray(): array { - return array_merge($this->asset()->toArray(), parent::toArray()); + return array_merge(parent::toArray(), $this->asset()->toArray(), [ + 'id' => $this->id(), + 'template' => $this->template(), + ]); } /** * Returns the Url - * - * @return string */ public function url(): string { @@ -636,10 +620,8 @@ class File extends ModelWithContent * Simplified File URL that uses the parent * Page URL and the filename as a more stable * alternative for the media URLs. - * - * @return string */ - public function previewUrl(): string + public function previewUrl(): string|null { $parent = $this->parent(); $url = Url::to($this->id()); diff --git a/kirby/src/Cms/FileActions.php b/kirby/src/Cms/FileActions.php index 5d2ce6f..9c5d2f8 100644 --- a/kirby/src/Cms/FileActions.php +++ b/kirby/src/Cms/FileActions.php @@ -21,29 +21,51 @@ use Kirby\Uuid\Uuids; */ trait FileActions { - /** - * Renames the file without touching the extension - * The store is used to actually execute this. - * - * @param string $name - * @param bool $sanitize - * @return $this|static - * @throws \Kirby\Exception\LogicException - */ - public function changeName(string $name, bool $sanitize = true) - { - if ($sanitize === true) { - $name = F::safeName($name); + protected function changeExtension( + File $file, + string|null $extension = null + ): File { + if ( + $extension === null || + $extension === $file->extension() + ) { + return $file; } + return $file->changeName($file->name(), false, $extension); + } + + /** + * Renames the file (optionally also the extension). + * The store is used to actually execute this. + * + * @throws \Kirby\Exception\LogicException + */ + public function changeName( + string $name, + bool $sanitize = true, + string|null $extension = null + ): static { + if ($sanitize === true) { + // sanitize the basename part only + // as the extension isn't included in $name + $name = F::safeBasename($name, false); + } + + // if no extension is passed, make sure to maintain current one + $extension ??= $this->extension(); + // don't rename if not necessary - if ($name === $this->name()) { + if ( + $name === $this->name() && + $extension === $this->extension() + ) { return $this; } - return $this->commit('changeName', ['file' => $this, 'name' => $name], function ($oldFile, $name) { + return $this->commit('changeName', ['file' => $this, 'name' => $name, 'extension' => $extension], function ($oldFile, $name, $extension) { $newFile = $oldFile->clone([ - 'filename' => $name . '.' . $oldFile->extension(), + 'filename' => $name . '.' . $extension, ]); // remove all public versions, lock and clear UUID cache @@ -60,16 +82,11 @@ trait FileActions // rename the main file F::move($oldFile->root(), $newFile->root()); - if ($newFile->kirby()->multilang() === true) { - foreach ($newFile->translations() as $translation) { - $translationCode = $translation->code(); - - // rename the content file - F::move($oldFile->contentFile($translationCode), $newFile->contentFile($translationCode)); - } - } else { - // rename the content file - F::move($oldFile->contentFile(), $newFile->contentFile()); + // move the content storage versions + foreach ($oldFile->storage()->all() as $version => $lang) { + $content = $oldFile->storage()->read($version, $lang); + $oldFile->storage()->delete($version, $lang); + $newFile->storage()->create($version, $lang, $content); } // update collections @@ -82,12 +99,14 @@ trait FileActions /** * Changes the file's sorting number in the meta file - * - * @param int $sort - * @return static */ - public function changeSort(int $sort) + public function changeSort(int $sort): static { + // skip if the sort number stays the same + if ($this->sort()->value() === $sort) { + return $this; + } + return $this->commit( 'changeSort', ['file' => $this, 'position' => $sort], @@ -95,6 +114,42 @@ trait FileActions ); } + /** + * @return $this|static + */ + public function changeTemplate(string|null $template): static + { + if ($template === $this->template()) { + return $this; + } + + $arguments = [ + 'file' => $this, + 'template' => $template ?? 'default' + ]; + + return $this->commit('changeTemplate', $arguments, function ($oldFile, $template) { + // convert to new template/blueprint incl. content + $file = $oldFile->convertTo($template); + + // update template, prefer unset over writing `default` + if ($template === 'default') { + $template = null; + } + + $file = $file->update( + ['template' => $template], + 'default' + ); + + // resize the file if configured by new blueprint + $create = $file->blueprint()->create(); + $file = $file->manipulate($create); + + return $file; + }); + } + /** * Commits a file action, by following these steps * @@ -103,14 +158,12 @@ trait FileActions * 3. commits the store action * 4. sends the after hook * 5. returns the result - * - * @param string $action - * @param array $arguments - * @param Closure $callback - * @return mixed */ - protected function commit(string $action, array $arguments, Closure $callback) - { + protected function commit( + string $action, + array $arguments, + Closure $callback + ): mixed { $old = $this->hardcopy(); $kirby = $this->kirby(); $argumentValues = array_values($arguments); @@ -134,24 +187,20 @@ trait FileActions /** * Copy the file to the given page - * - * @param \Kirby\Cms\Page $page - * @return \Kirby\Cms\File + * @internal */ - public function copy(Page $page) + public function copy(Page $page): static { F::copy($this->root(), $page->root() . '/' . $this->filename()); + $copy = $page->clone()->file($this->filename()); - if ($this->kirby()->multilang() === true) { - foreach ($this->kirby()->languages() as $language) { - $contentFile = $this->contentFile($language->code()); - F::copy($contentFile, $page->root() . '/' . basename($contentFile)); - } - } else { - $contentFile = $this->contentFile(); - F::copy($contentFile, $page->root() . '/' . basename($contentFile)); + foreach ($this->storage()->all() as $version => $lang) { + $content = $this->storage()->read($version, $lang); + $copy->storage()->create($version, $lang, $content); } + // ensure the content is re-read after copying it + // @todo find a more elegant way $copy = $page->clone()->file($this->filename()); // overwrite with new UUID (remove old, add new) @@ -168,13 +217,11 @@ trait FileActions * writing, so it can be replaced by any other * way of generating files. * - * @param array $props * @param bool $move If set to `true`, the source will be deleted - * @return static * @throws \Kirby\Exception\InvalidArgumentException * @throws \Kirby\Exception\LogicException */ - public static function create(array $props, bool $move = false) + public static function create(array $props, bool $move = false): File { if (isset($props['source'], $props['parent']) === false) { throw new InvalidArgumentException('Please provide the "source" and "parent" props for the File'); @@ -192,9 +239,25 @@ trait FileActions // gather content $content = $props['content'] ?? []; - // make sure that a UUID gets generated and - // added to content right away - if (Uuids::enabled() === true) { + // make sure that a UUID gets generated + // and added to content right away + if ( + Uuids::enabled() === true && + empty($content['uuid']) === true + ) { + // sets the current uuid if it is the exact same file + if ($file->exists() === true) { + $existing = $file->parent()->file($file->filename()); + + if ( + $file->sha1() === $upload->sha1() && + $file->template() === $existing->template() + ) { + // use existing content data if it is the exact same file + $content = $existing->content()->toArray(); + } + } + $content['uuid'] ??= Uuid::generate(); } @@ -204,9 +267,14 @@ trait FileActions // inject the content $file = $file->clone(['content' => $form->strings(true)]); + // if the format is different from the original, + // we need to already rename it so that the correct file rules + // are applied + $create = $file->blueprint()->create(); + // run the hook $arguments = compact('file', 'upload'); - return $file->commit('create', $arguments, function ($file, $upload) use ($move) { + return $file->commit('create', $arguments, function ($file, $upload) use ($create, $move) { // remove all public versions, lock and clear UUID cache $file->unpublish(); @@ -218,15 +286,15 @@ trait FileActions throw new LogicException('The file could not be created'); } - // always create pages in the default language - if ($file->kirby()->multilang() === true) { - $languageCode = $file->kirby()->defaultLanguage()->code(); - } else { - $languageCode = null; - } + // resize the file on upload if configured + $file = $file->manipulate($create); // store the content if necessary - $file->save($file->content()->toArray(), $languageCode); + // (always create files in the default language) + $file->save( + $file->content()->toArray(), + $file->kirby()->defaultLanguage()?->code() + ); // add the file to the list of siblings $file->siblings()->append($file->id(), $file); @@ -239,8 +307,6 @@ trait FileActions /** * Deletes the file. The store is used to * manipulate the filesystem or whatever you prefer. - * - * @return bool */ public function delete(): bool { @@ -248,12 +314,8 @@ trait FileActions // remove all public versions, lock and clear UUID cache $file->unpublish(); - if ($file->kirby()->multilang() === true) { - foreach ($file->translations() as $translation) { - F::remove($file->contentFile($translation->code())); - } - } else { - F::remove($file->contentFile()); + foreach ($file->storage()->all() as $version => $lang) { + $file->storage()->delete($version, $lang); } F::remove($file->root()); @@ -265,13 +327,36 @@ trait FileActions }); } + /** + * Resizes/crops the original file with Kirby's thumb handler + */ + public function manipulate(array|null $options = []): static + { + // nothing to process + if (empty($options) === true || $this->isResizable() === false) { + return $this; + } + + // generate image file and overwrite it in place + $this->kirby()->thumb($this->root(), $this->root(), $options); + + $file = $this->clone(); + + // change the file extension if format option configured + if ($format = $options['format'] ?? null) { + $file = $file->changeExtension($file, $format); + } + + return $file; + } + /** * Move the file to the public media folder * if it's not already there. * * @return $this */ - public function publish() + public function publish(): static { Media::publish($this, $this->mediaRoot()); return $this; @@ -284,12 +369,10 @@ trait FileActions * finally decides what it will support as * source. * - * @param string $source * @param bool $move If set to `true`, the source will be deleted - * @return static * @throws \Kirby\Exception\LogicException */ - public function replace(string $source, bool $move = false) + public function replace(string $source, bool $move = false): static { $file = $this->clone(); @@ -310,6 +393,10 @@ trait FileActions throw new LogicException('The file could not be created'); } + // apply the resizing/crop options from the blueprint + $create = $file->blueprint()->create(); + $file = $file->manipulate($create); + // return a fresh clone return $file->clone(); }); @@ -317,15 +404,13 @@ trait FileActions /** * Stores the content on disk - * * @internal - * @param array|null $data - * @param string|null $languageCode - * @param bool $overwrite - * @return static */ - public function save(array $data = null, string $languageCode = null, bool $overwrite = false) - { + public function save( + array|null $data = null, + string|null $languageCode = null, + bool $overwrite = false + ): static { $file = parent::save($data, $languageCode, $overwrite); // update model in siblings collection @@ -339,7 +424,7 @@ trait FileActions * * @return $this */ - public function unpublish(bool $onlyMedia = false) + public function unpublish(bool $onlyMedia = false): static { // unpublish media files Media::unpublish($this->parent()->mediaRoot(), $this); @@ -354,4 +439,23 @@ trait FileActions return $this; } + + /** + * Updates the file's data and ensures that + * media files get wiped if `focus` changed + * + * @throws \Kirby\Exception\InvalidArgumentException If the input array contains invalid values + */ + public function update( + array|null $input = null, + string|null $languageCode = null, + bool $validate = false + ): static { + // delete all public media versions when focus field gets changed + if (($input['focus'] ?? null) !== $this->focus()->value()) { + $this->unpublish(true); + } + + return parent::update($input, $languageCode, $validate); + } } diff --git a/kirby/src/Cms/FileBlueprint.php b/kirby/src/Cms/FileBlueprint.php index 9d17e5f..1cc50be 100644 --- a/kirby/src/Cms/FileBlueprint.php +++ b/kirby/src/Cms/FileBlueprint.php @@ -21,10 +21,8 @@ class FileBlueprint extends Blueprint /** * `true` if the default accepted * types are being used - * - * @var bool */ - protected $defaultTypes = false; + protected bool $defaultTypes = false; public function __construct(array $props) { @@ -35,12 +33,15 @@ class FileBlueprint extends Blueprint $this->props['options'] ?? true, // defaults [ - 'changeName' => null, - 'create' => null, - 'delete' => null, - 'read' => null, - 'replace' => null, - 'update' => null, + 'access' => null, + 'changeName' => null, + 'changeTemplate' => null, + 'create' => null, + 'delete' => null, + 'list' => null, + 'read' => null, + 'replace' => null, + 'update' => null, ] ); @@ -48,9 +49,6 @@ class FileBlueprint extends Blueprint $this->props['accept'] = $this->normalizeAccept($this->props['accept'] ?? []); } - /** - * @return array - */ public function accept(): array { return $this->props['accept']; @@ -60,7 +58,8 @@ class FileBlueprint extends Blueprint * Returns the list of all accepted MIME types for * file upload or `*` if all MIME types are allowed * - * @return string + * @deprecated 4.2.0 Use `acceptAttribute` instead + * @todo 5.0.0 Remove method */ public function acceptMime(): string { @@ -121,10 +120,74 @@ class FileBlueprint extends Blueprint } /** - * @param mixed $accept - * @return array + * Returns the list of all accepted file extensions + * for file upload or `*` if all extensions are allowed + * + * If a MIME type is specified in the blueprint, the `extension` and `type` options are ignored for the browser. + * Extensions and types, however, are still used to validate an uploaded file on the server. + * This behavior might change in the future to better represent which file extensions are actually allowed. + * + * If no MIME type is specified, the intersection between manually defined extensions and the Kirby "file types" is returned. + * If the intersection is empty, an empty string is returned. + * This behavior might change in the future to instead return the union of `mime`, `extension` and `type`. + * + * @since 4.2.0 */ - protected function normalizeAccept($accept = null): array + public function acceptAttribute(): string + { + // don't disclose the specific default types + if ($this->defaultTypes === true) { + return '*'; + } + + $accept = $this->accept(); + + // get extensions from "mime" option + if (is_array($accept['mime']) === true) { + // determine the extensions for each MIME type + $extensions = array_map( + fn ($pattern) => Mime::toExtensions($pattern, true), + $accept['mime'] + ); + + $fromMime = array_unique(array_merge(...array_values($extensions))); + + // return early to ignore the other options + return implode(',', array_map(fn ($ext) => ".$ext", $fromMime)); + } + + $restrictions = []; + + // get extensions from "type" option + if (is_array($accept['type']) === true) { + $extensions = array_map( + fn ($type) => F::typeToExtensions($type) ?? [], + $accept['type'] + ); + + $fromType = array_merge(...array_values($extensions)); + $restrictions[] = $fromType; + } + + // get extensions from "extension" option + if (is_array($accept['extension']) === true) { + $restrictions[] = $accept['extension']; + } + + // intersect all restrictions + $list = match (count($restrictions)) { + 0 => [], + 1 => $restrictions[0], + default => array_intersect(...$restrictions) + }; + + $list = array_unique($list); + + // format the list to include a leading dot on each extension + return implode(',', array_map(fn ($ext) => ".$ext", $list)); + } + + protected function normalizeAccept(mixed $accept = null): array { $accept = match (true) { is_string($accept) => ['mime' => $accept], diff --git a/kirby/src/Cms/FileModifications.php b/kirby/src/Cms/FileModifications.php index 11556ba..38629ab 100644 --- a/kirby/src/Cms/FileModifications.php +++ b/kirby/src/Cms/FileModifications.php @@ -2,6 +2,7 @@ namespace Kirby\Cms; +use Kirby\Content\Field; use Kirby\Exception\InvalidArgumentException; use Kirby\Filesystem\Asset; @@ -18,37 +19,30 @@ trait FileModifications { /** * Blurs the image by the given amount of pixels - * - * @param bool $pixels - * @return \Kirby\Cms\FileVersion|\Kirby\Cms\File */ - public function blur($pixels = true) + public function blur(int|bool $pixels = true): FileVersion|File|Asset { return $this->thumb(['blur' => $pixels]); } /** * Converts the image to black and white - * - * @return \Kirby\Cms\FileVersion|\Kirby\Cms\File */ - public function bw() + public function bw(): FileVersion|File|Asset { return $this->thumb(['grayscale' => true]); } /** * Crops the image by the given width and height - * - * @param int $width - * @param int|null $height - * @param string|array $options - * @return \Kirby\Cms\FileVersion|\Kirby\Cms\File */ - public function crop(int $width, int $height = null, $options = null) - { + public function crop( + int $width, + int|null $height = null, + $options = null + ): FileVersion|File|Asset { $quality = null; - $crop = 'center'; + $crop = true; if (is_int($options) === true) { $quality = $options; @@ -71,31 +65,24 @@ trait FileModifications /** * Alias for File::bw() - * - * @return \Kirby\Cms\FileVersion|\Kirby\Cms\File */ - public function grayscale() + public function grayscale(): FileVersion|File|Asset { return $this->thumb(['grayscale' => true]); } /** * Alias for File::bw() - * - * @return \Kirby\Cms\FileVersion|\Kirby\Cms\File */ - public function greyscale() + public function greyscale(): FileVersion|File|Asset { return $this->thumb(['grayscale' => true]); } /** * Sets the JPEG compression quality - * - * @param int $quality - * @return \Kirby\Cms\FileVersion|\Kirby\Cms\File */ - public function quality(int $quality) + public function quality(int $quality): FileVersion|File|Asset { return $this->thumb(['quality' => $quality]); } @@ -104,14 +91,13 @@ trait FileModifications * Resizes the file with the given width and height * while keeping the aspect ratio. * - * @param int|null $width - * @param int|null $height - * @param int|null $quality - * @return \Kirby\Cms\FileVersion|\Kirby\Cms\File * @throws \Kirby\Exception\InvalidArgumentException */ - public function resize(int $width = null, int $height = null, int $quality = null) - { + public function resize( + int|null $width = null, + int|null $height = null, + int|null $quality = null + ): FileVersion|File|Asset { return $this->thumb([ 'width' => $width, 'height' => $height, @@ -119,16 +105,21 @@ trait FileModifications ]); } + /** + * Sharpens the image + */ + public function sharpen(int $amount = 50): FileVersion|File|Asset + { + return $this->thumb(['sharpen' => $amount]); + } + /** * Create a srcset definition for the given sizes * Sizes can be defined as a simple array. They can * also be set up in the config with the thumbs.srcsets option. * @since 3.1.0 - * - * @param array|string|null $sizes - * @return string|null */ - public function srcset($sizes = null): string|null + public function srcset(array|string|null $sizes = null): string|null { if (empty($sizes) === true) { $sizes = $this->kirby()->option('thumbs.srcsets.default', []); @@ -175,12 +166,11 @@ trait FileModifications * could potentially also be a CDN or any other * place. * - * @param array|null|string $options - * @return \Kirby\Cms\FileVersion|\Kirby\Cms\File * @throws \Kirby\Exception\InvalidArgumentException */ - public function thumb($options = null) - { + public function thumb( + array|string|null $options = null + ): FileVersion|File|Asset { // thumb presets if (empty($options) === true) { $options = $this->kirby()->option('thumbs.presets.default'); @@ -192,6 +182,15 @@ trait FileModifications return $this; } + // fallback to content file options + if (($options['crop'] ?? false) === true) { + if ($this instanceof ModelWithContent === true) { + $options['crop'] = $this->focus()->value() ?? 'center'; + } else { + $options['crop'] = 'center'; + } + } + // fallback to global config options if (isset($options['format']) === false) { if ($format = $this->kirby()->option('thumbs.format')) { diff --git a/kirby/src/Cms/FilePermissions.php b/kirby/src/Cms/FilePermissions.php index e296de5..2f8b777 100644 --- a/kirby/src/Cms/FilePermissions.php +++ b/kirby/src/Cms/FilePermissions.php @@ -13,5 +13,14 @@ namespace Kirby\Cms; */ class FilePermissions extends ModelPermissions { - protected $category = 'files'; + protected string $category = 'files'; + + protected function canChangeTemplate(): bool + { + if (count($this->model->blueprints()) <= 1) { + return false; + } + + return true; + } } diff --git a/kirby/src/Cms/FilePicker.php b/kirby/src/Cms/FilePicker.php index 81899ba..1391110 100644 --- a/kirby/src/Cms/FilePicker.php +++ b/kirby/src/Cms/FilePicker.php @@ -19,8 +19,6 @@ class FilePicker extends Picker { /** * Extends the basic defaults - * - * @return array */ public function defaults(): array { @@ -33,10 +31,9 @@ class FilePicker extends Picker /** * Search all files for the picker * - * @return \Kirby\Cms\Files|null * @throws \Kirby\Exception\InvalidArgumentException */ - public function items() + public function items(): Files|null { $model = $this->options['model']; @@ -65,6 +62,9 @@ class FilePicker extends Picker default => throw new InvalidArgumentException('Your query must return a set of files') }; + // filter protected and hidden pages + $files = $files->filter('isListable', true); + // search $files = $this->search($files); diff --git a/kirby/src/Cms/FileRules.php b/kirby/src/Cms/FileRules.php index 64c961a..ee4b9dd 100644 --- a/kirby/src/Cms/FileRules.php +++ b/kirby/src/Cms/FileRules.php @@ -4,6 +4,7 @@ namespace Kirby\Cms; use Kirby\Exception\DuplicateException; use Kirby\Exception\InvalidArgumentException; +use Kirby\Exception\LogicException; use Kirby\Exception\PermissionException; use Kirby\Filesystem\File as BaseFile; use Kirby\Toolkit\Str; @@ -23,9 +24,6 @@ class FileRules /** * Validates if the filename can be changed * - * @param \Kirby\Cms\File $file - * @param string $name - * @return bool * @throws \Kirby\Exception\DuplicateException If a file with this name exists * @throws \Kirby\Exception\PermissionException If the user is not allowed to rename the file */ @@ -59,22 +57,51 @@ class FileRules /** * Validates if the file can be sorted - * - * @param \Kirby\Cms\File $file - * @param int $sort - * @return bool */ public static function changeSort(File $file, int $sort): bool { return true; } + /** + * Validates if the template of the file can be changed + * + * @throws \Kirby\Exception\LogicException If the template of the page cannot be changed at all + * @throws \Kirby\Exception\PermissionException If the user is not allowed to change the template + */ + public static function changeTemplate(File $file, string $template): bool + { + if ($file->permissions()->changeTemplate() !== true) { + throw new PermissionException([ + 'key' => 'file.changeTemplate.permission', + 'data' => ['id' => $file->id()] + ]); + } + + $blueprints = $file->blueprints(); + + // ensure that the $template is a valid blueprint + // option for this file + if ( + count($blueprints) <= 1 || + in_array($template, array_column($blueprints, 'name')) === false + ) { + throw new LogicException([ + 'key' => 'file.changeTemplate.invalid', + 'data' => [ + 'id' => $file->id(), + 'template' => $template, + 'blueprints' => implode(', ', array_column($blueprints, 'name')) + ] + ]); + } + + return true; + } + /** * Validates if the file can be created * - * @param \Kirby\Cms\File $file - * @param \Kirby\Filesystem\File $upload - * @return bool * @throws \Kirby\Exception\DuplicateException If a file with the same name exists * @throws \Kirby\Exception\PermissionException If the user is not allowed to create the file */ @@ -121,8 +148,6 @@ class FileRules /** * Validates if the file can be deleted * - * @param \Kirby\Cms\File $file - * @return bool * @throws \Kirby\Exception\PermissionException If the user is not allowed to delete the file */ public static function delete(File $file): bool @@ -137,9 +162,6 @@ class FileRules /** * Validates if the file can be replaced * - * @param \Kirby\Cms\File $file - * @param \Kirby\Filesystem\File $upload - * @return bool * @throws \Kirby\Exception\PermissionException If the user is not allowed to replace the file * @throws \Kirby\Exception\InvalidArgumentException If the file type of the new file is different */ @@ -170,9 +192,6 @@ class FileRules /** * Validates if the file can be updated * - * @param \Kirby\Cms\File $file - * @param array $content - * @return bool * @throws \Kirby\Exception\PermissionException If the user is not allowed to update the file */ public static function update(File $file, array $content = []): bool @@ -187,9 +206,6 @@ class FileRules /** * Validates the file extension * - * @param \Kirby\Cms\File $file - * @param string $extension - * @return bool * @throws \Kirby\Exception\InvalidArgumentException If the extension is missing or forbidden */ public static function validExtension(File $file, string $extension): bool @@ -235,20 +251,19 @@ class FileRules /** * Validates the extension, MIME type and filename * - * @param \Kirby\Cms\File $file - * @param string|null|false $mime If not passed, the MIME type is detected from the file, - * if `false`, the MIME type is not validated for performance reasons - * @return bool + * @param $mime If not passed, the MIME type is detected from the file, + * if `false`, the MIME type is not validated for performance reasons * @throws \Kirby\Exception\InvalidArgumentException If the extension, MIME type or filename is missing or forbidden */ - public static function validFile(File $file, $mime = null): bool - { - if ($mime === false) { + public static function validFile( + File $file, + string|false|null $mime = null + ): bool { + $validMime = match ($mime) { // request to skip the MIME check for performance reasons - $validMime = true; - } else { - $validMime = static::validMime($file, $mime ?? $file->mime()); - } + false => true, + default => static::validMime($file, $mime ?? $file->mime()) + }; return $validMime && @@ -259,9 +274,6 @@ class FileRules /** * Validates the filename * - * @param \Kirby\Cms\File $file - * @param string $filename - * @return bool * @throws \Kirby\Exception\InvalidArgumentException If the filename is missing or forbidden */ public static function validFilename(File $file, string $filename): bool @@ -298,15 +310,12 @@ class FileRules /** * Validates the MIME type * - * @param \Kirby\Cms\File $file - * @param string|null $mime - * @return bool * @throws \Kirby\Exception\InvalidArgumentException If the MIME type is missing or forbidden */ - public static function validMime(File $file, string $mime = null): bool + public static function validMime(File $file, string|null $mime = null): bool { // make it easier to compare the mime - $mime = strtolower($mime); + $mime = strtolower($mime ?? ''); if (empty($mime)) { throw new InvalidArgumentException([ diff --git a/kirby/src/Cms/FileVersion.php b/kirby/src/Cms/FileVersion.php index 96a497f..159da71 100644 --- a/kirby/src/Cms/FileVersion.php +++ b/kirby/src/Cms/FileVersion.php @@ -17,18 +17,22 @@ class FileVersion { use IsFile; - protected $modifications; + protected array $modifications; protected $original; + public function __construct(array $props) + { + $this->root = $props['root'] ?? null; + $this->url = $props['url'] ?? null; + $this->original = $props['original']; + $this->modifications = $props['modifications'] ?? []; + } + /** * Proxy for public properties, asset methods * and content field getters - * - * @param string $method - * @param array $arguments - * @return mixed */ - public function __call(string $method, array $arguments = []) + public function __call(string $method, array $arguments = []): mixed { // public property access if (isset($this->$method) === true) { @@ -46,14 +50,12 @@ class FileVersion // content fields if ($this->original() instanceof File) { - return $this->original()->content()->get($method, $arguments); + return $this->original()->content()->get($method); } } /** * Returns the unique ID - * - * @return string */ public function id(): string { @@ -62,30 +64,24 @@ class FileVersion /** * Returns the parent Kirby App instance - * - * @return \Kirby\Cms\App */ - public function kirby() + public function kirby(): App { return $this->original()->kirby(); } /** * Returns an array with all applied modifications - * - * @return array */ public function modifications(): array { - return $this->modifications ?? []; + return $this->modifications; } /** * Returns the instance of the original File object - * - * @return mixed */ - public function original() + public function original(): mixed { return $this->original; } @@ -96,7 +92,7 @@ class FileVersion * * @return $this */ - public function save() + public function save(): static { $this->kirby()->thumb( $this->original()->root(), @@ -106,36 +102,16 @@ class FileVersion return $this; } - /** - * Setter for modifications - * - * @param array|null $modifications - */ - protected function setModifications(array $modifications = null) - { - $this->modifications = $modifications; - } - - /** - * Setter for the original File object - * - * @param $original - */ - protected function setOriginal($original) - { - $this->original = $original; - } /** * Converts the object to an array - * - * @return array */ public function toArray(): array { - $array = array_merge($this->asset()->toArray(), [ - 'modifications' => $this->modifications(), - ]); + $array = array_merge( + $this->asset()->toArray(), + ['modifications' => $this->modifications()] + ); ksort($array); diff --git a/kirby/src/Cms/Files.php b/kirby/src/Cms/Files.php index 5e30528..ed5c76a 100644 --- a/kirby/src/Cms/Files.php +++ b/kirby/src/Cms/Files.php @@ -26,10 +26,8 @@ class Files extends Collection /** * All registered files methods - * - * @var array */ - public static $methods = []; + public static array $methods = []; /** * Adds a single file or @@ -40,25 +38,25 @@ class Files extends Collection * @return $this * @throws \Kirby\Exception\InvalidArgumentException When no `File` or `Files` object or an ID of an existing file is passed */ - public function add($object) + public function add($object): static { // add a files collection if ($object instanceof self) { $this->data = array_merge($this->data, $object->data); - // add a file by id + // add a file by id } elseif ( is_string($object) === true && $file = App::instance()->file($object) ) { $this->__set($file->id(), $file); - // add a file object + // add a file object } elseif ($object instanceof File) { $this->__set($object->id(), $object); - // give a useful error message on invalid input; - // silently ignore "empty" values for compatibility with existing setups + // give a useful error message on invalid input; + // silently ignore "empty" values for compatibility with existing setups } elseif (in_array($object, [null, false, true], true) !== true) { throw new InvalidArgumentException('You must pass a Files or File object or an ID of an existing file to the Files collection'); } @@ -74,7 +72,7 @@ class Files extends Collection * @param int $offset Sorting offset * @return $this */ - public function changeSort(array $files, int $offset = 0) + public function changeSort(array $files, int $offset = 0): static { foreach ($files as $filename) { if ($file = $this->get($filename)) { @@ -88,19 +86,13 @@ class Files extends Collection /** * Creates a files collection from an array of props - * - * @param array $files - * @param \Kirby\Cms\Model $parent - * @return static */ - public static function factory(array $files, Model $parent) + public static function factory(array $files, Page|Site|User $parent): static { $collection = new static([], $parent); - $kirby = $parent->kirby(); foreach ($files as $props) { $props['collection'] = $collection; - $props['kirby'] = $kirby; $props['parent'] = $parent; $file = File::factory($props); @@ -114,11 +106,8 @@ class Files extends Collection /** * Finds a file by its filename * @internal Use `$files->find()` instead - * - * @param string $key - * @return \Kirby\Cms\File|null */ - public function findByKey(string $key) + public function findByKey(string $key): File|null { if ($file = $this->findByUuid($key, 'file')) { return $file; @@ -136,7 +125,6 @@ class Files extends Collection * @param string|null|false $locale Locale for number formatting, * `null` for the current locale, * `false` to disable number formatting - * @return string */ public function niceSize($locale = null): string { @@ -147,8 +135,6 @@ class Files extends Collection * Returns the raw size for all * files in the collection * @since 3.6.0 - * - * @return int */ public function size(): int { @@ -158,10 +144,8 @@ class Files extends Collection /** * Returns the collection sorted by * the sort number and the filename - * - * @return static */ - public function sorted() + public function sorted(): static { return $this->sort('sort', 'asc', 'filename', 'asc'); } @@ -169,10 +153,9 @@ class Files extends Collection /** * Filter all files by the given template * - * @param null|string|array $template * @return $this|static */ - public function template($template) + public function template(string|array|null $template): static { if (empty($template) === true) { return $this; diff --git a/kirby/src/Cms/Find.php b/kirby/src/Cms/Find.php index cb2332c..6059c81 100644 --- a/kirby/src/Cms/Find.php +++ b/kirby/src/Cms/Find.php @@ -24,16 +24,17 @@ class Find * parent path and filename * * @param string $path Path to file's parent model - * @param string $filename Filename - * @return \Kirby\Cms\File|null * @throws \Kirby\Exception\NotFoundException if the file cannot be found */ - public static function file(string $path, string $filename) - { + public static function file( + string $path, + string $filename + ): File|null { $filename = urldecode($filename); - $file = static::parent($path)->file($filename); + $parent = empty($path) ? null : static::parent($path); + $file = App::instance()->file($filename, $parent); - if ($file?->isReadable() === true) { + if ($file?->isAccessible() === true) { return $file; } @@ -49,10 +50,9 @@ class Find * Returns the language object for the given code * * @param string $code Language code - * @return \Kirby\Cms\Language|null * @throws \Kirby\Exception\NotFoundException if the language cannot be found */ - public static function language(string $code) + public static function language(string $code): Language|null { if ($language = App::instance()->language($code)) { return $language; @@ -70,15 +70,16 @@ class Find * Returns the page object for the given id * * @param string $id Page's id - * @return \Kirby\Cms\Page|null * @throws \Kirby\Exception\NotFoundException if the page cannot be found */ - public static function page(string $id) + public static function page(string $id): Page|null { - $id = str_replace(['+', ' '], '/', $id); - $page = App::instance()->page($id); + // decode API ID encoding + $id = str_replace(['+', ' '], '/', $id); + $kirby = App::instance(); + $page = $kirby->page($id, null, true); - if ($page?->isReadable() === true) { + if ($page?->isAccessible() === true) { return $page; } @@ -94,11 +95,10 @@ class Find * Returns the model's object for the given path * * @param string $path Path to parent model - * @return \Kirby\Cms\Model|null * @throws \Kirby\Exception\InvalidArgumentException if the model type is invalid * @throws \Kirby\Exception\NotFoundException if the model cannot be found */ - public static function parent(string $path) + public static function parent(string $path): ModelWithContent { $path = trim($path, '/'); $modelType = in_array($path, ['site', 'account']) ? $path : trim(dirname($path), '/'); @@ -140,10 +140,9 @@ class Find * id is passed * * @param string|null $id User's id - * @return \Kirby\Cms\User|null * @throws \Kirby\Exception\NotFoundException if the user for the given id cannot be found */ - public static function user(string $id = null) + public static function user(string|null $id = null): User|null { // account is a reserved word to find the current // user. It's used in various API and area routes. diff --git a/kirby/src/Cms/HasChildren.php b/kirby/src/Cms/HasChildren.php index a798272..d12ee38 100644 --- a/kirby/src/Cms/HasChildren.php +++ b/kirby/src/Cms/HasChildren.php @@ -18,72 +18,40 @@ trait HasChildren { /** * The list of available published children - * - * @var \Kirby\Cms\Pages|null */ - public $children; + public Pages|null $children = null; /** * The list of available draft children - * - * @var \Kirby\Cms\Pages|null */ - public $drafts; + public Pages|null $drafts = null; /** * The combined list of available published * and draft children - * - * @var \Kirby\Cms\Pages|null */ - public $childrenAndDrafts; + public Pages|null $childrenAndDrafts = null; /** * Returns all published children - * - * @return \Kirby\Cms\Pages */ - public function children() + public function children(): Pages { - if ($this->children instanceof Pages) { - return $this->children; - } - - return $this->children = Pages::factory($this->inventory()['children'], $this); + return $this->children ??= Pages::factory($this->inventory()['children'], $this); } /** * Returns all published and draft children at the same time - * - * @return \Kirby\Cms\Pages */ - public function childrenAndDrafts() + public function childrenAndDrafts(): Pages { - if ($this->childrenAndDrafts instanceof Pages) { - return $this->childrenAndDrafts; - } - - return $this->childrenAndDrafts = $this->children()->merge($this->drafts()); - } - - /** - * Returns a list of IDs for the model's - * `toArray` method - * - * @return array - */ - protected function convertChildrenToArray(): array - { - return $this->children()->keys(); + return $this->childrenAndDrafts ??= $this->children()->merge($this->drafts()); } /** * Searches for a draft child by ID - * - * @param string $path - * @return \Kirby\Cms\Page|null */ - public function draft(string $path) + public function draft(string $path): Page|null { $path = str_replace('_drafts/', '', $path); @@ -113,10 +81,8 @@ trait HasChildren /** * Returns all draft children - * - * @return \Kirby\Cms\Pages */ - public function drafts() + public function drafts(): Pages { if ($this->drafts instanceof Pages) { return $this->drafts; @@ -137,40 +103,30 @@ trait HasChildren /** * Finds one or multiple published children by ID - * - * @param string ...$arguments - * @return \Kirby\Cms\Page|\Kirby\Cms\Pages|null */ - public function find(...$arguments) + public function find(string|array ...$arguments): Page|Pages|null { return $this->children()->find(...$arguments); } /** * Finds a single published or draft child - * - * @param string $path - * @return \Kirby\Cms\Page|null */ - public function findPageOrDraft(string $path) + public function findPageOrDraft(string $path): Page|null { return $this->children()->find($path) ?? $this->drafts()->find($path); } /** * Returns a collection of all published children of published children - * - * @return \Kirby\Cms\Pages */ - public function grandChildren() + public function grandChildren(): Pages { return $this->children()->children(); } /** * Checks if the model has any published children - * - * @return bool */ public function hasChildren(): bool { @@ -179,8 +135,6 @@ trait HasChildren /** * Checks if the model has any draft children - * - * @return bool */ public function hasDrafts(): bool { @@ -189,8 +143,6 @@ trait HasChildren /** * Checks if the page has any listed children - * - * @return bool */ public function hasListedChildren(): bool { @@ -199,8 +151,6 @@ trait HasChildren /** * Checks if the page has any unlisted children - * - * @return bool */ public function hasUnlistedChildren(): bool { @@ -211,9 +161,8 @@ trait HasChildren * Creates a flat child index * * @param bool $drafts If set to `true`, draft children are included - * @return \Kirby\Cms\Pages */ - public function index(bool $drafts = false) + public function index(bool $drafts = false): Pages { if ($drafts === true) { return $this->childrenAndDrafts()->index($drafts); @@ -225,10 +174,9 @@ trait HasChildren /** * Sets the published children collection * - * @param array|null $children * @return $this */ - protected function setChildren(array $children = null) + protected function setChildren(array|null $children = null): static { if ($children !== null) { $this->children = Pages::factory($children, $this); @@ -240,10 +188,9 @@ trait HasChildren /** * Sets the draft children collection * - * @param array|null $drafts * @return $this */ - protected function setDrafts(array $drafts = null) + protected function setDrafts(array|null $drafts = null): static { if ($drafts !== null) { $this->drafts = Pages::factory($drafts, $this, true); diff --git a/kirby/src/Cms/HasFiles.php b/kirby/src/Cms/HasFiles.php index 7d87d83..cc6ac65 100644 --- a/kirby/src/Cms/HasFiles.php +++ b/kirby/src/Cms/HasFiles.php @@ -17,50 +17,31 @@ trait HasFiles { /** * The Files collection - * - * @var \Kirby\Cms\Files */ - protected $files; + protected Files|array|null $files = null; /** * Filters the Files collection by type audio - * - * @return \Kirby\Cms\Files */ - public function audio() + public function audio(): Files { return $this->files()->filter('type', '==', 'audio'); } /** * Filters the Files collection by type code - * - * @return \Kirby\Cms\Files */ - public function code() + public function code(): Files { return $this->files()->filter('type', '==', 'code'); } - /** - * Returns a list of file ids - * for the toArray method of the model - * - * @return array - */ - protected function convertFilesToArray(): array - { - return $this->files()->keys(); - } - /** * Creates a new file * - * @param array $props * @param bool $move If set to `true`, the source will be deleted - * @return \Kirby\Cms\File */ - public function createFile(array $props, bool $move = false) + public function createFile(array $props, bool $move = false): File { $props = array_merge($props, [ 'parent' => $this, @@ -72,23 +53,19 @@ trait HasFiles /** * Filters the Files collection by type documents - * - * @return \Kirby\Cms\Files */ - public function documents() + public function documents(): Files { return $this->files()->filter('type', '==', 'document'); } /** * Returns a specific file by filename or the first one - * - * @param string|null $filename - * @param string $in - * @return \Kirby\Cms\File|null */ - public function file(string $filename = null, string $in = 'files') - { + public function file( + string|null $filename = null, + string $in = 'files' + ): File|null { if ($filename === null) { return $this->$in()->first(); } @@ -114,10 +91,8 @@ trait HasFiles /** * Returns the Files collection - * - * @return \Kirby\Cms\Files */ - public function files() + public function files(): Files { if ($this->files instanceof Files) { return $this->files; @@ -128,8 +103,6 @@ trait HasFiles /** * Checks if the Files collection has any audio files - * - * @return bool */ public function hasAudio(): bool { @@ -138,8 +111,6 @@ trait HasFiles /** * Checks if the Files collection has any code files - * - * @return bool */ public function hasCode(): bool { @@ -148,8 +119,6 @@ trait HasFiles /** * Checks if the Files collection has any document files - * - * @return bool */ public function hasDocuments(): bool { @@ -158,8 +127,6 @@ trait HasFiles /** * Checks if the Files collection has any files - * - * @return bool */ public function hasFiles(): bool { @@ -168,8 +135,6 @@ trait HasFiles /** * Checks if the Files collection has any images - * - * @return bool */ public function hasImages(): bool { @@ -178,8 +143,6 @@ trait HasFiles /** * Checks if the Files collection has any videos - * - * @return bool */ public function hasVideos(): bool { @@ -188,21 +151,16 @@ trait HasFiles /** * Returns a specific image by filename or the first one - * - * @param string|null $filename - * @return \Kirby\Cms\File|null */ - public function image(string $filename = null) + public function image(string|null $filename = null): File|null { return $this->file($filename, 'images'); } /** * Filters the Files collection by type image - * - * @return \Kirby\Cms\Files */ - public function images() + public function images(): Files { return $this->files()->filter('type', '==', 'image'); } @@ -210,10 +168,9 @@ trait HasFiles /** * Sets the Files collection * - * @param \Kirby\Cms\Files|null $files * @return $this */ - protected function setFiles(array $files = null) + protected function setFiles(array|null $files = null): static { if ($files !== null) { $this->files = Files::factory($files, $this); @@ -224,10 +181,8 @@ trait HasFiles /** * Filters the Files collection by type videos - * - * @return \Kirby\Cms\Files */ - public function videos() + public function videos(): Files { return $this->files()->filter('type', '==', 'video'); } diff --git a/kirby/src/Cms/HasMethods.php b/kirby/src/Cms/HasMethods.php index 053eb8b..9e08d5b 100644 --- a/kirby/src/Cms/HasMethods.php +++ b/kirby/src/Cms/HasMethods.php @@ -2,6 +2,7 @@ namespace Kirby\Cms; +use Closure; use Kirby\Exception\BadMethodCallException; /** @@ -17,22 +18,17 @@ trait HasMethods { /** * All registered methods - * - * @var array */ - public static $methods = []; + public static array $methods = []; /** * Calls a registered method class with the * passed arguments - * * @internal - * @param string $method - * @param array $args - * @return mixed + * * @throws \Kirby\Exception\BadMethodCallException */ - public function callMethod(string $method, array $args = []) + public function callMethod(string $method, array $args = []): mixed { $closure = $this->getMethod($method); @@ -45,10 +41,7 @@ trait HasMethods /** * Checks if the object has a registered method - * * @internal - * @param string $method - * @return bool */ public function hasMethod(string $method): bool { @@ -59,11 +52,8 @@ trait HasMethods * Returns a registered method by name, either from * the current class or from a parent class ordered by * inheritance order (top to bottom) - * - * @param string $method - * @return \Closure|null */ - protected function getMethod(string $method) + protected function getMethod(string $method): Closure|null { if (isset(static::$methods[$method]) === true) { return static::$methods[$method]; diff --git a/kirby/src/Cms/HasSiblings.php b/kirby/src/Cms/HasSiblings.php index fbe7bad..86e63e9 100644 --- a/kirby/src/Cms/HasSiblings.php +++ b/kirby/src/Cms/HasSiblings.php @@ -18,8 +18,6 @@ trait HasSiblings * Returns the position / index in the collection * * @param \Kirby\Cms\Collection|null $collection - * - * @return int|false */ public function indexOf($collection = null): int|false { @@ -88,7 +86,6 @@ trait HasSiblings /** * Returns all sibling elements * - * @param bool $self * @return \Kirby\Cms\Collection */ public function siblings(bool $self = true) @@ -106,8 +103,6 @@ trait HasSiblings * Checks if there's a next item in the collection * * @param \Kirby\Cms\Collection|null $collection - * - * @return bool */ public function hasNext($collection = null): bool { @@ -118,8 +113,6 @@ trait HasSiblings * Checks if there's a previous item in the collection * * @param \Kirby\Cms\Collection|null $collection - * - * @return bool */ public function hasPrev($collection = null): bool { @@ -130,8 +123,6 @@ trait HasSiblings * Checks if the item is the first in the collection * * @param \Kirby\Cms\Collection|null $collection - * - * @return bool */ public function isFirst($collection = null): bool { @@ -143,8 +134,6 @@ trait HasSiblings * Checks if the item is the last in the collection * * @param \Kirby\Cms\Collection|null $collection - * - * @return bool */ public function isLast($collection = null): bool { @@ -156,9 +145,6 @@ trait HasSiblings * Checks if the item is at a certain position * * @param \Kirby\Cms\Collection|null $collection - * @param int $n - * - * @return bool */ public function isNth(int $n, $collection = null): bool { diff --git a/kirby/src/Cms/Helpers.php b/kirby/src/Cms/Helpers.php index ae6c51f..9e27f42 100644 --- a/kirby/src/Cms/Helpers.php +++ b/kirby/src/Cms/Helpers.php @@ -29,20 +29,20 @@ class Helpers * ``` */ public static $deprecations = [ - // Passing the $slot or $slots variables to snippets is - // deprecated and will break in a future version. - 'snippet-pass-slots' => true, + // The internal `$model->contentFile*()` methods have been deprecated + 'model-content-file' => true, - // The `Toolkit\Query` class has been deprecated and will - // be removed in a future version. Use `Query\Query` instead: - // Kirby\Query\Query::factory($query)->resolve($data). - 'toolkit-query-class' => true, + // Passing an `info` array inside the `extends` array + // has been deprecated. Pass the individual entries (e.g. root, version) + // directly as named arguments. + // TODO: switch to true in v6 + 'plugin-extends-root' => false, - // Passing an empty string as value to `Xml::attr()` has been - // deprecated. In a future version, passing an empty string won't - // omit the attribute anymore but render it with an empty value. - // To omit the attribute, please pass `null`. - 'xml-attr-empty-string' => false, + // Passing a single space as value to `Xml::attr()` has been + // deprecated. In a future version, passing a single space won't + // render an empty value anymore but a single space. + // To render an empty value, please pass an empty string. + 'xml-attr-single-space' => true, ]; /** @@ -52,8 +52,10 @@ class Helpers * @param string|null $key If given, the key will be checked against the static array * @return bool Whether the warning was triggered */ - public static function deprecated(string $message, string|null $key = null): bool - { + public static function deprecated( + string $message, + string|null $key = null + ): bool { // only trigger warning in debug mode or when running PHPUnit tests // @codeCoverageIgnoreStart if ( @@ -75,19 +77,16 @@ class Helpers /** * Simple object and variable dumper * to help with debugging. - * - * @param mixed $variable - * @param bool $echo - * @return string */ - public static function dump($variable, bool $echo = true): string + public static function dump(mixed $variable, bool $echo = true): string { - $kirby = App::instance(); + $kirby = App::instance(); + $output = print_r($variable, true); if ($kirby->environment()->cli() === true) { - $output = print_r($variable, true) . PHP_EOL; + $output .= PHP_EOL; } else { - $output = '
      ' . print_r($variable, true) . '
      '; + $output = Str::wrap($output, '
      ', '
      '); } if ($echo === true) { @@ -110,10 +109,16 @@ class Helpers * @return mixed Return value of the `$action` closure, * possibly overridden by `$fallback` */ - public static function handleErrors(Closure $action, Closure $condition, $fallback = null) - { + public static function handleErrors( + Closure $action, + Closure $condition, + $fallback = null + ) { $override = null; + /** + * @psalm-suppress UndefinedVariable + */ $handler = set_error_handler(function () use (&$override, &$handler, $condition, $fallback) { // check if suppress condition is met $suppress = $condition(...func_get_args()); @@ -139,9 +144,14 @@ class Helpers return true; }); - $result = $action(); - - restore_error_handler(); + try { + $result = $action(); + } finally { + // always restore the error handler, even if the + // action or the standard error handler threw an + // exception; this avoids modifying global state + restore_error_handler(); + } return $override ?? $result; } @@ -152,7 +162,6 @@ class Helpers * @internal * * @param string $name Name of the helper - * @return bool */ public static function hasOverride(string $name): bool { @@ -164,11 +173,9 @@ class Helpers * Determines the size/length of numbers, * strings, arrays and countable objects * - * @param mixed $value - * @return int * @throws \Kirby\Exception\InvalidArgumentException */ - public static function size($value): int + public static function size(mixed $value): int { if (is_numeric($value)) { return (int)$value; diff --git a/kirby/src/Cms/Html.php b/kirby/src/Cms/Html.php index f8f3388..dfe8101 100644 --- a/kirby/src/Cms/Html.php +++ b/kirby/src/Cms/Html.php @@ -23,11 +23,20 @@ class Html extends \Kirby\Toolkit\Html * @since 3.7.0 * * @param string|array $url Relative or absolute URLs, an array of URLs or `@auto` for automatic template css loading - * @param string|array $options Pass an array of attributes for the link tag or a media attribute string - * @return string|null + * @param string|array|null $options Pass an array of attributes for the link tag or a media attribute string */ - public static function css($url, $options = null): string|null - { + public static function css( + string|array|Plugin|PluginAssets $url, + string|array|null $options = null + ): string|null { + if ($url instanceof Plugin) { + $url = $url->assets(); + } + + if ($url instanceof PluginAssets) { + $url = $url->css()->values(fn ($asset) => $asset->url()); + } + if (is_array($url) === true) { $links = A::map($url, fn ($url) => static::css($url, $options)); return implode(PHP_EOL, $links); @@ -69,23 +78,31 @@ class Html extends \Kirby\Toolkit\Html * @param string|null $href Relative or absolute Url * @param string|array|null $text If `null`, the link will be used as link text. If an array is passed, each element will be added unencoded * @param array $attr Additional attributes for the a tag. - * @return string */ - public static function link(string $href = null, $text = null, array $attr = []): string - { + public static function link( + string|null $href = null, + string|array|null $text = null, + array $attr = [] + ): string { return parent::link(Url::to($href), $text, $attr); } /** * Creates a script tag to load a javascript file * @since 3.7.0 - * - * @param string|array $url - * @param string|array $options - * @return string|null */ - public static function js($url, $options = null): string|null - { + public static function js( + string|array|Plugin|PluginAssets $url, + string|array|bool|null $options = null + ): string|null { + if ($url instanceof Plugin) { + $url = $url->assets(); + } + + if ($url instanceof PluginAssets) { + $url = $url->js()->values(fn ($asset) => $asset->url()); + } + if (is_array($url) === true) { $scripts = A::map($url, fn ($url) => static::js($url, $options)); return implode(PHP_EOL, $scripts); @@ -114,11 +131,8 @@ class Html extends \Kirby\Toolkit\Html * Includes an SVG file by absolute or * relative file path. * @since 3.7.0 - * - * @param string|\Kirby\Cms\File $file - * @return string|false */ - public static function svg($file) + public static function svg(string|File $file): string|false { // support for Kirby's file objects if ( diff --git a/kirby/src/Cms/Ingredients.php b/kirby/src/Cms/Ingredients.php index ab84861..e8f4b33 100644 --- a/kirby/src/Cms/Ingredients.php +++ b/kirby/src/Cms/Ingredients.php @@ -25,8 +25,6 @@ class Ingredients /** * Creates a new ingredient collection - * - * @param array $ingredients */ public function __construct(array $ingredients) { @@ -35,20 +33,15 @@ class Ingredients /** * Magic getter for single ingredients - * - * @param string $method - * @param array|null $args - * @return mixed */ - public function __call(string $method, array $args = null) + public function __call(string $method, array|null $args = null): mixed { return $this->ingredients[$method] ?? null; } /** * Improved `var_dump` output - * - * @return array + * @codeCoverageIgnore */ public function __debugInfo(): array { @@ -57,9 +50,6 @@ class Ingredients /** * Get a single ingredient by key - * - * @param string $key - * @return mixed */ public function __get(string $key) { @@ -69,12 +59,9 @@ class Ingredients /** * Resolves all ingredient callbacks * and creates a plain array - * * @internal - * @param array $ingredients - * @return static */ - public static function bake(array $ingredients) + public static function bake(array $ingredients): static { foreach ($ingredients as $name => $ingredient) { if ($ingredient instanceof Closure) { @@ -87,8 +74,6 @@ class Ingredients /** * Returns all ingredients as plain array - * - * @return array */ public function toArray(): array { diff --git a/kirby/src/Cms/Item.php b/kirby/src/Cms/Item.php index b7e3d3f..86dd85a 100644 --- a/kirby/src/Cms/Item.php +++ b/kirby/src/Cms/Item.php @@ -2,6 +2,7 @@ namespace Kirby\Cms; +use Kirby\Content\Field; use Kirby\Toolkit\Str; /** @@ -28,49 +29,28 @@ class Item protected Field|null $field; - /** - * @var string - */ - protected $id; - - /** - * @var array - */ - protected $params; - - /** - * @var \Kirby\Cms\Page|\Kirby\Cms\Site|\Kirby\Cms\File|\Kirby\Cms\User - */ - protected $parent; - - /** - * @var \Kirby\Cms\Items - */ - protected $siblings; + protected string $id; + protected array $params; + protected ModelWithContent $parent; + protected Items $siblings; /** * Creates a new item - * - * @param array $params */ public function __construct(array $params = []) { - $siblingsClass = static::ITEMS_CLASS; - + $class = static::ITEMS_CLASS; $this->id = $params['id'] ?? Str::uuid(); $this->params = $params; $this->field = $params['field'] ?? null; $this->parent = $params['parent'] ?? App::instance()->site(); - $this->siblings = $params['siblings'] ?? new $siblingsClass(); + $this->siblings = $params['siblings'] ?? new $class(); } /** * Static Item factory - * - * @param array $params - * @return \Kirby\Cms\Item */ - public static function factory(array $params) + public static function factory(array $params): static { return new static($params); } @@ -85,8 +65,6 @@ class Item /** * Returns the unique item id (UUID v4) - * - * @return string */ public function id(): string { @@ -95,9 +73,6 @@ class Item /** * Compares the item to another one - * - * @param \Kirby\Cms\Item $item - * @return bool */ public function is(Item $item): bool { @@ -106,20 +81,16 @@ class Item /** * Returns the Kirby instance - * - * @return \Kirby\Cms\App */ - public function kirby() + public function kirby(): App { return $this->parent()->kirby(); } /** * Returns the parent model - * - * @return \Kirby\Cms\Page|\Kirby\Cms\Site|\Kirby\Cms\File|\Kirby\Cms\User */ - public function parent() + public function parent(): ModelWithContent { return $this->parent; } @@ -128,18 +99,15 @@ class Item * Returns the sibling collection * This is required by the HasSiblings trait * - * @return \Kirby\Cms\Items * @psalm-return self::ITEMS_CLASS */ - protected function siblingsCollection() + protected function siblingsCollection(): Items { return $this->siblings; } /** * Converts the item to an array - * - * @return array */ public function toArray(): array { diff --git a/kirby/src/Cms/Items.php b/kirby/src/Cms/Items.php index 7910e2f..13cc966 100644 --- a/kirby/src/Cms/Items.php +++ b/kirby/src/Cms/Items.php @@ -3,7 +3,8 @@ namespace Kirby\Cms; use Closure; -use Exception; +use Kirby\Content\Field; +use Kirby\Exception\InvalidArgumentException; /** * A collection of items @@ -23,27 +24,16 @@ class Items extends Collection /** * All registered items methods - * - * @var array */ - public static $methods = []; + public static array $methods = []; - /** - * @var array - */ - protected $options; + protected array $options; /** * @var \Kirby\Cms\ModelWithContent */ protected $parent; - /** - * Constructor - * - * @param array $objects - * @param array $options - */ public function __construct($objects = [], array $options = []) { $this->options = $options; @@ -56,41 +46,36 @@ class Items extends Collection /** * Creates a new item collection from a * an array of item props - * - * @param array $items - * @param array $params - * @return \Kirby\Cms\Items */ - public static function factory(array $items = null, array $params = []) - { - $options = array_merge([ - 'field' => null, - 'options' => [], - 'parent' => App::instance()->site(), - ], $params); - + public static function factory( + array|null $items = null, + array $params = [] + ): static { if (empty($items) === true || is_array($items) === false) { return new static(); } - if (is_array($options) === false) { - throw new Exception('Invalid item options'); + if (is_array($params) === false) { + throw new InvalidArgumentException('Invalid item options'); } // create a new collection of blocks - $collection = new static([], $options); + $collection = new static([], $params); - foreach ($items as $params) { - if (is_array($params) === false) { - continue; + foreach ($items as $item) { + if (is_array($item) === false) { + throw new InvalidArgumentException('Invalid data for ' . static::ITEM_CLASS); } - $params['field'] = $options['field']; - $params['options'] = $options['options']; - $params['parent'] = $options['parent']; - $params['siblings'] = $collection; + // inject properties from the parent + $item['field'] = $collection->field(); + $item['options'] = $params['options'] ?? []; + $item['parent'] = $collection->parent(); + $item['siblings'] = $collection; + $item['params'] = $item; + $class = static::ITEM_CLASS; - $item = $class::factory($params); + $item = $class::factory($item); $collection->append($item->id(), $item); } @@ -107,10 +92,8 @@ class Items extends Collection /** * Convert the items to an array - * - * @return array */ - public function toArray(Closure $map = null): array + public function toArray(Closure|null $map = null): array { return array_values(parent::toArray($map)); } diff --git a/kirby/src/Cms/Language.php b/kirby/src/Cms/Language.php index 6a53d25..4c6b491 100644 --- a/kirby/src/Cms/Language.php +++ b/kirby/src/Cms/Language.php @@ -4,6 +4,8 @@ namespace Kirby\Cms; use Kirby\Data\Data; use Kirby\Exception\Exception; +use Kirby\Exception\InvalidArgumentException; +use Kirby\Exception\LogicException; use Kirby\Exception\PermissionException; use Kirby\Filesystem\F; use Kirby\Toolkit\Locale; @@ -26,80 +28,54 @@ use Throwable; * @copyright Bastian Allgeier * @license https://getkirby.com/license */ -class Language extends Model +class Language { - /** - * @var string - */ - protected $code; + use HasSiblings; /** - * @var bool + * The parent Kirby instance */ - protected $default; + public static App|null $kirby; - /** - * @var string - */ - protected $direction; - - /** - * @var array - */ - protected $locale; - - /** - * @var string - */ - protected $name; - - /** - * @var array|null - */ - protected $slugs; - - /** - * @var array|null - */ - protected $smartypants; - - /** - * @var array|null - */ - protected $translations; - - /** - * @var string - */ - protected $url; + protected string $code; + protected bool $default; + protected string $direction; + protected array $locale; + protected string $name; + protected array $slugs; + protected array $smartypants; + protected array $translations; + protected string|null $url; /** * Creates a new language object - * - * @param array $props */ public function __construct(array $props) { - $this->setRequiredProperties($props, [ - 'code' - ]); + if (isset($props['code']) === false) { + throw new InvalidArgumentException('The property "code" is required'); + } - $this->setOptionalProperties($props, [ - 'default', - 'direction', - 'locale', - 'name', - 'slugs', - 'smartypants', - 'translations', - 'url', - ]); + static::$kirby = $props['kirby'] ?? null; + $this->code = trim($props['code']); + $this->default = ($props['default'] ?? false) === true; + $this->direction = ($props['direction'] ?? null) === 'rtl' ? 'rtl' : 'ltr'; + $this->name = trim($props['name'] ?? $this->code); + $this->slugs = $props['slugs'] ?? []; + $this->smartypants = $props['smartypants'] ?? []; + $this->translations = $props['translations'] ?? []; + $this->url = $props['url'] ?? null; + + if ($locale = $props['locale'] ?? null) { + $this->locale = Locale::normalize($locale); + } else { + $this->locale = [LC_ALL => $this->code]; + } } /** * Improved `var_dump` output - * - * @return array + * @codeCoverageIgnore */ public function __debugInfo(): array { @@ -109,8 +85,6 @@ class Language extends Model /** * Returns the language code * when the language is converted to a string - * - * @return string */ public function __toString(): string { @@ -120,8 +94,6 @@ class Language extends Model /** * Returns the base Url for the language * without the path or other cruft - * - * @return string */ public function baseUrl(): string { @@ -139,67 +111,40 @@ class Language extends Model return Url::base($languageUrl) ?? $kirbyUrl; } + /** + * Creates an instance with the same + * initial properties. + */ + public function clone(array $props = []): static + { + return new static(array_replace_recursive([ + 'code' => $this->code, + 'default' => $this->default, + 'direction' => $this->direction, + 'locale' => $this->locale, + 'name' => $this->name, + 'slugs' => $this->slugs, + 'smartypants' => $this->smartypants, + 'translations' => $this->translations, + 'url' => $this->url, + ], $props)); + } + /** * Returns the language code/id. * The language code is used in * text file names as appendix. - * - * @return string */ public function code(): string { return $this->code; } - /** - * Internal converter to create or remove - * translation files. - * - * @param string $from - * @param string $to - * @return bool - */ - protected static function converter(string $from, string $to): bool - { - $kirby = App::instance(); - $site = $kirby->site(); - - // convert site - foreach ($site->files() as $file) { - F::move($file->contentFile($from, true), $file->contentFile($to, true)); - } - - F::move($site->contentFile($from, true), $site->contentFile($to, true)); - - // convert all pages - foreach ($kirby->site()->index(true) as $page) { - foreach ($page->files() as $file) { - F::move($file->contentFile($from, true), $file->contentFile($to, true)); - } - - F::move($page->contentFile($from, true), $page->contentFile($to, true)); - } - - // convert all users - foreach ($kirby->users() as $user) { - foreach ($user->files() as $file) { - F::move($file->contentFile($from, true), $file->contentFile($to, true)); - } - - F::move($user->contentFile($from, true), $user->contentFile($to, true)); - } - - return true; - } - /** * Creates a new language object - * * @internal - * @param array $props - * @return static */ - public static function create(array $props) + public static function create(array $props): static { $kirby = App::instance(); $user = $kirby->user(); @@ -236,7 +181,12 @@ class Language extends Model $language->save(); if ($languages->count() === 0) { - static::converter('', $language->code()); + foreach ($kirby->models() as $model) { + $model->storage()->convertLanguage( + 'default', + $language->code() + ); + } } // update the main languages collection in the app instance @@ -257,18 +207,15 @@ class Language extends Model /** * Delete the current language and * all its translation files - * * @internal - * @return bool + * * @throws \Kirby\Exception\Exception */ public function delete(): bool { - $kirby = App::instance(); - $user = $kirby->user(); - $languages = $kirby->languages(); - $code = $this->code(); - $isLast = $languages->count() === 1; + $kirby = App::instance(); + $user = $kirby->user(); + $code = $this->code(); if ( $user === null || @@ -277,6 +224,10 @@ class Language extends Model throw new PermissionException(['key' => 'language.delete.permission']); } + if ($this->isDeletable() === false) { + throw new Exception('The language cannot be deleted'); + } + // trigger before hook $kirby->trigger('language.delete:before', [ 'language' => $this @@ -286,10 +237,12 @@ class Language extends Model throw new Exception('The language could not be deleted'); } - if ($isLast === true) { - $this->converter($code, ''); - } else { - $this->deleteContentFiles($code); + foreach ($kirby->models() as $model) { + if ($this->isLast() === true) { + $model->storage()->convertLanguage($code, 'default'); + } else { + $model->storage()->deleteLanguage($code); + } } // get the original language collection and remove the current language @@ -303,43 +256,8 @@ class Language extends Model return true; } - /** - * When the language is deleted, all content files with - * the language code must be removed as well. - * - * @param mixed $code - * @return bool - */ - protected function deleteContentFiles($code): bool - { - $kirby = App::instance(); - $site = $kirby->site(); - - F::remove($site->contentFile($code, true)); - - foreach ($kirby->site()->index(true) as $page) { - foreach ($page->files() as $file) { - F::remove($file->contentFile($code, true)); - } - - F::remove($page->contentFile($code, true)); - } - - foreach ($kirby->users() as $user) { - foreach ($user->files() as $file) { - F::remove($file->contentFile($code, true)); - } - - F::remove($user->contentFile($code, true)); - } - - return true; - } - /** * Reading direction of this language - * - * @return string */ public function direction(): string { @@ -348,8 +266,6 @@ class Language extends Model /** * Check if the language file exists - * - * @return bool */ public function exists(): bool { @@ -359,19 +275,36 @@ class Language extends Model /** * Checks if this is the default language * for the site. - * - * @return bool */ public function isDefault(): bool { return $this->default; } + /** + * Checks if the language can be deleted + */ + public function isDeletable(): bool + { + // the default language can only be deleted if it's the last + if ($this->isDefault() === true && $this->isLast() === false) { + return false; + } + + return true; + } + + /** + * Checks if this is the last language + */ + public function isLast(): bool + { + return App::instance()->languages()->count() === 1; + } + /** * The id is required for collections * to work properly. The code is used as id - * - * @return string */ public function id(): string { @@ -379,11 +312,17 @@ class Language extends Model } /** - * Loads the language rules for provided locale code - * - * @param string $code + * Returns the parent Kirby instance */ - public static function loadRules(string $code) + public function kirby(): App + { + return static::$kirby ??= App::instance(); + } + + /** + * Loads the language rules for provided locale code + */ + public static function loadRules(string $code): array { $kirby = App::instance(); $code = Str::contains($code, '.') ? Str::before($code, '.') : $code; @@ -404,9 +343,8 @@ class Language extends Model * Returns the PHP locale setting array * * @param int $category If passed, returns the locale for the specified category (e.g. LC_ALL) as string - * @return array|string */ - public function locale(int $category = null) + public function locale(int|null $category = null): array|string|null { if ($category !== null) { return $this->locale[$category] ?? $this->locale[LC_ALL] ?? null; @@ -418,8 +356,6 @@ class Language extends Model /** * Returns the human-readable name * of the language - * - * @return string */ public function name(): string { @@ -428,8 +364,6 @@ class Language extends Model /** * Returns the URL path for the language - * - * @return string */ public function path(): string { @@ -442,8 +376,6 @@ class Language extends Model /** * Returns the routing pattern for the language - * - * @return string */ public function pattern(): string { @@ -458,8 +390,6 @@ class Language extends Model /** * Returns the absolute path to the language file - * - * @return string */ public function root(): string { @@ -470,19 +400,15 @@ class Language extends Model * Returns the LanguageRouter instance * which is used to handle language specific * routes. - * - * @return \Kirby\Cms\LanguageRouter */ - public function router() + public function router(): LanguageRouter { return new LanguageRouter($this); } /** * Get slug rules for language - * * @internal - * @return array */ public function rules(): array { @@ -493,11 +419,11 @@ class Language extends Model /** * Saves the language settings in the languages folder - * * @internal + * * @return $this */ - public function save() + public function save(): static { try { $existingData = Data::read($this->root()); @@ -525,104 +451,15 @@ class Language extends Model } /** - * @param string $code - * @return $this + * Private siblings collector */ - protected function setCode(string $code) + protected function siblingsCollection(): Collection { - $this->code = trim($code); - return $this; - } - - /** - * @param bool $default - * @return $this - */ - protected function setDefault(bool $default = false) - { - $this->default = $default; - return $this; - } - - /** - * @param string $direction - * @return $this - */ - protected function setDirection(string $direction = 'ltr') - { - $this->direction = $direction === 'rtl' ? 'rtl' : 'ltr'; - return $this; - } - - /** - * @param string|array $locale - * @return $this - */ - protected function setLocale($locale = null) - { - if ($locale === null) { - $this->locale = [LC_ALL => $this->code]; - } else { - $this->locale = Locale::normalize($locale); - } - - return $this; - } - - /** - * @param string $name - * @return $this - */ - protected function setName(string $name = null) - { - $this->name = trim($name ?? $this->code); - return $this; - } - - /** - * @param array $slugs - * @return $this - */ - protected function setSlugs(array $slugs = null) - { - $this->slugs = $slugs ?? []; - return $this; - } - - /** - * @param array $smartypants - * @return $this - */ - protected function setSmartypants(array $smartypants = null) - { - $this->smartypants = $smartypants ?? []; - return $this; - } - - /** - * @param array $translations - * @return $this - */ - protected function setTranslations(array $translations = null) - { - $this->translations = $translations ?? []; - return $this; - } - - /** - * @param string $url - * @return $this - */ - protected function setUrl(string $url = null) - { - $this->url = $url; - return $this; + return App::instance()->languages(); } /** * Returns the custom slug rules for this language - * - * @return array */ public function slugs(): array { @@ -631,8 +468,6 @@ class Language extends Model /** * Returns the custom SmartyPants options for this language - * - * @return array */ public function smartypants(): array { @@ -642,8 +477,6 @@ class Language extends Model /** * Returns the most important * properties as array - * - * @return array */ public function toArray(): array { @@ -660,8 +493,6 @@ class Language extends Model /** * Returns the translation strings for this language - * - * @return array */ public function translations(): array { @@ -670,8 +501,6 @@ class Language extends Model /** * Returns the absolute Url for the language - * - * @return string */ public function url(): string { @@ -682,12 +511,9 @@ class Language extends Model /** * Update language properties and save them - * * @internal - * @param array $props - * @return static */ - public function update(array $props = null) + public function update(array|null $props = null): static { $kirby = App::instance(); $user = $kirby->user(); @@ -707,6 +533,10 @@ class Language extends Model $updated = $this->clone($props); + if (isset($props['translations']) === true) { + $updated->translations = $props['translations']; + } + // validate the updated language LanguageRules::update($updated); @@ -716,32 +546,31 @@ class Language extends Model 'input' => $props ]); - // convert the current default to a non-default language - if ($updated->isDefault() === true) { - $kirby->defaultLanguage()?->clone(['default' => false])->save(); + // if language just got promoted to be the new default language… + if ($this->isDefault() === false && $updated->isDefault() === true) { + // convert the current default to a non-default language + $previous = $kirby->defaultLanguage()?->clone(['default' => false])->save(); + $kirby->languages(false)->set($previous->code(), $previous); - $code = $this->code(); - $site = $kirby->site(); - - touch($site->contentFile($code)); - - foreach ($kirby->site()->index(true) as $page) { - $files = $page->files(); - - foreach ($files as $file) { - touch($file->contentFile($code)); - } - - touch($page->contentFile($code)); + foreach ($kirby->models() as $model) { + $model->storage()->touchLanguage($this); } - } elseif ($this->isDefault() === true) { - throw new PermissionException('Please select another language to be the primary language'); + } + + // if language was the default language and got demoted… + if ( + $this->isDefault() === true && + $updated->isDefault() === false && + $kirby->defaultLanguage()->code() === $this->code() + ) { + // ensure another language has already been set as default + throw new LogicException('Please select another language to be the primary language'); } $language = $updated->save(); - // make sure the language is also updated in the Kirby language collection - App::instance()->languages(false)->set($language->code(), $language); + // make sure the language is also updated in the languages collection + $kirby->languages(false)->set($language->code(), $language); // trigger after hook $kirby->trigger('language.update:after', [ @@ -752,4 +581,19 @@ class Language extends Model return $language; } + + /** + * Returns a language variable object + * for the key in the translations array + */ + public function variable(string $key, bool $decode = false): LanguageVariable + { + // allows decoding if base64-url encoded url is sent + // for compatibility of different environments + if ($decode === true) { + $key = rawurldecode(base64_decode($key)); + } + + return new LanguageVariable($this, $key); + } } diff --git a/kirby/src/Cms/LanguageRouter.php b/kirby/src/Cms/LanguageRouter.php index 60d3623..13ab2d1 100644 --- a/kirby/src/Cms/LanguageRouter.php +++ b/kirby/src/Cms/LanguageRouter.php @@ -7,6 +7,7 @@ use Kirby\Exception\NotFoundException; use Kirby\Http\Router; use Kirby\Toolkit\A; use Kirby\Toolkit\Str; +use Kirby\Uuid\Uuid; /** * The language router is used internally @@ -20,36 +21,21 @@ use Kirby\Toolkit\Str; */ class LanguageRouter { - /** - * The parent language - * - * @var Language - */ - protected $language; - - /** - * The router instance - * - * @var Router - */ - protected $router; + protected Router $router; /** * Creates a new language router instance * for the given language - * - * @param \Kirby\Cms\Language $language */ - public function __construct(Language $language) - { - $this->language = $language; + public function __construct( + protected Language $language + ) { } /** * Fetches all scoped routes for the * current language from the Kirby instance * - * @return array * @throws \Kirby\Exception\NotFoundException */ public function routes(): array @@ -99,6 +85,27 @@ class LanguageRouter } } + // Language-specific UUID URLs + $routes[] = [ + 'pattern' => '@/(page|file)/(:all)', + 'method' => 'ALL', + 'env' => 'site', + 'action' => function (string $languageCode, string $type, string $id) use ($kirby, $language) { + // try to resolve to model, but only from UUID cache; + // this ensures that only existing UUIDs can be queried + // and attackers can't force Kirby to go through the whole + // site index with a non-existing UUID + if ($model = Uuid::for($type . '://' . $id)?->model(true)) { + return $kirby + ->response() + ->redirect($model->url($language->code())); + } + + // render the error page + return false; + } + ]; + return $routes; } @@ -106,26 +113,32 @@ class LanguageRouter * Wrapper around the Router::call method * that injects the Language instance and * if needed also the Page as arguments. - * - * @param string|null $path - * @return mixed */ - public function call(string $path = null) + public function call(string|null $path = null): mixed { - $language = $this->language; - $kirby = $language->kirby(); - $router = new Router($this->routes()); + $language = $this->language; + $kirby = $language->kirby(); + $this->router ??= new Router($this->routes()); try { - return $router->call($path, $kirby->request()->method(), function ($route) use ($kirby, $language) { + return $this->router->call($path, $kirby->request()->method(), function ($route) use ($kirby, $language) { $kirby->setCurrentTranslation($language); $kirby->setCurrentLanguage($language); if ($page = $route->page()) { - return $route->action()->call($route, $language, $page, ...$route->arguments()); + return $route->action()->call( + $route, + $language, + $page, + ...$route->arguments() + ); } - return $route->action()->call($route, $language, ...$route->arguments()); + return $route->action()->call( + $route, + $language, + ...$route->arguments() + ); }); } catch (Exception) { return $kirby->resolve($path, $language->code()); diff --git a/kirby/src/Cms/LanguageRoutes.php b/kirby/src/Cms/LanguageRoutes.php index cfce0ca..68bdf42 100644 --- a/kirby/src/Cms/LanguageRoutes.php +++ b/kirby/src/Cms/LanguageRoutes.php @@ -8,9 +8,6 @@ class LanguageRoutes { /** * Creates all multi-language routes - * - * @param \Kirby\Cms\App $kirby - * @return array */ public static function create(App $kirby): array { @@ -58,9 +55,6 @@ class LanguageRoutes /** * Create the fallback route * for unprefixed default language URLs. - * - * @param \Kirby\Cms\App $kirby - * @return array */ public static function fallback(App $kirby): array { @@ -73,7 +67,10 @@ class LanguageRoutes $extension = F::extension($path); // try to redirect prefixed pages - if (empty($extension) === true && $page = $kirby->page($path)) { + if ( + empty($extension) === true && + $page = $kirby->page($path) + ) { $url = $kirby->request()->url([ 'query' => null, 'params' => null, @@ -81,15 +78,17 @@ class LanguageRoutes ]); if ($url->toString() !== $page->url()) { - // redirect to translated page directly - // if translation is exists and languages detect is enabled + // redirect to translated page directly if translation + // is exists and languages detect is enabled + $lang = $kirby->detectedLanguage()->code(); + if ( $kirby->option('languages.detect') === true && - $page->translation($kirby->detectedLanguage()->code())->exists() === true + $page->translation($lang)->exists() === true ) { return $kirby ->response() - ->redirect($page->url($kirby->detectedLanguage()->code())); + ->redirect($page->url($lang)); } return $kirby @@ -105,9 +104,6 @@ class LanguageRoutes /** * Create the multi-language home page route - * - * @param \Kirby\Cms\App $kirby - * @return array */ public static function home(App $kirby): array { @@ -118,7 +114,10 @@ class LanguageRoutes 'env' => 'site', 'action' => function () use ($kirby) { // find all languages with the same base url as the current installation - $languages = $kirby->languages()->filter('baseurl', $kirby->url()); + $languages = $kirby->languages()->filter( + 'baseurl', + $kirby->url() + ); // if there's no language with a matching base url, // redirect to the default language @@ -128,7 +127,8 @@ class LanguageRoutes ->redirect($kirby->defaultLanguage()->url()); } - // if there's just one language, we take that to render the home page + // if there's just one language, + // we take that to render the home page if ($languages->count() === 1) { $currentLanguage = $languages->first(); } else { diff --git a/kirby/src/Cms/LanguageRules.php b/kirby/src/Cms/LanguageRules.php index 31c751e..a59a678 100644 --- a/kirby/src/Cms/LanguageRules.php +++ b/kirby/src/Cms/LanguageRules.php @@ -20,8 +20,6 @@ class LanguageRules /** * Validates if the language can be created * - * @param \Kirby\Cms\Language $language - * @return bool * @throws \Kirby\Exception\DuplicateException If the language already exists */ public static function create(Language $language): bool @@ -43,10 +41,8 @@ class LanguageRules /** * Validates if the language can be updated - * - * @param \Kirby\Cms\Language $language */ - public static function update(Language $language) + public static function update(Language $language): void { static::validLanguageCode($language); static::validLanguageName($language); @@ -55,8 +51,6 @@ class LanguageRules /** * Validates if the language code is formatted correctly * - * @param \Kirby\Cms\Language $language - * @return bool * @throws \Kirby\Exception\InvalidArgumentException If the language code is not valid */ public static function validLanguageCode(Language $language): bool @@ -77,8 +71,6 @@ class LanguageRules /** * Validates if the language name is formatted correctly * - * @param \Kirby\Cms\Language $language - * @return bool * @throws \Kirby\Exception\InvalidArgumentException If the language name is invalid */ public static function validLanguageName(Language $language): bool diff --git a/kirby/src/Cms/LanguageVariable.php b/kirby/src/Cms/LanguageVariable.php new file mode 100644 index 0000000..7ca846b --- /dev/null +++ b/kirby/src/Cms/LanguageVariable.php @@ -0,0 +1,122 @@ + + * @link https://getkirby.com + * @copyright Bastian Allgeier + * @license https://getkirby.com/license + */ +class LanguageVariable +{ + protected App $kirby; + + public function __construct( + protected Language $language, + protected string $key + ) { + $this->kirby = App::instance(); + } + + /** + * Creates a new language variable. This will + * be added to the default language first and + * can then be translated in other languages. + */ + public static function create( + string $key, + string|null $value = null + ): static { + if (is_numeric($key) === true) { + throw new InvalidArgumentException('The variable key must not be numeric'); + } + + if (empty($key) === true) { + throw new InvalidArgumentException('The variable needs a valid key'); + } + + $kirby = App::instance(); + $language = $kirby->defaultLanguage(); + $translations = $language->translations(); + + if ($kirby->translation()->get($key) !== null) { + if (isset($translations[$key]) === true) { + throw new DuplicateException('The variable already exists'); + } + + throw new DuplicateException('The variable is part of the core translation and cannot be overwritten'); + } + + $translations[$key] = $value ?? ''; + + $language->update(['translations' => $translations]); + + return $language->variable($key); + } + + /** + * Deletes a language variable from the translations array. + * This will go through all language files and delete the + * key from all translation arrays to keep them clean. + */ + public function delete(): bool + { + // go through all languages and remove the variable + foreach ($this->kirby->languages() as $language) { + $variables = $language->translations(); + + unset($variables[$this->key]); + + $language->update(['translations' => $variables]); + } + + return true; + } + + /** + * Checks if a language variable exists in the default language + */ + public function exists(): bool + { + $language = $this->kirby->defaultLanguage(); + return isset($language->translations()[$this->key]) === true; + } + + /** + * Returns the unique key for the variable + */ + public function key(): string + { + return $this->key; + } + + /** + * Sets a new value for the language variable + */ + public function update(string|null $value = null): static + { + $translations = $this->language->translations(); + $translations[$this->key] = $value ?? ''; + + $language = $this->language->update(['translations' => $translations]); + + return $language->variable($this->key); + } + + /** + * Returns the value if the variable has been translated. + */ + public function value(): string|null + { + return $this->language->translations()[$this->key] ?? null; + } +} diff --git a/kirby/src/Cms/Languages.php b/kirby/src/Cms/Languages.php index 7143340..ba631b9 100644 --- a/kirby/src/Cms/Languages.php +++ b/kirby/src/Cms/Languages.php @@ -18,20 +18,19 @@ class Languages extends Collection { /** * All registered languages methods - * - * @var array */ - public static $methods = []; + public static array $methods = []; /** * Creates a new collection with the given language objects * - * @param array $objects `Kirby\Cms\Language` objects * @param null $parent * @throws \Kirby\Exception\DuplicateException */ - public function __construct($objects = [], $parent = null) - { + public function __construct( + array $objects = [], + $parent = null + ) { $defaults = array_filter( $objects, fn ($language) => $language->isDefault() === true @@ -41,48 +40,39 @@ class Languages extends Collection throw new DuplicateException('You cannot have multiple default languages. Please check your language config files.'); } - parent::__construct($objects, $parent); + parent::__construct($objects, null); } /** * Returns all language codes as array - * - * @return array */ public function codes(): array { - return $this->keys(); + return App::instance()->multilang() ? $this->keys() : ['default']; } /** * Creates a new language with the given props - * * @internal - * @param array $props - * @return \Kirby\Cms\Language */ - public function create(array $props) + public function create(array $props): Language { return Language::create($props); } /** * Returns the default language - * - * @return \Kirby\Cms\Language|null */ - public function default() + public function default(): Language|null { return $this->findBy('isDefault', true) ?? $this->first(); } /** * Convert all defined languages to a collection - * * @internal - * @return static */ - public static function load() + public static function load(): static { $languages = []; $files = glob(App::instance()->root('languages') . '/*.php'); diff --git a/kirby/src/Cms/Layout.php b/kirby/src/Cms/Layout.php index 2aaa960..c6920fd 100644 --- a/kirby/src/Cms/Layout.php +++ b/kirby/src/Cms/Layout.php @@ -2,6 +2,8 @@ namespace Kirby\Cms; +use Kirby\Content\Content; + /** * Represents a single Layout with * multiple columns @@ -19,24 +21,13 @@ class Layout extends Item public const ITEMS_CLASS = Layouts::class; - /** - * @var \Kirby\Cms\Content - */ - protected $attrs; - - /** - * @var \Kirby\Cms\LayoutColumns - */ - protected $columns; + protected Content $attrs; + protected LayoutColumns $columns; /** * Proxy for attrs - * - * @param string $method - * @param array $args - * @return \Kirby\Cms\Field */ - public function __call(string $method, array $args = []) + public function __call(string $method, array $args = []): mixed { // layout methods if ($this->hasMethod($method) === true) { @@ -48,8 +39,6 @@ class Layout extends Item /** * Creates a new Layout object - * - * @param array $params */ public function __construct(array $params = []) { @@ -66,20 +55,16 @@ class Layout extends Item /** * Returns the attrs object - * - * @return \Kirby\Cms\Content */ - public function attrs() + public function attrs(): Content { return $this->attrs; } /** * Returns the columns in this layout - * - * @return \Kirby\Cms\LayoutColumns */ - public function columns() + public function columns(): LayoutColumns { return $this->columns; } @@ -87,24 +72,18 @@ class Layout extends Item /** * Checks if the layout is empty * @since 3.5.2 - * - * @return bool */ public function isEmpty(): bool { return $this ->columns() - ->filter(function ($column) { - return $column->isNotEmpty(); - }) + ->filter('isEmpty', false) ->count() === 0; } /** * Checks if the layout is not empty * @since 3.5.2 - * - * @return bool */ public function isNotEmpty(): bool { @@ -114,8 +93,6 @@ class Layout extends Item /** * The result is being sent to the editor * via the API in the panel - * - * @return array */ public function toArray(): array { diff --git a/kirby/src/Cms/LayoutColumn.php b/kirby/src/Cms/LayoutColumn.php index b4706cc..11d61d7 100644 --- a/kirby/src/Cms/LayoutColumn.php +++ b/kirby/src/Cms/LayoutColumn.php @@ -21,20 +21,11 @@ class LayoutColumn extends Item public const ITEMS_CLASS = LayoutColumns::class; - /** - * @var \Kirby\Cms\Blocks - */ - protected $blocks; - - /** - * @var string - */ - protected $width; + protected Blocks $blocks; + protected string $width; /** * Creates a new LayoutColumn object - * - * @param array $params */ public function __construct(array $params = []) { @@ -50,12 +41,8 @@ class LayoutColumn extends Item /** * Magic getter function - * - * @param string $method - * @param mixed $args - * @return mixed */ - public function __call(string $method, $args) + public function __call(string $method, mixed $args): mixed { // layout column methods if ($this->hasMethod($method) === true) { @@ -67,9 +54,8 @@ class LayoutColumn extends Item * Returns the blocks collection * * @param bool $includeHidden Sets whether to include hidden blocks - * @return \Kirby\Cms\Blocks */ - public function blocks(bool $includeHidden = false) + public function blocks(bool $includeHidden = false): Blocks { if ($includeHidden === false) { return $this->blocks->filter('isHidden', false); @@ -81,8 +67,6 @@ class LayoutColumn extends Item /** * Checks if the column is empty * @since 3.5.2 - * - * @return bool */ public function isEmpty(): bool { @@ -95,8 +79,6 @@ class LayoutColumn extends Item /** * Checks if the column is not empty * @since 3.5.2 - * - * @return bool */ public function isNotEmpty(): bool { @@ -105,9 +87,6 @@ class LayoutColumn extends Item /** * Returns the number of columns this column spans - * - * @param int $columns - * @return int */ public function span(int $columns = 12): int { @@ -121,8 +100,6 @@ class LayoutColumn extends Item /** * The result is being sent to the editor * via the API in the panel - * - * @return array */ public function toArray(): array { @@ -135,8 +112,6 @@ class LayoutColumn extends Item /** * Returns the width of the column - * - * @return string */ public function width(): string { diff --git a/kirby/src/Cms/LayoutColumns.php b/kirby/src/Cms/LayoutColumns.php index bed504f..195e39e 100644 --- a/kirby/src/Cms/LayoutColumns.php +++ b/kirby/src/Cms/LayoutColumns.php @@ -18,8 +18,6 @@ class LayoutColumns extends Items /** * All registered layout columns methods - * - * @var array */ - public static $methods = []; + public static array $methods = []; } diff --git a/kirby/src/Cms/Layouts.php b/kirby/src/Cms/Layouts.php index 4ddd4c2..fa6031c 100644 --- a/kirby/src/Cms/Layouts.php +++ b/kirby/src/Cms/Layouts.php @@ -2,7 +2,7 @@ namespace Kirby\Cms; -use Kirby\Data\Data; +use Kirby\Data\Json; use Kirby\Toolkit\Str; use Throwable; @@ -22,17 +22,28 @@ class Layouts extends Items /** * All registered layouts methods - * - * @var array */ - public static $methods = []; + public static array $methods = []; + + public static function factory( + array|null $items = null, + array $params = [] + ): static { + // convert single layout to layouts array + if ( + isset($items['columns']) === true || + isset($items['id']) === true + ) { + $items = [$items]; + } - public static function factory(array $items = null, array $params = []) - { $first = $items[0] ?? []; // if there are no wrapping layouts for blocks yet … - if (array_key_exists('content', $first) === true || array_key_exists('type', $first) === true) { + if ( + isset($first['content']) === true || + isset($first['type']) === true + ) { $items = [ [ 'id' => Str::uuid(), @@ -52,9 +63,6 @@ class Layouts extends Items /** * Checks if a given block type exists in the layouts collection * @since 3.6.0 - * - * @param string $type - * @return bool */ public function hasBlockType(string $type): bool { @@ -63,15 +71,15 @@ class Layouts extends Items /** * Parse layouts data - * - * @param array|string $input - * @return array */ - public static function parse($input): array + public static function parse(array|string|null $input): array { - if (empty($input) === false && is_array($input) === false) { + if ( + empty($input) === false && + is_array($input) === false + ) { try { - $input = Data::decode($input, 'json'); + $input = Json::decode((string)$input); } catch (Throwable) { return []; } @@ -89,9 +97,8 @@ class Layouts extends Items * @since 3.6.0 * * @param bool $includeHidden Sets whether to include hidden blocks - * @return \Kirby\Cms\Blocks */ - public function toBlocks(bool $includeHidden = false) + public function toBlocks(bool $includeHidden = false): Blocks { $blocks = []; diff --git a/kirby/src/Cms/License.php b/kirby/src/Cms/License.php new file mode 100644 index 0000000..6179cbb --- /dev/null +++ b/kirby/src/Cms/License.php @@ -0,0 +1,530 @@ + + * @link https://getkirby.com + * @copyright Bastian Allgeier + * @license https://getkirby.com/license + */ +class License +{ + public const HISTORY = [ + '3' => '2019-02-05', + '4' => '2023-11-28' + ]; + + protected const SALT = 'kwAHMLyLPBnHEskzH9pPbJsBxQhKXZnX'; + + // cache + protected LicenseStatus $status; + protected LicenseType $type; + + public function __construct( + protected string|null $activation = null, + protected string|null $code = null, + protected string|null $domain = null, + protected string|null $email = null, + protected string|null $order = null, + protected string|null $date = null, + protected string|null $signature = null, + ) { + // normalize arguments + $this->code = $this->code !== null ? trim($this->code) : null; + $this->email = $this->email !== null ? $this->normalizeEmail($this->email) : null; + } + + /** + * Returns the activation date if available + */ + public function activation( + string|IntlDateFormatter|null $format = null, + string|null $handler = null + ): int|string|null { + return $this->activation !== null ? Str::date(strtotime($this->activation), $format, $handler) : null; + } + + /** + * Returns the license code if available + */ + public function code(bool $obfuscated = false): string|null + { + if ($this->code !== null && $obfuscated === true) { + return Str::substr($this->code, 0, 10) . str_repeat('X', 22); + } + + return $this->code; + } + + /** + * Content for the license file + */ + public function content(): array + { + return [ + 'activation' => $this->activation, + 'code' => $this->code, + 'date' => $this->date, + 'domain' => $this->domain, + 'email' => $this->email, + 'order' => $this->order, + 'signature' => $this->signature, + ]; + } + + /** + * Returns the purchase date if available + */ + public function date( + string|IntlDateFormatter|null $format = null, + string|null $handler = null + ): int|string|null { + return $this->date !== null ? Str::date(strtotime($this->date), $format, $handler) : null; + } + + /** + * Returns the activation domain if available + */ + public function domain(): string|null + { + return $this->domain; + } + + /** + * Returns the activation email if available + */ + public function email(): string|null + { + return $this->email; + } + + /** + * Validates the email address of the license + */ + public function hasValidEmailAddress(): bool + { + return V::email($this->email) === true; + } + + /** + * Hub address + */ + public static function hub(): string + { + return App::instance()->option('hub', 'https://hub.getkirby.com'); + } + + /** + * Checks for all required components of a valid license + */ + public function isComplete(): bool + { + if ( + $this->code !== null && + $this->date !== null && + $this->domain !== null && + $this->email !== null && + $this->order !== null && + $this->signature !== null && + $this->hasValidEmailAddress() === true && + $this->type() !== LicenseType::Invalid + ) { + return true; + } + + return false; + } + + /** + * The license is still valid for the currently + * installed version, but it passed the 3 year period. + */ + public function isInactive(): bool + { + return $this->renewal() < time(); + } + + /** + * Checks for licenses beyond their 3 year period + */ + public function isLegacy(): bool + { + if ($this->type() === LicenseType::Legacy) { + return true; + } + + // without an activation date, the license + // renewal cannot be evaluated and the license + // has to be marked as expired + if ($this->activation === null) { + return true; + } + + // get release date of current major version + $major = Str::before(App::instance()->version(), '.'); + $release = strtotime(static::HISTORY[$major] ?? ''); + + // if there's no matching version in the history + // rather throw an exception to avoid further issues + // @codeCoverageIgnoreStart + if ($release === false) { + throw new InvalidArgumentException('The version for your license could not be found'); + } + // @codeCoverageIgnoreEnd + + // If the renewal date is older than the version launch + // date, the license is expired + return $this->renewal() < $release; + } + + /** + * Runs multiple checks to find out if the license is + * installed and verifiable + */ + public function isMissing(): bool + { + return + $this->isComplete() === false || + $this->isOnCorrectDomain() === false || + $this->isSigned() === false; + } + + /** + * Checks if the license is on the correct domain + */ + public function isOnCorrectDomain(): bool + { + if ($this->domain === null) { + return false; + } + + // compare domains + if ($this->normalizeDomain(App::instance()->system()->indexUrl()) !== $this->normalizeDomain($this->domain)) { + return false; + } + + return true; + } + + /** + * Compares the signature with all ingredients + */ + public function isSigned(): bool + { + if ($this->signature === null) { + return false; + } + + // get the public key + $pubKey = F::read(App::instance()->root('kirby') . '/kirby.pub'); + + // verify the license signature + $data = json_encode($this->signatureData()); + $signature = hex2bin($this->signature); + + return openssl_verify($data, $signature, $pubKey, 'RSA-SHA256') === 1; + } + + /** + * Returns a reliable label for the license type + */ + public function label(): string + { + if ($this->status() === LicenseStatus::Missing) { + return LicenseType::Invalid->label(); + } + + return $this->type()->label(); + } + + /** + * Prepares the email address to be make sure it + * does not have trailing spaces and is lowercase. + */ + protected function normalizeEmail(string $email): string + { + return Str::lower(trim($email)); + } + + /** + * Prepares the domain to be comparable + */ + protected function normalizeDomain(string $domain): string + { + // remove common "testing" subdomains as well as www. + // to ensure that installations of the same site have + // the same license URL; only for installations at /, + // subdirectory installations are difficult to normalize + if (Str::contains($domain, '/') === false) { + if (Str::startsWith($domain, 'www.')) { + return substr($domain, 4); + } + + if (Str::startsWith($domain, 'dev.')) { + return substr($domain, 4); + } + + if (Str::startsWith($domain, 'test.')) { + return substr($domain, 5); + } + + if (Str::startsWith($domain, 'staging.')) { + return substr($domain, 8); + } + } + + return $domain; + } + + /** + * Returns the order id if available + */ + public function order(): string|null + { + return $this->order; + } + + /** + * Support the old license file dataset + * from older licenses + */ + public static function polyfill(array $license): array + { + return [ + 'activation' => $license['activation'] ?? null, + 'code' => $license['code'] ?? $license['license'] ?? null, + 'date' => $license['date'] ?? null, + 'domain' => $license['domain'] ?? null, + 'email' => $license['email'] ?? null, + 'order' => $license['order'] ?? null, + 'signature' => $license['signature'] ?? null, + ]; + } + + /** + * Reads the license file in the config folder + * and creates a new license instance for it. + */ + public static function read(): static + { + try { + $license = Json::read(App::instance()->root('license')); + } catch (Throwable) { + return new static(); + } + + return new static(...static::polyfill($license)); + } + + /** + * Sends a request to the hub to register the license + */ + public function register(): static + { + if ($this->type() === LicenseType::Invalid) { + throw new InvalidArgumentException(['key' => 'license.format']); + } + + if ($this->hasValidEmailAddress() === false) { + throw new InvalidArgumentException(['key' => 'license.email']); + } + + if ($this->domain === null) { + throw new InvalidArgumentException(['key' => 'license.domain']); + } + + // @codeCoverageIgnoreStart + $response = $this->request('register', [ + 'license' => $this->code, + 'email' => $this->email, + 'domain' => $this->domain + ]); + + return $this->update($response); + // @codeCoverageIgnoreEnd + } + + /** + * Returns the renewal date + */ + public function renewal( + string|IntlDateFormatter|null $format = null, + string|null $handler = null + ): int|string|null { + if ($this->activation === null) { + return null; + } + + $time = strtotime('+3 years', $this->activation()); + return Str::date($time, $format, $handler); + } + + /** + * Sends a hub request + */ + public function request(string $path, array $data): array + { + // @codeCoverageIgnoreStart + $response = Remote::get(static::hub() . '/' . $path, [ + 'data' => $data + ]); + + // handle request errors + if ($response->code() !== 200) { + $message = $response->json()['message'] ?? 'The request failed'; + + throw new LogicException($message, $response->code()); + } + + return $response->json(); + // @codeCoverageIgnoreEnd + } + + /** + * Saves the license in the config folder + */ + public function save(): bool + { + if ($this->status()->activatable() !== true) { + throw new InvalidArgumentException([ + 'key' => 'license.verification' + ]); + } + + // where to store the license file + $file = App::instance()->root('license'); + + // save the license information + return Json::write($file, $this->content()); + } + + /** + * Returns the signature if available + */ + public function signature(): string|null + { + return $this->signature; + } + + /** + * Creates the signature data array to compare + * with the signature in ::isSigned + */ + public function signatureData(): array + { + if ($this->type() === LicenseType::Legacy) { + return [ + 'license' => $this->code, + 'order' => $this->order, + 'email' => hash('sha256', $this->email . static::SALT), + 'domain' => $this->domain, + 'date' => $this->date, + ]; + } + + return [ + 'activation' => $this->activation, + 'code' => $this->code, + 'date' => $this->date, + 'domain' => $this->domain, + 'email' => hash('sha256', $this->email . static::SALT), + 'order' => $this->order, + ]; + } + + /** + * Returns the license status as string + * This is used to build the proper UI elements + * for the license activation + */ + public function status(): LicenseStatus + { + return $this->status ??= match (true) { + $this->isMissing() === true => LicenseStatus::Missing, + $this->isLegacy() === true => LicenseStatus::Legacy, + $this->isInactive() === true => LicenseStatus::Inactive, + default => LicenseStatus::Active + }; + } + + /** + * Detects the license type if the license key is available + */ + public function type(): LicenseType + { + return $this->type ??= LicenseType::detect($this->code); + } + + /** + * Updates the license file + */ + public function update(array $data): static + { + // decode the response + $data = static::polyfill($data); + + $this->activation = $data['activation']; + $this->code = $data['code']; + $this->date = $data['date']; + $this->order = $data['order']; + $this->signature = $data['signature']; + + // clear the caches + unset($this->status, $this->type); + + // save the new state of the license + $this->save(); + + return $this; + } + + /** + * Sends an upgrade request to the hub in order + * to either redirect to the upgrade form or + * sync the new license state + * + * @codeCoverageIgnore + */ + public function upgrade(): array + { + $response = $this->request('upgrade', [ + 'domain' => $this->domain, + 'email' => $this->email, + 'license' => $this->code, + ]); + + // the license still needs an upgrade + if (empty($response['url']) === false) { + // validate the redirect URL + if (Str::startsWith($response['url'], static::hub()) === false) { + throw new Exception('We couldn’t redirect you to the Hub'); + } + + return [ + 'status' => 'upgrade', + 'url' => $response['url'] + ]; + } + + // the license has already been upgraded + // and can now be replaced + $this->update($response); + + return [ + 'status' => 'complete', + ]; + } +} diff --git a/kirby/src/Cms/LicenseStatus.php b/kirby/src/Cms/LicenseStatus.php new file mode 100644 index 0000000..42cdc56 --- /dev/null +++ b/kirby/src/Cms/LicenseStatus.php @@ -0,0 +1,144 @@ + + * @link https://getkirby.com + * @copyright Bastian Allgeier + * @license https://getkirby.com/license + * + * @codeCoverageIgnore + */ +enum LicenseStatus: string +{ + /** + * The license is valid and active + */ + case Active = 'active'; + + /** + * Only used for the demo instance + */ + case Demo = 'demo'; + + /** + * The included updates period of + * the license is over. + */ + case Inactive = 'inactive'; + + /** + * The installation has an old + * license (v1, v2, v3) + */ + case Legacy = 'legacy'; + + /** + * The installation has no license or + * the license cannot be validated + */ + case Missing = 'missing'; + + /** + * Checks if the license can be saved when it + * was entered in the activation dialog; + * renewable licenses are accepted as well + * to allow renewal from the Panel + */ + public function activatable(): bool + { + return match ($this) { + static::Active, + static::Inactive, + static::Legacy => true, + default => false + }; + } + + /** + * Returns the dialog according to the status + */ + public function dialog(): string|null + { + return match ($this) { + static::Missing => 'registration', + static::Demo => null, + default => 'license' + }; + } + + /** + * Returns the icon according to the status. + * The icon is used for the system view and + * in the license dialog. + */ + public function icon(): string + { + return match ($this) { + static::Missing => 'key', + static::Legacy => 'alert', + static::Inactive => 'clock', + static::Active => 'check', + static::Demo => 'preview', + }; + } + + /** + * The info text is shown in the license dialog + * in the status row. + */ + public function info(string|null $end = null): string + { + return I18n::template('license.status.' . $this->value . '.info', ['date' => $end]); + } + + /** + * Label for the system view + */ + public function label(): string + { + return I18n::translate('license.status.' . $this->value . '.label'); + } + + /** + * Checks if the license can be renewed + * The license dialog will show the renew + * button in this case and redirect to the hub + */ + public function renewable(): bool + { + return match ($this) { + static::Demo, + static::Active => false, + default => true + }; + } + + /** + * Returns the theme according to the status + * The theme is used for the label in the system + * view and the status icon in the license dialog. + */ + public function theme(): string + { + return match ($this) { + static::Missing => 'love', + static::Legacy => 'negative', + static::Inactive => 'notice', + static::Active => 'positive', + static::Demo => 'notice', + }; + } + + /** + * Returns the status as string value + */ + public function value(): string + { + return $this->value; + } +} diff --git a/kirby/src/Cms/LicenseType.php b/kirby/src/Cms/LicenseType.php new file mode 100644 index 0000000..6a9fea7 --- /dev/null +++ b/kirby/src/Cms/LicenseType.php @@ -0,0 +1,111 @@ + + * @link https://getkirby.com + * @copyright Bastian Allgeier + * @license https://getkirby.com/license + * + * @codeCoverageIgnore + */ +enum LicenseType: string +{ + /** + * New basic licenses + */ + case Basic = 'basic'; + + /** + * New enterprise licenses + */ + case Enterprise = 'enterprise'; + + /** + * Invalid license codes + */ + case Invalid = 'invalid'; + + /** + * Old Kirby 3 licenses + */ + case Legacy = 'legacy'; + + /** + * Detects the correct LicenseType based on the code + */ + public static function detect(string|null $code): static + { + return match (true) { + static::Basic->isValidCode($code) => static::Basic, + static::Enterprise->isValidCode($code) => static::Enterprise, + static::Legacy->isValidCode($code) => static::Legacy, + default => static::Invalid + }; + } + + /** + * Checks for a valid license code + * by prefix and length. This is just a + * rough validation. + */ + public function isValidCode(string|null $code): bool + { + return + $code !== null && + Str::length($code) === $this->length() && + Str::startsWith($code, $this->prefix()) === true; + } + + /** + * The expected lengths of the license code + */ + public function length(): int + { + return match ($this) { + static::Basic => 38, + static::Enterprise => 38, + static::Legacy => 39, + static::Invalid => 0, + }; + } + + /** + * A human-readable license type label + */ + public function label(): string + { + return match ($this) { + static::Basic => 'Kirby Basic', + static::Enterprise => 'Kirby Enterprise', + static::Legacy => 'Kirby 3', + static::Invalid => I18n::translate('license.unregistered.label'), + }; + } + + /** + * The expected prefix for the license code + */ + public function prefix(): string|null + { + return match ($this) { + static::Basic => 'K-BAS-', + static::Enterprise => 'K-ENT-', + static::Legacy => 'K3-PRO-', + static::Invalid => null, + }; + } + + /** + * Returns the enum value + */ + public function value(): string + { + return $this->value; + } +} diff --git a/kirby/src/Cms/Loader.php b/kirby/src/Cms/Loader.php index d0a5488..6e71878 100644 --- a/kirby/src/Cms/Loader.php +++ b/kirby/src/Cms/Loader.php @@ -50,9 +50,6 @@ class Loader /** * Loads the area definition - * - * @param string $name - * @return array|null */ public function area(string $name): array|null { @@ -62,15 +59,14 @@ class Loader /** * Loads all areas and makes sure that plugins * are injected properly - * - * @return array */ public function areas(): array { $areas = []; $extensions = $this->withPlugins === true ? $this->kirby->extensions('areas') : []; - // load core areas and extend them with elements from plugins if they exist + // load core areas and extend them with elements + // from plugins if they exist foreach ($this->kirby->core()->areas() as $id => $area) { $area = $this->resolveArea($area); @@ -98,9 +94,6 @@ class Loader /** * Loads a core component closure - * - * @param string $name - * @return \Closure|null */ public function component(string $name): Closure|null { @@ -109,8 +102,6 @@ class Loader /** * Loads all core component closures - * - * @return array */ public function components(): array { @@ -119,21 +110,14 @@ class Loader /** * Loads a particular extension - * - * @param string $type - * @param string $name - * @return mixed */ - public function extension(string $type, string $name) + public function extension(string $type, string $name): mixed { return $this->extensions($type)[$name] ?? null; } /** * Loads all defined extensions - * - * @param string $type - * @return array */ public function extensions(string $type): array { @@ -152,11 +136,8 @@ class Loader * * 3.) closures will be called and the Kirby instance will be * passed as first argument - * - * @param mixed $item - * @return mixed */ - public function resolve($item) + public function resolve(mixed $item): mixed { if (is_string($item) === true) { $item = match (F::extension($item)) { @@ -175,9 +156,6 @@ class Loader /** * Calls `static::resolve()` on all items * in the given array - * - * @param array $items - * @return array */ public function resolveAll(array $items): array { @@ -193,11 +171,8 @@ class Loader /** * Areas need a bit of special treatment * when they are being loaded - * - * @param string|array|Closure $area - * @return array */ - public function resolveArea($area): array + public function resolveArea(string|array|Closure $area): array { $area = $this->resolve($area); $dropdowns = $area['dropdowns'] ?? []; @@ -217,9 +192,6 @@ class Loader /** * Loads a particular section definition - * - * @param string $name - * @return array|null */ public function section(string $name): array|null { @@ -228,8 +200,6 @@ class Loader /** * Loads all section defintions - * - * @return array */ public function sections(): array { @@ -239,8 +209,6 @@ class Loader /** * Returns the status flag, which shows * if plugins are loaded as well. - * - * @return bool */ public function withPlugins(): bool { diff --git a/kirby/src/Cms/Media.php b/kirby/src/Cms/Media.php index 777ab9a..fe8c8e9 100644 --- a/kirby/src/Cms/Media.php +++ b/kirby/src/Cms/Media.php @@ -3,6 +3,8 @@ namespace Kirby\Cms; use Kirby\Data\Data; +use Kirby\Exception\InvalidArgumentException; +use Kirby\Exception\NotFoundException; use Kirby\Filesystem\Dir; use Kirby\Filesystem\F; use Kirby\Toolkit\Str; @@ -23,14 +25,12 @@ class Media /** * Tries to find a file by model and filename * and to copy it to the media folder. - * - * @param \Kirby\Cms\Model|null $model - * @param string $hash - * @param string $filename - * @return \Kirby\Cms\Response|false */ - public static function link(Model $model = null, string $hash, string $filename) - { + public static function link( + Page|Site|User|null $model, + string $hash, + string $filename + ): Response|false { if ($model === null) { return false; } @@ -57,15 +57,16 @@ class Media } // try to generate a thumb for the file - return static::thumb($model, $hash, $filename); + try { + return static::thumb($model, $hash, $filename); + } catch (NotFoundException) { + // render the error page if there is no job for this filename + return false; + } } /** * Copy the file to the final media folder location - * - * @param \Kirby\Cms\File $file - * @param string $dest - * @return bool */ public static function publish(File $file, string $dest): bool { @@ -87,14 +88,12 @@ class Media * Tries to find a job file for the * given filename and then calls the thumb * component to create a thumbnail accordingly - * - * @param \Kirby\Cms\Model|string $model - * @param string $hash - * @param string $filename - * @return \Kirby\Cms\Response|false */ - public static function thumb($model, string $hash, string $filename) - { + public static function thumb( + File|Page|Site|User|string $model, + string $hash, + string $filename + ): Response|false { $kirby = App::instance(); $root = match (true) { @@ -109,15 +108,23 @@ class Media => $model->mediaRoot() . '/' . $hash }; + $thumb = $root . '/' . $filename; + $job = $root . '/.jobs/' . $filename . '.json'; + try { - $thumb = $root . '/' . $filename; - $job = $root . '/.jobs/' . $filename . '.json'; $options = Data::read($job); + } catch (Throwable) { + // send a customized error message to make clearer what happened here + throw new NotFoundException('The thumbnail configuration could not be found'); + } - if (empty($options) === true) { - return false; - } + if (empty($options['filename']) === true) { + throw new InvalidArgumentException('Incomplete thumbnail configuration'); + } + try { + // find the correct source file depending on the model + // this adds support for custom assets $source = match (true) { is_string($model) === true => $kirby->root('index') . '/' . $model . '/' . $options['filename'], @@ -125,30 +132,30 @@ class Media => $model->file($options['filename'])->root() }; - try { - $kirby->thumb($source, $thumb, $options); - F::remove($job); - return Response::file($thumb); - } catch (Throwable) { - F::remove($thumb); - return Response::file($source); - } - } catch (Throwable) { - return false; + // generate the thumbnail and save it in the media folder + $kirby->thumb($source, $thumb, $options); + + // remove the job file once the thumbnail has been created + F::remove($job); + + // read the file and send it to the browser + return Response::file($thumb); + } catch (Throwable $e) { + // remove potentially broken thumbnails + F::remove($thumb); + throw $e; } } /** * Deletes all versions of the given file * within the parent directory - * - * @param string $directory - * @param \Kirby\Cms\File $file - * @param string|null $ignore - * @return bool */ - public static function unpublish(string $directory, File $file, string $ignore = null): bool - { + public static function unpublish( + string $directory, + File $file, + string|null $ignore = null + ): bool { if (is_dir($directory) === false) { return true; } diff --git a/kirby/src/Cms/Model.php b/kirby/src/Cms/Model.php index 7acf7c6..a9c61e5 100644 --- a/kirby/src/Cms/Model.php +++ b/kirby/src/Cms/Model.php @@ -5,7 +5,7 @@ namespace Kirby\Cms; use Kirby\Toolkit\Properties; /** - * Foundation for Page, Site, File and User models. + * @deprecated 4.0.0 will be removed in Kirby 5.0 * * @package Kirby Cms * @author Bastian Allgeier @@ -86,7 +86,7 @@ abstract class Model * @param \Kirby\Cms\App|null $kirby * @return $this */ - protected function setKirby(App $kirby = null) + protected function setKirby(App|null $kirby = null) { static::$kirby = $kirby; return $this; @@ -99,7 +99,7 @@ abstract class Model * @param \Kirby\Cms\Site|null $site * @return $this */ - public function setSite(Site $site = null) + public function setSite(Site|null $site = null) { $this->site = $site; return $this; diff --git a/kirby/src/Cms/ModelPermissions.php b/kirby/src/Cms/ModelPermissions.php index 916c0d8..a700b26 100644 --- a/kirby/src/Cms/ModelPermissions.php +++ b/kirby/src/Cms/ModelPermissions.php @@ -15,28 +15,13 @@ use Kirby\Toolkit\A; */ abstract class ModelPermissions { - protected $category; - protected $model; - protected $options; - protected $permissions; - protected $user; + protected string $category; + protected ModelWithContent $model; + protected array $options; + protected Permissions $permissions; + protected User $user; - /** - * @param string $method - * @param array $arguments - * @return bool - */ - public function __call(string $method, array $arguments = []): bool - { - return $this->can($method); - } - - /** - * ModelPermissions constructor - * - * @param \Kirby\Cms\Model $model - */ - public function __construct(Model $model) + public function __construct(ModelWithContent $model) { $this->model = $model; $this->options = $model->blueprint()->options(); @@ -44,10 +29,14 @@ abstract class ModelPermissions $this->permissions = $this->user->role()->permissions(); } + public function __call(string $method, array $arguments = []): bool + { + return $this->can($method); + } + /** * Improved `var_dump` output - * - * @return array + * @codeCoverageIgnore */ public function __debugInfo(): array { @@ -55,18 +44,27 @@ abstract class ModelPermissions } /** - * @param string $action - * @return bool + * Returns whether the current user is allowed to do + * a certain action on the model + * + * @param bool $default Will be returned if $action does not exist */ - public function can(string $action): bool - { + public function can( + string $action, + bool $default = false + ): bool { + $user = $this->user->id(); $role = $this->user->role()->id(); + // users with the `nobody` role can do nothing + // that needs a permission check if ($role === 'nobody') { return false; } - // check for a custom overall can method + // check for a custom `can` method + // which would take priority over any other + // role-based permission rules if ( method_exists($this, 'can' . $action) === true && $this->{'can' . $action}() === false @@ -74,6 +72,11 @@ abstract class ModelPermissions return false; } + // the almighty `kirby` user can do anything + if ($user === 'kirby' && $role === 'admin') { + return true; + } + // evaluate the blueprint options block if (isset($this->options[$action]) === true) { $options = $this->options[$action]; @@ -90,25 +93,32 @@ abstract class ModelPermissions is_array($options) === true && A::isAssociative($options) === true ) { - return $options[$role] ?? $options['*'] ?? false; + if (isset($options[$role]) === true) { + return $options[$role]; + } + + if (isset($options['*']) === true) { + return $options['*']; + } } } - return $this->permissions->for($this->category, $action); + return $this->permissions->for($this->category, $action, $default); } /** - * @param string $action - * @return bool + * Returns whether the current user is not allowed to do + * a certain action on the model + * + * @param bool $default Will be returned if $action does not exist */ - public function cannot(string $action): bool - { - return $this->can($action) === false; + public function cannot( + string $action, + bool $default = true + ): bool { + return $this->can($action, !$default) === false; } - /** - * @return array - */ public function toArray(): array { $array = []; diff --git a/kirby/src/Cms/ModelWithContent.php b/kirby/src/Cms/ModelWithContent.php index 42ad403..d525307 100644 --- a/kirby/src/Cms/ModelWithContent.php +++ b/kirby/src/Cms/ModelWithContent.php @@ -3,9 +3,14 @@ namespace Kirby\Cms; use Closure; -use Kirby\Data\Data; +use Kirby\Content\Content; +use Kirby\Content\ContentStorage; +use Kirby\Content\ContentTranslation; +use Kirby\Content\PlainTextContentStorageHandler; use Kirby\Exception\InvalidArgumentException; +use Kirby\Exception\NotFoundException; use Kirby\Form\Form; +use Kirby\Panel\Model; use Kirby\Toolkit\Str; use Kirby\Uuid\Identifiable; use Kirby\Uuid\Uuid; @@ -21,70 +26,106 @@ use Throwable; * @copyright Bastian Allgeier * @license https://getkirby.com/license */ -abstract class ModelWithContent extends Model implements Identifiable +abstract class ModelWithContent implements Identifiable { /** - * The content - * - * @var \Kirby\Cms\Content + * Each model must define a CLASS_ALIAS + * which will be used in template queries. + * The CLASS_ALIAS is a short human-readable + * version of the class name, i.e. page. */ - public $content; + public const CLASS_ALIAS = null; /** - * @var \Kirby\Cms\Translations + * Cached array of valid blueprints + * that could be used for the model */ - public $translations; + public array|null $blueprints = null; + + public Content|null $content; + public static App $kirby; + protected Site|null $site; + protected ContentStorage $storage; + public Collection|null $translations = null; + + /** + * Store values used to initilaize object + */ + protected array $propertyData = []; + + public function __construct(array $props = []) + { + $this->site = $props['site'] ?? null; + + $this->setContent($props['content'] ?? null); + $this->setTranslations($props['translations'] ?? null); + + $this->propertyData = $props; + } /** * Returns the blueprint of the model - * - * @return \Kirby\Cms\Blueprint */ - abstract public function blueprint(); + abstract public function blueprint(): Blueprint; /** * Returns an array with all blueprints that are available - * - * @param string|null $inSection - * @return array */ - public function blueprints(string $inSection = null): array + public function blueprints(string|null $inSection = null): array { - $blueprints = []; - $blueprint = $this->blueprint(); - $sections = $inSection !== null ? [$blueprint->section($inSection)] : $blueprint->sections(); + // helper function + $toBlueprints = function (array $sections): array { + $blueprints = []; - foreach ($sections as $section) { - if ($section === null) { - continue; + foreach ($sections as $section) { + if ($section === null) { + continue; + } + + foreach ((array)$section->blueprints() as $blueprint) { + $blueprints[$blueprint['name']] = $blueprint; + } } - foreach ((array)$section->blueprints() as $blueprint) { - $blueprints[$blueprint['name']] = $blueprint; - } + return array_values($blueprints); + }; + + $blueprint = $this->blueprint(); + + // no caching for when collecting for specific section + if ($inSection !== null) { + return $toBlueprints([$blueprint->section($inSection)]); } - return array_values($blueprints); + return $this->blueprints ??= $toBlueprints($blueprint->sections()); + } + + /** + * Creates a new instance with the same + * initial properties + * + * @todo eventually refactor without need of propertyData + */ + public function clone(array $props = []): static + { + return new static(array_replace_recursive($this->propertyData, $props)); } /** * Executes any given model action - * - * @param string $action - * @param array $arguments - * @param \Closure $callback - * @return mixed */ - abstract protected function commit(string $action, array $arguments, Closure $callback); + abstract protected function commit( + string $action, + array $arguments, + Closure $callback + ): mixed; /** * Returns the content * - * @param string|null $languageCode - * @return \Kirby\Cms\Content * @throws \Kirby\Exception\InvalidArgumentException If the language for the given code does not exist */ - public function content(string $languageCode = null) + public function content(string|null $languageCode = null): Content { // single language support if ($this->kirby()->multilang() === false) { @@ -124,72 +165,55 @@ abstract class ModelWithContent extends Model implements Identifiable } /** - * Returns the absolute path to the content file - * + * Returns the absolute path to the content file; + * NOTE: only supports the published content file + * (use `$model->storage()->contentFile()` for other versions) * @internal - * @param string|null $languageCode - * @param bool $force - * @return string + * @deprecated 4.0.0 + * @todo Remove in v5 + * @codeCoverageIgnore + * * @throws \Kirby\Exception\InvalidArgumentException If the language for the given code does not exist */ - public function contentFile(string $languageCode = null, bool $force = false): string - { - $extension = $this->contentFileExtension(); - $directory = $this->contentFileDirectory(); - $filename = $this->contentFileName(); + public function contentFile( + string|null $languageCode = null, + bool $force = false + ): string { + Helpers::deprecated('The internal $model->contentFile() method has been deprecated. You can use $model->storage()->contentFile() instead, however please note that this method is also internal and may be removed in the future.', 'model-content-file'); - // overwrite the language code - if ($force === true) { - if (empty($languageCode) === false) { - return $directory . '/' . $filename . '.' . $languageCode . '.' . $extension; - } - - return $directory . '/' . $filename . '.' . $extension; - } - - // add and validate the language code in multi language mode - if ($this->kirby()->multilang() === true) { - if ($language = $this->kirby()->languageCode($languageCode)) { - return $directory . '/' . $filename . '.' . $language . '.' . $extension; - } - - throw new InvalidArgumentException('Invalid language: ' . $languageCode); - } - - return $directory . '/' . $filename . '.' . $extension; + return $this->storage()->contentFile( + $this->storage()->defaultVersion(), + $languageCode, + $force + ); } /** - * Returns an array with all content files - * - * @return array + * Returns an array with all content files; + * NOTE: only supports the published content file + * (use `$model->storage()->contentFiles()` for other versions) + * @deprecated 4.0.0 + * @todo Remove in v5 + * @codeCoverageIgnore */ public function contentFiles(): array { - if ($this->kirby()->multilang() === true) { - $files = []; - foreach ($this->kirby()->languages()->codes() as $code) { - $files[] = $this->contentFile($code); - } - return $files; - } + Helpers::deprecated('The internal $model->contentFiles() method has been deprecated. You can use $model->storage()->contentFiles() instead, however please note that this method is also internal and may be removed in the future.', 'model-content-file'); - return [ - $this->contentFile() - ]; + return $this->storage()->contentFiles( + $this->storage()->defaultVersion() + ); } /** * Prepares the content that should be written * to the text file - * * @internal - * @param array $data - * @param string|null $languageCode - * @return array */ - public function contentFileData(array $data, string $languageCode = null): array - { + public function contentFileData( + array $data, + string|null $languageCode = null + ): array { return $data; } @@ -197,44 +221,102 @@ abstract class ModelWithContent extends Model implements Identifiable * Returns the absolute path to the * folder in which the content file is * located - * * @internal - * @return string|null + * @deprecated 4.0.0 + * @todo Remove in v5 + * @codeCoverageIgnore */ public function contentFileDirectory(): string|null { + Helpers::deprecated('The internal $model->contentFileDirectory() method has been deprecated. Please let us know via a GitHub issue if you need this method and tell us your use case.', 'model-content-file'); return $this->root(); } /** * Returns the extension of the content file - * * @internal - * @return string + * @deprecated 4.0.0 + * @todo Remove in v5 + * @codeCoverageIgnore */ public function contentFileExtension(): string { + Helpers::deprecated('The internal $model->contentFileName() method has been deprecated. Please let us know via a GitHub issue if you need this method and tell us your use case.', 'model-content-file'); return $this->kirby()->contentExtension(); } /** * Needs to be declared by the final model - * * @internal - * @return string + * @deprecated 4.0.0 + * @todo Remove in v5 + * @codeCoverageIgnore */ abstract public function contentFileName(): string; /** - * Decrement a given field value - * - * @param string $field - * @param int $by - * @param int $min - * @return static + * Converts model to new blueprint + * incl. its content for all translations */ - public function decrement(string $field, int $by = 1, int $min = 0) + protected function convertTo(string $blueprint): static { + // first close object with new blueprint as template + $new = $this->clone(['template' => $blueprint]); + + // temporary compatibility change (TODO: also convert changes) + $identifier = $this->storage()->defaultVersion(); + + // for multilang, we go through all translations and + // covnert the content for each of them, remove the content file + // to rewrite it with converted content afterwards + if ($this->kirby()->multilang() === true) { + $translations = []; + + foreach ($this->kirby()->languages()->codes() as $code) { + if ($this->translation($code)?->exists() === true) { + $content = $this->content($code)->convertTo($blueprint); + + // delete the old text file + $this->storage()->delete( + $identifier, + $code + ); + + // save to re-create the translation content file + // with the converted/updated content + $new->save($content, $code); + } + + $translations[] = [ + 'code' => $code, + 'content' => $content ?? null + ]; + } + + // cloning the object with the new translations content ensures + // that `propertyData` prop does not hold any old translations + // content that could surface on subsequent cloning + return $new->clone(['translations' => $translations]); + } + + // for single language setups, we do the same, + // just once for the main content + $content = $this->content()->convertTo($blueprint); + + // delete the old text file + $this->storage()->delete($identifier, 'default'); + + return $new->save($content); + } + + /** + * Decrement a given field value + */ + public function decrement( + string $field, + int $by = 1, + int $min = 0 + ): static { $value = (int)$this->content()->get($field)->value() - $by; if ($value < $min) { @@ -246,8 +328,6 @@ abstract class ModelWithContent extends Model implements Identifiable /** * Returns all content validation errors - * - * @return array */ public function errors(): array { @@ -261,15 +341,38 @@ abstract class ModelWithContent extends Model implements Identifiable } /** - * Increment a given field value - * - * @param string $field - * @param int $by - * @param int|null $max - * @return static + * Creates a clone and fetches all + * lazy-loaded getters to get a full copy */ - public function increment(string $field, int $by = 1, int $max = null) + public function hardcopy(): static { + $clone = $this->clone(); + + foreach (get_object_vars($clone) as $name => $default) { + if (method_exists($clone, $name) === true) { + $clone->$name(); + } + } + + return $clone; + } + + /** + * Each model must return a unique id + */ + public function id(): string|null + { + return null; + } + + /** + * Increment a given field value + */ + public function increment( + string $field, + int $by = 1, + int|null $max = null + ): static { $value = (int)$this->content()->get($field)->value() + $by; if ($max && $value > $max) { @@ -281,8 +384,6 @@ abstract class ModelWithContent extends Model implements Identifiable /** * Checks if the model is locked for the current user - * - * @return bool */ public function isLocked(): bool { @@ -292,12 +393,18 @@ abstract class ModelWithContent extends Model implements Identifiable /** * Checks if the data has any errors - * - * @return bool */ public function isValid(): bool { - return Form::for($this)->hasErrors() === false; + return Form::for($this)->isValid() === true; + } + + /** + * Returns the parent Kirby instance + */ + public function kirby(): App + { + return static::$kirby ??= App::instance(); } /** @@ -305,12 +412,14 @@ abstract class ModelWithContent extends Model implements Identifiable * * Only if a content directory exists, * virtual pages will need to overwrite this method - * - * @return \Kirby\Cms\ContentLock|null */ - public function lock() + public function lock(): ContentLock|null { - $dir = $this->contentFileDirectory(); + $dir = $this->root(); + + if ($this::CLASS_ALIAS === 'file') { + $dir = dirname($dir); + } if ( $this->kirby()->option('content.locking', true) && @@ -319,33 +428,43 @@ abstract class ModelWithContent extends Model implements Identifiable ) { return new ContentLock($this); } + + return null; } /** * Returns the panel info of the model * @since 3.6.0 - * - * @return \Kirby\Panel\Model */ - abstract public function panel(); + abstract public function panel(): Model; /** * Must return the permissions object for the model - * - * @return \Kirby\Cms\ModelPermissions */ - abstract public function permissions(); + abstract public function permissions(): ModelPermissions; + + /** + * Clean internal caches + * + * @return $this + */ + public function purge(): static + { + $this->blueprints = null; + $this->content = null; + $this->translations = null; + + return $this; + } /** * Creates a string query, starting from the model - * * @internal - * @param string|null $query - * @param string|null $expect - * @return mixed */ - public function query(string $query = null, string $expect = null) - { + public function query( + string|null $query = null, + string|null $expect = null + ): mixed { if ($query === null) { return null; } @@ -370,43 +489,37 @@ abstract class ModelWithContent extends Model implements Identifiable /** * Read the content from the content file - * * @internal - * @param string|null $languageCode - * @return array */ - public function readContent(string $languageCode = null): array + public function readContent(string|null $languageCode = null): array { - $file = $this->contentFile($languageCode); - - // only if the content file really does not exist, it's ok - // to return empty content. Otherwise this could lead to - // content loss in case of file reading issues - if (file_exists($file) === false) { + try { + return $this->storage()->read( + $this->storage()->defaultVersion(), + $languageCode + ); + } catch (NotFoundException) { + // only if the content file really does not exist, it's ok + // to return empty content. Otherwise this could lead to + // content loss in case of file reading issues return []; } - - return Data::read($file); } /** * Returns the absolute path to the model - * - * @return string|null */ abstract public function root(): string|null; /** * Stores the content on disk - * * @internal - * @param array|null $data - * @param string|null $languageCode - * @param bool $overwrite - * @return static */ - public function save(array $data = null, string $languageCode = null, bool $overwrite = false) - { + public function save( + array|null $data = null, + string|null $languageCode = null, + bool $overwrite = false + ): static { if ($this->kirby()->multilang() === true) { return $this->saveTranslation($data, $languageCode, $overwrite); } @@ -416,13 +529,11 @@ abstract class ModelWithContent extends Model implements Identifiable /** * Save the single language content - * - * @param array|null $data - * @param bool $overwrite - * @return static */ - protected function saveContent(array $data = null, bool $overwrite = false) - { + protected function saveContent( + array|null $data = null, + bool $overwrite = false + ): static { // create a clone to avoid modifying the original $clone = $this->clone(); @@ -438,14 +549,13 @@ abstract class ModelWithContent extends Model implements Identifiable /** * Save a translation * - * @param array|null $data - * @param string|null $languageCode - * @param bool $overwrite - * @return static * @throws \Kirby\Exception\InvalidArgumentException If the language for the given code does not exist */ - protected function saveTranslation(array $data = null, string $languageCode = null, bool $overwrite = false) - { + protected function saveTranslation( + array|null $data = null, + string|null $languageCode = null, + bool $overwrite = false + ): static { // create a clone to not touch the original $clone = $this->clone(); @@ -491,10 +601,9 @@ abstract class ModelWithContent extends Model implements Identifiable /** * Sets the Content object * - * @param array|null $content * @return $this */ - protected function setContent(array $content = null) + protected function setContent(array|null $content = null): static { if ($content !== null) { $content = new Content($content, $this); @@ -507,10 +616,9 @@ abstract class ModelWithContent extends Model implements Identifiable /** * Create the translations collection from an array * - * @param array|null $translations * @return $this */ - protected function setTranslations(array $translations = null) + protected function setTranslations(array|null $translations = null): static { if ($translations !== null) { $this->translations = new Collection(); @@ -520,23 +628,57 @@ abstract class ModelWithContent extends Model implements Identifiable $translation = new ContentTranslation($props); $this->translations->data[$translation->code()] = $translation; } + } else { + $this->translations = null; } return $this; } + /** + * Returns the parent Site instance + */ + public function site(): Site + { + return $this->site ??= $this->kirby()->site(); + } + + /** + * Returns the content storage handler + * @internal + */ + public function storage(): ContentStorage + { + return $this->storage ??= new ContentStorage( + model: $this, + handler: PlainTextContentStorageHandler::class + ); + } + + /** + * Convert the model to a simple array + */ + public function toArray(): array + { + return [ + 'content' => $this->content()->toArray(), + 'translations' => $this->translations()->toArray() + ]; + } + /** * String template builder with automatic HTML escaping * @since 3.6.0 * * @param string|null $template Template string or `null` to use the model ID - * @param array $data * @param string|null $fallback Fallback for tokens in the template that cannot be replaced * (`null` to keep the original token) - * @return string */ - public function toSafeString(string $template = null, array $data = [], string|null $fallback = ''): string - { + public function toSafeString( + string|null $template = null, + array $data = [], + string|null $fallback = '' + ): string { return $this->toString($template, $data, $fallback, 'safeTemplate'); } @@ -544,14 +686,16 @@ abstract class ModelWithContent extends Model implements Identifiable * String template builder * * @param string|null $template Template string or `null` to use the model ID - * @param array $data * @param string|null $fallback Fallback for tokens in the template that cannot be replaced * (`null` to keep the original token) * @param string $handler For internal use - * @return string */ - public function toString(string $template = null, array $data = [], string|null $fallback = '', string $handler = 'template'): string - { + public function toString( + string|null $template = null, + array $data = [], + string|null $fallback = '', + string $handler = 'template' + ): string { if ($template === null) { return $this->id() ?? ''; } @@ -570,15 +714,22 @@ abstract class ModelWithContent extends Model implements Identifiable return $result; } + /** + * Makes it possible to convert the entire model + * to a string. Mostly useful for debugging + */ + public function __toString(): string + { + return $this->id(); + } + /** * Returns a single translation by language code * If no code is specified the current translation is returned - * - * @param string|null $languageCode - * @return \Kirby\Cms\ContentTranslation|null */ - public function translation(string $languageCode = null) - { + public function translation( + string|null $languageCode = null + ): ContentTranslation|null { if ($language = $this->kirby()->language($languageCode)) { return $this->translations()->find($language->code()); } @@ -588,10 +739,8 @@ abstract class ModelWithContent extends Model implements Identifiable /** * Returns the translations collection - * - * @return \Kirby\Cms\Collection */ - public function translations() + public function translations(): Collection { if ($this->translations !== null) { return $this->translations; @@ -614,14 +763,13 @@ abstract class ModelWithContent extends Model implements Identifiable /** * Updates the model data * - * @param array|null $input - * @param string|null $languageCode - * @param bool $validate - * @return static * @throws \Kirby\Exception\InvalidArgumentException If the input array contains invalid values */ - public function update(array $input = null, string $languageCode = null, bool $validate = false) - { + public function update( + array|null $input = null, + string|null $languageCode = null, + bool $validate = false + ): static { $form = Form::for($this, [ 'ignoreDisabled' => $validate === false, 'input' => $input, @@ -636,10 +784,17 @@ abstract class ModelWithContent extends Model implements Identifiable ]); } - $arguments = [static::CLASS_ALIAS => $this, 'values' => $form->data(), 'strings' => $form->strings(), 'languageCode' => $languageCode]; - return $this->commit('update', $arguments, function ($model, $values, $strings, $languageCode) { - return $model->save($strings, $languageCode, true); - }); + return $this->commit( + 'update', + [ + static::CLASS_ALIAS => $this, + 'values' => $form->data(), + 'strings' => $form->strings(), + 'languageCode' => $languageCode + ], + fn ($model, $values, $strings, $languageCode) => + $model->save($strings, $languageCode, true) + ); } /** @@ -654,17 +809,21 @@ abstract class ModelWithContent extends Model implements Identifiable /** * Low level data writer method * to store the given data on disk or anywhere else - * * @internal - * @param array $data - * @param string|null $languageCode - * @return bool */ - public function writeContent(array $data, string $languageCode = null): bool + public function writeContent(array $data, string|null $languageCode = null): bool { - return Data::write( - $this->contentFile($languageCode), - $this->contentFileData($data, $languageCode) - ); + $data = $this->contentFileData($data, $languageCode); + $id = $this->storage()->defaultVersion(); + + try { + // we can only update if the version already exists + $this->storage()->update($id, $languageCode, $data); + } catch (NotFoundException) { + // otherwise create a new version + $this->storage()->create($id, $languageCode, $data); + } + + return true; } } diff --git a/kirby/src/Cms/Nest.php b/kirby/src/Cms/Nest.php index cde1b29..0f8521d 100644 --- a/kirby/src/Cms/Nest.php +++ b/kirby/src/Cms/Nest.php @@ -2,6 +2,8 @@ namespace Kirby\Cms; +use Kirby\Content\Field; + /** * The Nest class converts any array type * into a Kirby style collection/object. This diff --git a/kirby/src/Cms/NestCollection.php b/kirby/src/Cms/NestCollection.php index 129668d..605b0cb 100644 --- a/kirby/src/Cms/NestCollection.php +++ b/kirby/src/Cms/NestCollection.php @@ -21,7 +21,7 @@ class NestCollection extends BaseCollection * to an array. This can also take a callback * function to further modify the array result. */ - public function toArray(Closure $map = null): array + public function toArray(Closure|null $map = null): array { return parent::toArray($map ?? fn ($object) => $object->toArray()); } diff --git a/kirby/src/Cms/NestObject.php b/kirby/src/Cms/NestObject.php index 2dc9176..2466023 100644 --- a/kirby/src/Cms/NestObject.php +++ b/kirby/src/Cms/NestObject.php @@ -2,6 +2,7 @@ namespace Kirby\Cms; +use Kirby\Content\Field; use Kirby\Toolkit\Obj; /** diff --git a/kirby/src/Cms/Page.php b/kirby/src/Cms/Page.php index 49fa97f..a1e04df 100644 --- a/kirby/src/Cms/Page.php +++ b/kirby/src/Cms/Page.php @@ -3,15 +3,19 @@ namespace Kirby\Cms; use Closure; +use Kirby\Content\Field; use Kirby\Exception\Exception; use Kirby\Exception\InvalidArgumentException; use Kirby\Exception\NotFoundException; use Kirby\Filesystem\Dir; -use Kirby\Filesystem\F; use Kirby\Http\Response; use Kirby\Http\Uri; use Kirby\Panel\Page as Panel; +use Kirby\Template\Template; use Kirby\Toolkit\A; +use Kirby\Toolkit\LazyValue; +use Kirby\Toolkit\Str; +use Throwable; /** * The `$page` object is the heart and @@ -27,141 +31,127 @@ use Kirby\Toolkit\A; */ class Page extends ModelWithContent { - use PageActions; - use PageSiblings; use HasChildren; use HasFiles; use HasMethods; use HasSiblings; + use PageActions; + use PageSiblings; public const CLASS_ALIAS = 'page'; /** * All registered page methods - * - * @var array + * @todo Remove when support for PHP 8.2 is dropped */ - public static $methods = []; + public static array $methods = []; /** * Registry with all Page models - * - * @var array */ - public static $models = []; + public static array $models = []; /** * The PageBlueprint object - * - * @var \Kirby\Cms\PageBlueprint */ - protected $blueprint; + protected PageBlueprint|null $blueprint = null; /** * Nesting level - * - * @var int */ - protected $depth; + protected int $depth; /** * Sorting number + slug - * - * @var string */ - protected $dirname; + protected string|null $dirname; /** * Path of dirnames - * - * @var string */ - protected $diruri; + protected string|null $diruri = null; /** * Draft status flag - * - * @var bool */ - protected $isDraft; + protected bool $isDraft; /** * The Page id - * - * @var string */ - protected $id; + protected string|null $id = null; /** * The template, that should be loaded * if it exists - * - * @var \Kirby\Template\Template */ - protected $intendedTemplate; + protected Template|null $intendedTemplate = null; - /** - * @var array - */ - protected $inventory; + protected array|null $inventory = null; /** * The sorting number - * - * @var int|null */ - protected $num; + protected int|null $num; /** * The parent page - * - * @var \Kirby\Cms\Page|null */ - protected $parent; + protected Page|null $parent; /** * Absolute path to the page directory - * - * @var string */ - protected $root; - - /** - * The parent Site object - * - * @var \Kirby\Cms\Site|null - */ - protected $site; + protected string|null $root; /** * The URL-appendix aka slug - * - * @var string */ - protected $slug; + protected string $slug; /** * The intended page template - * - * @var \Kirby\Template\Template */ - protected $template; + protected Template|null $template = null; /** * The page url - * - * @var string|null */ - protected $url; + protected string|null $url; + + /** + * Creates a new page object + */ + public function __construct(array $props) + { + if (isset($props['slug']) === false) { + throw new InvalidArgumentException('The page slug is required'); + } + + parent::__construct($props); + + $this->slug = $props['slug']; + // Sets the dirname manually, which works + // more reliable in connection with the inventory + // than computing the dirname afterwards + $this->dirname = $props['dirname'] ?? null; + $this->isDraft = $props['isDraft'] ?? false; + $this->num = $props['num'] ?? null; + $this->parent = $props['parent'] ?? null; + $this->root = $props['root'] ?? null; + + $this->setBlueprint($props['blueprint'] ?? null); + $this->setChildren($props['children'] ?? null); + $this->setDrafts($props['drafts'] ?? null); + $this->setFiles($props['files'] ?? null); + $this->setTemplate($props['template'] ?? null); + $this->setUrl($props['url'] ?? null); + } /** * Magic caller - * - * @param string $method - * @param array $arguments - * @return mixed */ - public function __call(string $method, array $arguments = []) + public function __call(string $method, array $arguments = []): mixed { // public property access if (isset($this->$method) === true) { @@ -177,24 +167,9 @@ class Page extends ModelWithContent return $this->content()->get($method); } - /** - * Creates a new page object - * - * @param array $props - */ - public function __construct(array $props) - { - // set the slug as the first property - $this->slug = $props['slug'] ?? null; - - // add all other properties - $this->setProperties($props); - } - /** * Improved `var_dump` output - * - * @return array + * @codeCoverageIgnore */ public function __debugInfo(): array { @@ -209,10 +184,7 @@ class Page extends ModelWithContent /** * Returns the url to the api endpoint - * * @internal - * @param bool $relative - * @return string */ public function apiUrl(bool $relative = false): string { @@ -225,23 +197,18 @@ class Page extends ModelWithContent /** * Returns the blueprint object - * - * @return \Kirby\Cms\PageBlueprint */ - public function blueprint() + public function blueprint(): PageBlueprint { - if ($this->blueprint instanceof PageBlueprint) { - return $this->blueprint; - } - - return $this->blueprint = PageBlueprint::factory('pages/' . $this->intendedTemplate(), 'pages/default', $this); + return $this->blueprint ??= PageBlueprint::factory( + 'pages/' . $this->intendedTemplate(), + 'pages/default', + $this + ); } /** * Returns an array with all blueprints that are available for the page - * - * @param string|null $inSection - * @return array */ public function blueprints(string|null $inSection = null): array { @@ -249,6 +216,10 @@ class Page extends ModelWithContent return $this->blueprint()->section($inSection)->blueprints(); } + if ($this->blueprints !== null) { + return $this->blueprints; + } + $blueprints = []; $templates = $this->blueprint()->changeTemplate() ?? $this->blueprint()->options()['changeTemplate'] ?? []; $currentTemplate = $this->intendedTemplate()->name(); @@ -278,14 +249,11 @@ class Page extends ModelWithContent } } - return array_values($blueprints); + return $this->blueprints = array_values($blueprints); } /** * Builds the cache id for the page - * - * @param string $contentType - * @return string */ protected function cacheId(string $contentType): string { @@ -302,14 +270,12 @@ class Page extends ModelWithContent /** * Prepares the content for the write method - * * @internal - * @param array $data - * @param string|null $languageCode - * @return array */ - public function contentFileData(array $data, string|null $languageCode = null): array - { + public function contentFileData( + array $data, + string|null $languageCode = null + ): array { return A::prepend($data, [ 'title' => $data['title'] ?? null, 'slug' => $data['slug'] ?? null @@ -319,68 +285,75 @@ class Page extends ModelWithContent /** * Returns the content text file * which is found by the inventory method - * * @internal - * @param string|null $languageCode - * @return string + * @deprecated 4.0.0 + * @todo Remove in v5 + * @codeCoverageIgnore */ public function contentFileName(string|null $languageCode = null): string { + Helpers::deprecated('The internal $model->contentFileName() method has been deprecated. Please let us know via a GitHub issue if you need this method and tell us your use case.', 'model-content-file'); return $this->intendedTemplate()->name(); } /** * Call the page controller - * * @internal - * @param array $data - * @param string $contentType - * @return array + * * @throws \Kirby\Exception\InvalidArgumentException If the controller returns invalid objects for `kirby`, `site`, `pages` or `page` */ - public function controller(array $data = [], string $contentType = 'html'): array - { + public function controller( + array $data = [], + string $contentType = 'html' + ): array { // create the template data $data = array_merge($data, [ 'kirby' => $kirby = $this->kirby(), 'site' => $site = $this->site(), - 'pages' => $site->children(), - 'page' => $site->visit($this) + 'pages' => new LazyValue(fn () => $site->children()), + 'page' => new LazyValue(fn () => $site->visit($this)) ]); // call the template controller if there's one. - $controllerData = $kirby->controller($this->template()->name(), $data, $contentType); + $controllerData = $kirby->controller( + $this->template()->name(), + $data, + $contentType + ); // merge controller data with original data safely + // to provide original data to template even if + // it wasn't returned by the controller explicitly if (empty($controllerData) === false) { $classes = [ - 'kirby' => 'Kirby\Cms\App', - 'site' => 'Kirby\Cms\Site', - 'pages' => 'Kirby\Cms\Pages', - 'page' => 'Kirby\Cms\Page' + 'kirby' => App::class, + 'site' => Site::class, + 'pages' => Pages::class, + 'page' => Page::class ]; foreach ($controllerData as $key => $value) { - if (array_key_exists($key, $classes) === true) { - if ($value instanceof $classes[$key]) { - $data[$key] = $value; - } else { - throw new InvalidArgumentException('The returned variable "' . $key . '" from the controller "' . $this->template()->name() . '" is not of the required type "' . $classes[$key] . '"'); - } - } else { - $data[$key] = $value; - } + $data[$key] = match (true) { + // original data wasn't overwritten + array_key_exists($key, $classes) === false => $value, + // original data was overwritten, but matches expected type + $value instanceof $classes[$key] => $value, + // throw error if data was overwritten with wrong type + default => throw new InvalidArgumentException('The returned variable "' . $key . '" from the controller "' . $this->template()->name() . '" is not of the required type "' . $classes[$key] . '"') + }; } } + // unwrap remaining lazy values in data + // (happens if the controller didn't override an original lazy Kirby object) + $data = LazyValue::unwrap($data); + return $data; } /** * Returns a number indicating how deep the page * is nested within the content folder - * - * @return int */ public function depth(): int { @@ -389,8 +362,6 @@ class Page extends ModelWithContent /** * Sorting number + Slug - * - * @return string */ public function dirname(): string { @@ -407,8 +378,6 @@ class Page extends ModelWithContent /** * Sorting number + Slug - * - * @return string */ public function diruri(): string { @@ -431,8 +400,6 @@ class Page extends ModelWithContent /** * Checks if the page exists on disk - * - * @return bool */ public function exists(): bool { @@ -442,18 +409,11 @@ class Page extends ModelWithContent /** * Constructs a Page object and also * takes page models into account. - * * @internal - * @param mixed $props - * @return static */ - public static function factory($props) + public static function factory($props): static { - if (empty($props['model']) === false) { - return static::model($props['model'], $props); - } - - return new static($props); + return static::model($props['model'] ?? 'default', $props); } /** @@ -465,7 +425,7 @@ class Page extends ModelWithContent * @param array $options Options for `Kirby\Http\Uri` to create URL parts * @param int $code HTTP status code */ - public function go(array $options = [], int $code = 302) + public function go(array $options = [], int $code = 302): void { Response::go($this->url($options), $code); } @@ -473,8 +433,6 @@ class Page extends ModelWithContent /** * Checks if the intended template * for the page exists. - * - * @return bool */ public function hasTemplate(): bool { @@ -483,8 +441,6 @@ class Page extends ModelWithContent /** * Returns the Page Id - * - * @return string */ public function id(): string { @@ -503,10 +459,8 @@ class Page extends ModelWithContent /** * Returns the template that should be * loaded if it exists. - * - * @return \Kirby\Template\Template */ - public function intendedTemplate() + public function intendedTemplate(): Template { if ($this->intendedTemplate !== null) { return $this->intendedTemplate; @@ -518,9 +472,7 @@ class Page extends ModelWithContent /** * Returns the inventory of files * children and content files - * * @internal - * @return array */ public function inventory(): array { @@ -542,7 +494,6 @@ class Page extends ModelWithContent * Compares the current object with the given page object * * @param \Kirby\Cms\Page|string $page - * @return bool */ public function is($page): bool { @@ -562,24 +513,35 @@ class Page extends ModelWithContent } /** - * Checks if the page is the current page - * - * @return bool + * Checks if the page is accessible that accessible and listable. + * This permission depends on the `read` option until v5 */ - public function isActive(): bool + public function isAccessible(): bool { - if ($this->site()->page()?->is($this) === true) { - return true; + // TODO: remove this check when `read` option deprecated in v5 + if ($this->isReadable() === false) { + return false; } - return false; + static $accessible = []; + $role = $this->kirby()->user()?->role()->id() ?? '__none__'; + $template = $this->intendedTemplate()->name(); + $accessible[$role] ??= []; + + return $accessible[$role][$template] ??= $this->permissions()->can('access'); } /** - * Checks if the page is a direct or indirect ancestor of the given $page object - * - * @param Page $child - * @return bool + * Checks if the page is the current page + */ + public function isActive(): bool + { + return $this->site()->page()?->is($this) === true; + } + + /** + * Checks if the page is a direct or indirect ancestor + * of the given $page object */ public function isAncestorOf(Page $child): bool { @@ -590,8 +552,6 @@ class Page extends ModelWithContent * Checks if the page can be cached in the * pages cache. This will also check if one * of the ignore rules from the config kick in. - * - * @return bool */ public function isCacheable(): bool { @@ -644,7 +604,6 @@ class Page extends ModelWithContent * Checks if the page is a child of the given page * * @param \Kirby\Cms\Page|string $parent - * @return bool */ public function isChildOf($parent): bool { @@ -655,7 +614,6 @@ class Page extends ModelWithContent * Checks if the page is a descendant of the given page * * @param \Kirby\Cms\Page|string $parent - * @return bool */ public function isDescendantOf($parent): bool { @@ -672,8 +630,6 @@ class Page extends ModelWithContent /** * Checks if the page is a descendant of the currently active page - * - * @return bool */ public function isDescendantOfActive(): bool { @@ -686,8 +642,6 @@ class Page extends ModelWithContent /** * Checks if the current page is a draft - * - * @return bool */ public function isDraft(): bool { @@ -696,8 +650,6 @@ class Page extends ModelWithContent /** * Checks if the page is the error page - * - * @return bool */ public function isErrorPage(): bool { @@ -706,8 +658,6 @@ class Page extends ModelWithContent /** * Checks if the page is the home page - * - * @return bool */ public function isHomePage(): bool { @@ -718,30 +668,57 @@ class Page extends ModelWithContent * It's often required to check for the * home and error page to stop certain * actions. That's why there's a shortcut. - * - * @return bool */ public function isHomeOrErrorPage(): bool { return $this->isHomePage() === true || $this->isErrorPage() === true; } + /** + * Check if the page can be listable by the current user + * This permission depends on the `read` option until v5 + */ + public function isListable(): bool + { + // TODO: remove this check when `read` option deprecated in v5 + if ($this->isReadable() === false) { + return false; + } + + // not accessible also means not listable + if ($this->isAccessible() === false) { + return false; + } + + static $listable = []; + $role = $this->kirby()->user()?->role()->id() ?? '__none__'; + $template = $this->intendedTemplate()->name(); + $listable[$role] ??= []; + + return $listable[$role][$template] ??= $this->permissions()->can('list'); + } + /** * Checks if the page has a sorting number - * - * @return bool */ public function isListed(): bool { return $this->isPublished() && $this->num() !== null; } + public function isMovableTo(Page|Site $parent): bool + { + try { + return PageRules::move($this, $parent); + } catch (Throwable) { + return false; + } + } + /** * Checks if the page is open. * Open pages are either the current one * or descendants of the current one. - * - * @return bool */ public function isOpen(): bool { @@ -758,8 +735,6 @@ class Page extends ModelWithContent /** * Checks if the page is not a draft. - * - * @return bool */ public function isPublished(): bool { @@ -768,22 +743,20 @@ class Page extends ModelWithContent /** * Check if the page can be read by the current user - * - * @return bool + * @todo Deprecate `read` option in v5 and make the necessary changes for `access` and `list` options. */ public function isReadable(): bool { - static $readable = []; + static $readable = []; + $role = $this->kirby()->user()?->role()->id() ?? '__none__'; + $template = $this->intendedTemplate()->name(); + $readable[$role] ??= []; - $template = $this->intendedTemplate()->name(); - - return $readable[$template] ??= $this->permissions()->can('read'); + return $readable[$role][$template] ??= $this->permissions()->can('read'); } /** * Checks if the page is sortable - * - * @return bool */ public function isSortable(): bool { @@ -792,8 +765,6 @@ class Page extends ModelWithContent /** * Checks if the page has no sorting number - * - * @return bool */ public function isUnlisted(): bool { @@ -803,12 +774,9 @@ class Page extends ModelWithContent /** * Checks if the page access is verified. * This is only used for drafts so far. - * * @internal - * @param string|null $token - * @return bool */ - public function isVerified(string $token = null) + public function isVerified(string|null $token = null): bool { if ( $this->isPublished() === true && @@ -826,9 +794,7 @@ class Page extends ModelWithContent /** * Returns the root to the media folder for the page - * * @internal - * @return string */ public function mediaRoot(): string { @@ -837,9 +803,7 @@ class Page extends ModelWithContent /** * The page's base URL for any files - * * @internal - * @return string */ public function mediaUrl(): string { @@ -848,15 +812,14 @@ class Page extends ModelWithContent /** * Creates a page model if it has been registered - * * @internal - * @param string $name - * @param array $props - * @return static */ - public static function model(string $name, array $props = []) + public static function model(string $name, array $props = []): static { - if ($class = (static::$models[$name] ?? null)) { + $class = static::$models[$name] ?? null; + $class ??= static::$models['default'] ?? null; + + if ($class !== null) { $object = new $class($props); if ($object instanceof self) { @@ -869,25 +832,28 @@ class Page extends ModelWithContent /** * Returns the last modification date of the page - * - * @param string|null $format - * @param string|null $handler - * @param string|null $languageCode - * @return int|string */ - public function modified(string $format = null, string $handler = null, string $languageCode = null) - { - return F::modified( - $this->contentFile($languageCode), - $format, - $handler ?? $this->kirby()->option('date.handler', 'date') + public function modified( + string|null $format = null, + string|null $handler = null, + string|null $languageCode = null + ): int|string|false|null { + $identifier = $this->isDraft() === true ? 'changes' : 'published'; + + $modified = $this->storage()->modified( + $identifier, + $languageCode ); + + if ($modified === null) { + return null; + } + + return Str::date($modified, $format, $handler); } /** * Returns the sorting number - * - * @return int|null */ public function num(): int|null { @@ -896,29 +862,23 @@ class Page extends ModelWithContent /** * Returns the panel info object - * - * @return \Kirby\Panel\Page */ - public function panel() + public function panel(): Panel { return new Panel($this); } /** * Returns the parent Page object - * - * @return \Kirby\Cms\Page|null */ - public function parent() + public function parent(): Page|null { return $this->parent; } /** * Returns the parent id, if a parent exists - * * @internal - * @return string|null */ public function parentId(): string|null { @@ -929,21 +889,17 @@ class Page extends ModelWithContent * Returns the parent model, * which can either be another Page * or the Site - * * @internal - * @return \Kirby\Cms\Page|\Kirby\Cms\Site */ - public function parentModel() + public function parentModel(): Page|Site { return $this->parent() ?? $this->site(); } /** * Returns a list of all parents and their parents recursively - * - * @return \Kirby\Cms\Pages */ - public function parents() + public function parents(): Pages { $parents = new Pages(); $page = $this->parent(); @@ -967,19 +923,15 @@ class Page extends ModelWithContent /** * Returns the permissions object for this page - * - * @return \Kirby\Cms\PagePermissions */ - public function permissions() + public function permissions(): PagePermissions { return new PagePermissions($this); } /** * Draft preview Url - * * @internal - * @return string|null */ public function previewUrl(): string|null { @@ -989,11 +941,10 @@ class Page extends ModelWithContent return null; } - if ($preview === true) { - $url = $this->url(); - } else { - $url = $preview; - } + $url = match ($preview) { + true => $this->url(), + default => $preview + }; if ($this->isDraft() === true) { $uri = new Uri($url); @@ -1012,9 +963,7 @@ class Page extends ModelWithContent * render a content representation instead of * the default template. * - * @param array $data * @param string $contentType - * @return string * @throws \Kirby\Exception\NotFoundException If the default template cannot be found */ public function render(array $data = [], $contentType = 'html'): string @@ -1095,11 +1044,9 @@ class Page extends ModelWithContent /** * @internal - * @param mixed $type - * @return \Kirby\Template\Template * @throws \Kirby\Exception\NotFoundException If the content representation cannot be found */ - public function representation($type) + public function representation(mixed $type): Template { $kirby = $this->kirby(); $template = $this->template(); @@ -1115,8 +1062,6 @@ class Page extends ModelWithContent /** * Returns the absolute root to the page directory * No matter if it exists or not. - * - * @return string */ public function root(): string { @@ -1127,22 +1072,16 @@ class Page extends ModelWithContent * Returns the PageRules class instance * which is being used in various methods * to check for valid actions and input. - * - * @return \Kirby\Cms\PageRules */ - protected function rules() + protected function rules(): PageRules { return new PageRules(); } /** * Search all pages within the current page - * - * @param string|null $query - * @param array $params - * @return \Kirby\Cms\Pages */ - public function search(string $query = null, $params = []) + public function search(string|null $query = null, string|array $params = []): Pages { return $this->index()->search($query, $params); } @@ -1150,10 +1089,9 @@ class Page extends ModelWithContent /** * Sets the Blueprint object * - * @param array|null $blueprint * @return $this */ - protected function setBlueprint(array $blueprint = null) + protected function setBlueprint(array|null $blueprint = null): static { if ($blueprint !== null) { $blueprint['model'] = $this; @@ -1163,87 +1101,12 @@ class Page extends ModelWithContent return $this; } - /** - * Sets the dirname manually, which works - * more reliable in connection with the inventory - * than computing the dirname afterwards - * - * @param string|null $dirname - * @return $this - */ - protected function setDirname(string $dirname = null) - { - $this->dirname = $dirname; - return $this; - } - - /** - * Sets the draft flag - * - * @param bool $isDraft - * @return $this - */ - protected function setIsDraft(bool $isDraft = null) - { - $this->isDraft = $isDraft ?? false; - return $this; - } - - /** - * Sets the sorting number - * - * @param int|null $num - * @return $this - */ - protected function setNum(int $num = null) - { - $this->num = $num === null ? $num : (int)$num; - return $this; - } - - /** - * Sets the parent page object - * - * @param \Kirby\Cms\Page|null $parent - * @return $this - */ - protected function setParent(Page $parent = null) - { - $this->parent = $parent; - return $this; - } - - /** - * Sets the absolute path to the page - * - * @param string|null $root - * @return $this - */ - protected function setRoot(string $root = null) - { - $this->root = $root; - return $this; - } - - /** - * Sets the required Page slug - * - * @param string $slug - * @return $this - */ - protected function setSlug(string $slug) - { - $this->slug = $slug; - return $this; - } - /** * Sets the intended template * - * @param string|null $template * @return $this */ - protected function setTemplate(string $template = null) + protected function setTemplate(string|null $template = null): static { if ($template !== null) { $this->intendedTemplate = $this->kirby()->template($template); @@ -1255,10 +1118,9 @@ class Page extends ModelWithContent /** * Sets the Url * - * @param string|null $url * @return $this */ - protected function setUrl(string $url = null) + protected function setUrl(string|null $url = null): static { if (is_string($url) === true) { $url = rtrim($url, '/'); @@ -1270,11 +1132,8 @@ class Page extends ModelWithContent /** * Returns the slug of the page - * - * @param string|null $languageCode - * @return string */ - public function slug(string $languageCode = null): string + public function slug(string|null $languageCode = null): string { if ($this->kirby()->multilang() === true) { $languageCode ??= $this->kirby()->languageCode(); @@ -1294,8 +1153,6 @@ class Page extends ModelWithContent /** * Returns the page status, which * can be `draft`, `listed` or `unlisted` - * - * @return string */ public function status(): string { @@ -1312,10 +1169,8 @@ class Page extends ModelWithContent /** * Returns the final template - * - * @return \Kirby\Template\Template */ - public function template() + public function template(): Template { if ($this->template !== null) { return $this->template; @@ -1332,10 +1187,8 @@ class Page extends ModelWithContent /** * Returns the title field or the slug as fallback - * - * @return \Kirby\Cms\Field */ - public function title() + public function title(): Field { return $this->content()->get('title')->or($this->slug()); } @@ -1343,38 +1196,35 @@ class Page extends ModelWithContent /** * Converts the most important * properties to array - * - * @return array */ public function toArray(): array { - return [ - 'children' => $this->children()->keys(), - 'content' => $this->content()->toArray(), - 'files' => $this->files()->keys(), - 'id' => $this->id(), - 'mediaUrl' => $this->mediaUrl(), - 'mediaRoot' => $this->mediaRoot(), - 'num' => $this->num(), - 'parent' => $this->parent() ? $this->parent()->id() : null, - 'slug' => $this->slug(), - 'template' => $this->template(), - 'translations' => $this->translations()->toArray(), - 'uid' => $this->uid(), - 'uri' => $this->uri(), - 'url' => $this->url() - ]; + return array_merge(parent::toArray(), [ + 'children' => $this->children()->keys(), + 'files' => $this->files()->keys(), + 'id' => $this->id(), + 'mediaUrl' => $this->mediaUrl(), + 'mediaRoot' => $this->mediaRoot(), + 'num' => $this->num(), + 'parent' => $this->parent()?->id(), + 'slug' => $this->slug(), + 'template' => $this->template(), + 'uid' => $this->uid(), + 'uri' => $this->uri(), + 'url' => $this->url() + ]); } /** * Returns a verification token, which * is used for the draft authentication - * - * @return string */ protected function token(): string { - return $this->kirby()->contentToken($this, $this->id() . $this->template()); + return $this->kirby()->contentToken( + $this, + $this->id() . $this->template() + ); } /** @@ -1385,7 +1235,6 @@ class Page extends ModelWithContent * can be translated. * * @see self::slug() - * @return string */ public function uid(): string { @@ -1395,11 +1244,8 @@ class Page extends ModelWithContent /** * The uri is the same as the id, except * that it will be translated in multi-language setups - * - * @param string|null $languageCode - * @return string */ - public function uri(string $languageCode = null): string + public function uri(string|null $languageCode = null): string { // set the id, depending on the parent if ($parent = $this->parent()) { @@ -1413,7 +1259,6 @@ class Page extends ModelWithContent * Returns the Url * * @param array|string|null $options - * @return string */ public function url($options = null): string { @@ -1453,11 +1298,11 @@ class Page extends ModelWithContent * * @internal * @param string|null $language - * @param array|null $options - * @return string */ - public function urlForLanguage($language = null, array $options = null): string - { + public function urlForLanguage( + $language = null, + array|null $options = null + ): string { if ($options !== null) { return Url::to($this->urlForLanguage($language), $options); } diff --git a/kirby/src/Cms/PageActions.php b/kirby/src/Cms/PageActions.php index ccea50e..4e27018 100644 --- a/kirby/src/Cms/PageActions.php +++ b/kirby/src/Cms/PageActions.php @@ -8,7 +8,6 @@ use Kirby\Exception\InvalidArgumentException; use Kirby\Exception\LogicException; use Kirby\Exception\NotFoundException; use Kirby\Filesystem\Dir; -use Kirby\Filesystem\F; use Kirby\Form\Form; use Kirby\Toolkit\A; use Kirby\Toolkit\I18n; @@ -30,6 +29,7 @@ trait PageActions /** * Adapts necessary modifications which page uuid, page slug and files uuid * of copy objects for single or multilang environments + * @internal */ protected function adaptCopy(Page $copy, bool $files = false, bool $children = false): Page { @@ -103,11 +103,10 @@ trait PageActions * This only affects this page, * siblings will not be resorted. * - * @param int|null $num * @return $this|static * @throws \Kirby\Exception\LogicException If a draft is being sorted or the directory cannot be moved */ - public function changeNum(int $num = null) + public function changeNum(int|null $num = null): static { if ($this->isDraft() === true) { throw new LogicException('Drafts cannot change their sorting number'); @@ -130,7 +129,7 @@ trait PageActions if (Dir::move($oldPage->root(), $newPage->root()) === true) { // Updates the root path of the old page with the root path // of the moved new page to use fly actions on old page in loop - $oldPage->setRoot($newPage->root()); + $oldPage->root = $newPage->root(); } else { throw new LogicException('The page directory cannot be moved'); } @@ -146,15 +145,15 @@ trait PageActions /** * Changes the slug/uid of the page * - * @param string $slug - * @param string|null $languageCode * @return $this|static * @throws \Kirby\Exception\LogicException If the directory cannot be moved */ - public function changeSlug(string $slug, string $languageCode = null) - { + public function changeSlug( + string $slug, + string|null $languageCode = null + ): static { // always sanitize the slug - $slug = Str::slug($slug); + $slug = Url::slug($slug); // in multi-language installations the slug for the non-default // languages is stored in the text file. The changeSlugForLanguage @@ -205,14 +204,13 @@ trait PageActions /** * Change the slug for a specific language * - * @param string $slug - * @param string|null $languageCode - * @return static * @throws \Kirby\Exception\NotFoundException If the language for the given language code cannot be found * @throws \Kirby\Exception\InvalidArgumentException If the slug for the default language is being changed */ - protected function changeSlugForLanguage(string $slug, string $languageCode = null) - { + protected function changeSlugForLanguage( + string $slug, + string|null $languageCode = null + ): static { $language = $this->kirby()->language($languageCode); if (!$language) { @@ -247,10 +245,9 @@ trait PageActions * * @param string $status "draft", "listed" or "unlisted" * @param int|null $position Optional sorting number - * @return static * @throws \Kirby\Exception\InvalidArgumentException If an invalid status is being passed */ - public function changeStatus(string $status, int $position = null) + public function changeStatus(string $status, int|null $position = null): static { return match ($status) { 'draft' => $this->changeStatusToDraft(), @@ -260,10 +257,7 @@ trait PageActions }; } - /** - * @return static - */ - protected function changeStatusToDraft() + protected function changeStatusToDraft(): static { $arguments = ['page' => $this, 'status' => 'draft', 'position' => null]; $page = $this->commit( @@ -276,10 +270,9 @@ trait PageActions } /** - * @param int|null $position * @return $this|static */ - protected function changeStatusToListed(int $position = null) + protected function changeStatusToListed(int|null $position = null): static { // create a sorting number for the page $num = $this->createNum($position); @@ -289,10 +282,16 @@ trait PageActions return $this; } - $arguments = ['page' => $this, 'status' => 'listed', 'position' => $num]; - $page = $this->commit('changeStatus', $arguments, function ($page, $status, $position) { - return $page->publish()->changeNum($position); - }); + $page = $this->commit( + 'changeStatus', + [ + 'page' => $this, + 'status' => 'listed', + 'position' => $num + ], + fn ($page, $status, $position) => + $page->publish()->changeNum($position) + ); if ($this->blueprint()->num() === 'default') { $page->resortSiblingsAfterListing($num); @@ -304,16 +303,21 @@ trait PageActions /** * @return $this|static */ - protected function changeStatusToUnlisted() + protected function changeStatusToUnlisted(): static { if ($this->status() === 'unlisted') { return $this; } - $arguments = ['page' => $this, 'status' => 'unlisted', 'position' => null]; - $page = $this->commit('changeStatus', $arguments, function ($page) { - return $page->publish()->changeNum(null); - }); + $page = $this->commit( + 'changeStatus', + [ + 'page' => $this, + 'status' => 'unlisted', + 'position' => null + ], + fn ($page) => $page->publish()->changeNum(null) + ); $this->resortSiblingsAfterUnlisting(); @@ -325,10 +329,9 @@ trait PageActions * collection. Siblings will be resorted. If the page * status isn't yet `listed`, it will be changed to it. * - * @param int|null $position * @return $this|static */ - public function changeSort(int $position = null) + public function changeSort(int|null $position = null): static { return $this->changeStatus('listed', $position); } @@ -336,51 +339,18 @@ trait PageActions /** * Changes the page template * - * @param string $template * @return $this|static * @throws \Kirby\Exception\LogicException If the textfile cannot be renamed/moved */ - public function changeTemplate(string $template) + public function changeTemplate(string $template): static { if ($template === $this->intendedTemplate()->name()) { return $this; } return $this->commit('changeTemplate', ['page' => $this, 'template' => $template], function ($oldPage, $template) { - if ($this->kirby()->multilang() === true) { - $newPage = $this->clone([ - 'template' => $template - ]); - - foreach ($this->kirby()->languages()->codes() as $code) { - if ($oldPage->translation($code)->exists() !== true) { - continue; - } - - $content = $oldPage->content($code)->convertTo($template); - - if (F::remove($oldPage->contentFile($code)) !== true) { - throw new LogicException('The old text file could not be removed'); - } - - // save the language file - $newPage->save($content, $code); - } - - // return a fresh copy of the object - $page = $newPage->clone(); - } else { - $newPage = $this->clone([ - 'content' => $this->content()->convertTo($template), - 'template' => $template - ]); - - if (F::remove($oldPage->contentFile()) !== true) { - throw new LogicException('The old text file could not be removed'); - } - - $page = $newPage->save(); - } + // convert for new template/blueprint + $page = $oldPage->convertTo($template); // update the parent collection static::updateParentCollections($page, 'set'); @@ -391,14 +361,24 @@ trait PageActions /** * Change the page title - * - * @param string $title - * @param string|null $languageCode - * @return static */ - public function changeTitle(string $title, string $languageCode = null) - { + public function changeTitle( + string $title, + string|null $languageCode = null + ): static { + // if the `$languageCode` argument is not set and is not the default language + // the `$languageCode` argument is sent as the current language + if ( + $languageCode === null && + $language = $this->kirby()->language() + ) { + if ($language->isDefault() === false) { + $languageCode = $language->code(); + } + } + $arguments = ['page' => $this, 'title' => $title, 'languageCode' => $languageCode]; + return $this->commit('changeTitle', $arguments, function ($page, $title, $languageCode) { $page = $page->save(['title' => $title], $languageCode); @@ -417,14 +397,12 @@ trait PageActions * 3. commits the store action * 4. sends the after hook * 5. returns the result - * - * @param string $action - * @param array $arguments - * @param \Closure $callback - * @return mixed */ - protected function commit(string $action, array $arguments, Closure $callback) - { + protected function commit( + string $action, + array $arguments, + Closure $callback + ): mixed { $old = $this->hardcopy(); $kirby = $this->kirby(); $argumentValues = array_values($arguments); @@ -452,11 +430,11 @@ trait PageActions /** * Copies the page to a new parent * - * @param array $options - * @return \Kirby\Cms\Page * @throws \Kirby\Exception\DuplicateException If the page already exists + * + * @internal */ - public function copy(array $options = []) + public function copy(array $options = []): static { $slug = $options['slug'] ?? $this->slug(); $isDraft = $options['isDraft'] ?? $this->isDraft(); @@ -467,7 +445,7 @@ trait PageActions $files = $options['files'] ?? false; // clean up the slug - $slug = Str::slug($slug); + $slug = Url::slug($slug); if ($parentModel->findPageOrDraft($slug)) { throw new DuplicateException([ @@ -495,7 +473,8 @@ trait PageActions $ignore[] = $file->root(); // append all content files - array_push($ignore, ...$file->contentFiles()); + array_push($ignore, ...$file->storage()->contentFiles('published')); + array_push($ignore, ...$file->storage()->contentFiles('changes')); } } @@ -514,14 +493,11 @@ trait PageActions /** * Creates and stores a new page - * - * @param array $props - * @return static */ - public static function create(array $props) + public static function create(array $props): Page { // clean up the slug - $props['slug'] = Str::slug($props['slug'] ?? $props['content']['title'] ?? null); + $props['slug'] = Url::slug($props['slug'] ?? $props['content']['title'] ?? null); $props['template'] = $props['model'] = strtolower($props['template'] ?? 'default'); $props['isDraft'] ??= $props['draft'] ?? true; @@ -584,11 +560,8 @@ trait PageActions /** * Creates a child of the current page - * - * @param array $props - * @return static */ - public function createChild(array $props) + public function createChild(array $props): Page { $props = array_merge($props, [ 'url' => null, @@ -597,18 +570,15 @@ trait PageActions 'site' => $this->site(), ]); - $modelClass = Page::$models[$props['template']] ?? Page::class; + $modelClass = Page::$models[$props['template'] ?? null] ?? Page::class; return $modelClass::create($props); } /** * Create the sorting number for the page * depending on the blueprint settings - * - * @param int|null $num - * @return int */ - public function createNum(int $num = null): int + public function createNum(int|null $num = null): int { $mode = $this->blueprint()->num(); @@ -664,9 +634,6 @@ trait PageActions /** * Deletes the page - * - * @param bool $force - * @return bool */ public function delete(bool $force = false): bool { @@ -716,15 +683,11 @@ trait PageActions /** * Duplicates the page with the given * slug and optionally copies all files - * - * @param string|null $slug - * @param array $options - * @return \Kirby\Cms\Page */ - public function duplicate(string $slug = null, array $options = []) + public function duplicate(string|null $slug = null, array $options = []): static { // create the slug for the duplicate - $slug = Str::slug($slug ?? $this->slug() . '-' . Str::slug(I18n::translate('page.duplicate.appendix'))); + $slug = Url::slug($slug ?? $this->slug() . '-' . Url::slug(I18n::translate('page.duplicate.appendix'))); $arguments = [ 'originalPage' => $this, @@ -749,11 +712,61 @@ trait PageActions }); } + /** + * Moves the page to a new parent if the + * new parent accepts the page type + */ + public function move(Site|Page $parent): Page + { + // nothing to move + if ($this->parentModel()->is($parent) === true) { + return $this; + } + + $arguments = [ + 'page' => $this, + 'parent' => $parent + ]; + + return $this->commit('move', $arguments, function ($page, $parent) { + // remove the uuid cache for this page + $page->uuid()?->clear(true); + + // move drafts into the drafts folder of the parent + if ($page->isDraft() === true) { + $newRoot = $parent->root() . '/_drafts/' . $page->dirname(); + } else { + $newRoot = $parent->root() . '/' . $page->dirname(); + } + + // try to move the page directory on disk + if (Dir::move($page->root(), $newRoot) !== true) { + throw new LogicException([ + 'key' => 'page.move.directory' + ]); + } + + // flush all collection caches to be sure that + // the new child is included afterwards + $parent->purge(); + + // double-check if the new child can actually be found + if (!$newPage = $parent->childrenAndDrafts()->find($page->slug())) { + throw new LogicException([ + 'key' => 'page.move.notFound' + ]); + } + + return $newPage; + }); + } + /** * @return $this|static * @throws \Kirby\Exception\LogicException If the folder cannot be moved + * @internal */ - public function publish() + public function publish(): static { if ($this->isDraft() === false) { return $this; @@ -794,28 +807,27 @@ trait PageActions /** * Clean internal caches + * * @return $this */ - public function purge() + public function purge(): static { + parent::purge(); + $this->blueprint = null; $this->children = null; $this->childrenAndDrafts = null; - $this->content = null; $this->drafts = null; $this->files = null; $this->inventory = null; - $this->translations = null; return $this; } /** - * @param int|null $position - * @return bool * @throws \Kirby\Exception\LogicException If the page is not included in the siblings collection */ - protected function resortSiblingsAfterListing(int $position = null): bool + protected function resortSiblingsAfterListing(int|null $position = null): bool { // get all siblings including the current page $siblings = $this @@ -859,7 +871,7 @@ trait PageActions } /** - * @return bool + * @internal */ public function resortSiblingsAfterUnlisting(): bool { @@ -886,15 +898,13 @@ trait PageActions /** * Stores the content on disk - * * @internal - * @param array|null $data - * @param string|null $languageCode - * @param bool $overwrite - * @return static */ - public function save(array $data = null, string $languageCode = null, bool $overwrite = false) - { + public function save( + array|null $data = null, + string|null $languageCode = null, + bool $overwrite = false + ): static { $page = parent::save($data, $languageCode, $overwrite); // overwrite the updated page in the parent collection @@ -906,11 +916,12 @@ trait PageActions /** * Convert a page from listed or * unlisted to draft. + * @internal * * @return $this|static * @throws \Kirby\Exception\LogicException If the folder cannot be moved */ - public function unpublish() + public function unpublish(): static { if ($this->isDraft() === true) { return $this; @@ -947,14 +958,12 @@ trait PageActions /** * Updates the page data - * - * @param array|null $input - * @param string|null $languageCode - * @param bool $validate - * @return static */ - public function update(array $input = null, string $languageCode = null, bool $validate = false) - { + public function update( + array|null $input = null, + string|null $languageCode = null, + bool $validate = false + ): static { if ($this->isDraft() === true) { $validate = false; } @@ -982,10 +991,12 @@ trait PageActions * @param \Kirby\Cms\Page $page * @param string $method Method to call on the parent collections * @param \Kirby\Cms\Page|null $parentMdel - * @return void */ - protected static function updateParentCollections($page, string $method, $parentModel = null): void - { + protected static function updateParentCollections( + $page, + string $method, + $parentModel = null + ): void { $parentModel ??= $page->parentModel(); // method arguments depending on the called method diff --git a/kirby/src/Cms/PageBlueprint.php b/kirby/src/Cms/PageBlueprint.php index 12843d3..6015fbb 100644 --- a/kirby/src/Cms/PageBlueprint.php +++ b/kirby/src/Cms/PageBlueprint.php @@ -16,8 +16,6 @@ class PageBlueprint extends Blueprint /** * Creates a new page blueprint object * with the given props - * - * @param array $props */ public function __construct(array $props) { @@ -28,6 +26,7 @@ class PageBlueprint extends Blueprint $this->props['options'] ?? true, // defaults [ + 'access' => null, 'changeSlug' => null, 'changeStatus' => null, 'changeTemplate' => null, @@ -35,8 +34,10 @@ class PageBlueprint extends Blueprint 'create' => null, 'delete' => null, 'duplicate' => null, - 'read' => null, + 'list' => null, + 'move' => null, 'preview' => null, + 'read' => null, 'sort' => null, 'update' => null, ], @@ -58,8 +59,6 @@ class PageBlueprint extends Blueprint /** * Returns the page numbering mode - * - * @return string */ public function num(): string { @@ -70,7 +69,6 @@ class PageBlueprint extends Blueprint * Normalizes the ordering number * * @param mixed $num - * @return string */ protected function normalizeNum($num): string { @@ -86,7 +84,6 @@ class PageBlueprint extends Blueprint * Normalizes the available status options for the page * * @param mixed $status - * @return array */ protected function normalizeStatus($status): array { @@ -163,8 +160,6 @@ class PageBlueprint extends Blueprint /** * Returns the options object * that handles page options and permissions - * - * @return array */ public function options(): array { @@ -176,10 +171,8 @@ class PageBlueprint extends Blueprint * The preview setting controls the "Open" * button in the panel and redirects it to a * different URL if necessary. - * - * @return string|bool */ - public function preview() + public function preview(): string|bool { $preview = $this->props['options']['preview'] ?? true; @@ -187,13 +180,11 @@ class PageBlueprint extends Blueprint return $this->model->toString($preview); } - return $preview; + return $this->model->permissions()->can('preview', true); } /** * Returns the status array - * - * @return array */ public function status(): array { diff --git a/kirby/src/Cms/PagePermissions.php b/kirby/src/Cms/PagePermissions.php index fbc69a9..b4ca118 100644 --- a/kirby/src/Cms/PagePermissions.php +++ b/kirby/src/Cms/PagePermissions.php @@ -13,30 +13,18 @@ namespace Kirby\Cms; */ class PagePermissions extends ModelPermissions { - /** - * @var string - */ - protected $category = 'pages'; + protected string $category = 'pages'; - /** - * @return bool - */ protected function canChangeSlug(): bool { return $this->model->isHomeOrErrorPage() !== true; } - /** - * @return bool - */ protected function canChangeStatus(): bool { return $this->model->isErrorPage() !== true; } - /** - * @return bool - */ protected function canChangeTemplate(): bool { if ($this->model->isErrorPage() === true) { @@ -50,17 +38,16 @@ class PagePermissions extends ModelPermissions return true; } - /** - * @return bool - */ protected function canDelete(): bool { return $this->model->isHomeOrErrorPage() !== true; } - /** - * @return bool - */ + protected function canMove(): bool + { + return $this->model->isHomeOrErrorPage() !== true; + } + protected function canSort(): bool { if ($this->model->isErrorPage() === true) { diff --git a/kirby/src/Cms/PagePicker.php b/kirby/src/Cms/PagePicker.php index a8e38de..e51464a 100644 --- a/kirby/src/Cms/PagePicker.php +++ b/kirby/src/Cms/PagePicker.php @@ -18,25 +18,14 @@ use Kirby\Exception\InvalidArgumentException; */ class PagePicker extends Picker { - /** - * @var \Kirby\Cms\Pages - */ - protected $items; - - /** - * @var \Kirby\Cms\Pages - */ - protected $itemsForQuery; - - /** - * @var \Kirby\Cms\Page|\Kirby\Cms\Site|null - */ - protected $parent; + // TODO: null only due to our Properties setters, + // remove once our implementation is better + protected Pages|null $items = null; + protected Pages|null $itemsForQuery = null; + protected Page|Site|null $parent = null; /** * Extends the basic defaults - * - * @return array */ public function defaults(): array { @@ -55,10 +44,8 @@ class PagePicker extends Picker * also be any subpage. When a query is given * and subpage navigation is deactivated, * there will be no model available at all. - * - * @return \Kirby\Cms\Page|\Kirby\Cms\Site|null */ - public function model() + public function model(): Page|Site|null { // no subpages navigation = no model if ($this->options['subpages'] === false) { @@ -77,10 +64,8 @@ class PagePicker extends Picker * Returns a model object for the given * query, depending on the parent and subpages * options. - * - * @return \Kirby\Cms\Page|\Kirby\Cms\Site|null */ - public function modelForQuery() + public function modelForQuery(): Page|Site|null { if ($this->options['subpages'] === true && empty($this->options['parent']) === false) { return $this->parent(); @@ -93,11 +78,8 @@ class PagePicker extends Picker * Returns basic information about the * parent model that is currently selected * in the page picker. - * - * @param \Kirby\Cms\Site|\Kirby\Cms\Page|null - * @return array|null */ - public function modelToArray($model = null): array|null + public function modelToArray(Page|Site|null $model = null): array|null { if ($model === null) { return null; @@ -132,10 +114,8 @@ class PagePicker extends Picker /** * Search all pages for the picker - * - * @return \Kirby\Cms\Pages|null */ - public function items() + public function items(): Pages|null { // cache if ($this->items !== null) { @@ -146,19 +126,19 @@ class PagePicker extends Picker if (empty($this->options['query']) === true) { $items = $this->itemsForParent(); - // when subpage navigation is enabled, a parent - // might be passed in addition to the query. - // The parent then takes priority. + // when subpage navigation is enabled, a parent + // might be passed in addition to the query. + // The parent then takes priority. } elseif ($this->options['subpages'] === true && empty($this->options['parent']) === false) { $items = $this->itemsForParent(); - // search by query + // search by query } else { $items = $this->itemsForQuery(); } - // filter protected pages - $items = $items->filter('isReadable', true); + // filter protected and hidden pages + $items = $items->filter('isListable', true); // search $items = $this->search($items); @@ -169,10 +149,8 @@ class PagePicker extends Picker /** * Search for pages by parent - * - * @return \Kirby\Cms\Pages */ - public function itemsForParent() + public function itemsForParent(): Pages { return $this->parent()->children(); } @@ -180,10 +158,9 @@ class PagePicker extends Picker /** * Search for pages by query string * - * @return \Kirby\Cms\Pages * @throws \Kirby\Exception\InvalidArgumentException */ - public function itemsForQuery() + public function itemsForQuery(): Pages { // cache if ($this->itemsForQuery !== null) { @@ -212,26 +189,18 @@ class PagePicker extends Picker * The model will be used to fetch * subpages unless there's a specific * query to find pages instead. - * - * @return \Kirby\Cms\Page|\Kirby\Cms\Site */ - public function parent() + public function parent(): Page|Site { - if ($this->parent !== null) { - return $this->parent; - } - - return $this->parent = $this->kirby->page($this->options['parent']) ?? $this->site; + return $this->parent ??= $this->kirby->page($this->options['parent']) ?? $this->site; } /** * Calculates the top-most model (page or site) * that can be accessed when navigating * through pages. - * - * @return \Kirby\Cms\Page|\Kirby\Cms\Site */ - public function start() + public function start(): Page|Site { if (empty($this->options['query']) === false) { return $this->itemsForQuery()?->parent() ?? $this->site; @@ -244,8 +213,6 @@ class PagePicker extends Picker * Returns an associative array * with all information for the picker. * This will be passed directly to the API. - * - * @return array */ public function toArray(): array { diff --git a/kirby/src/Cms/PageRules.php b/kirby/src/Cms/PageRules.php index 30ef6e8..bef0993 100644 --- a/kirby/src/Cms/PageRules.php +++ b/kirby/src/Cms/PageRules.php @@ -6,6 +6,7 @@ use Kirby\Exception\DuplicateException; use Kirby\Exception\InvalidArgumentException; use Kirby\Exception\LogicException; use Kirby\Exception\PermissionException; +use Kirby\Toolkit\A; use Kirby\Toolkit\Str; /** @@ -22,12 +23,9 @@ class PageRules /** * Validates if the sorting number of the page can be changed * - * @param \Kirby\Cms\Page $page - * @param int|null $num - * @return bool * @throws \Kirby\Exception\InvalidArgumentException If the given number is invalid */ - public static function changeNum(Page $page, int $num = null): bool + public static function changeNum(Page $page, int|null $num = null): bool { if ($num !== null && $num < 0) { throw new InvalidArgumentException(['key' => 'page.num.invalid']); @@ -39,9 +37,6 @@ class PageRules /** * Validates if the slug for the page can be changed * - * @param \Kirby\Cms\Page $page - * @param string $slug - * @return bool * @throws \Kirby\Exception\DuplicateException If a page with this slug already exists * @throws \Kirby\Exception\PermissionException If the user is not allowed to change the slug */ @@ -57,6 +52,7 @@ class PageRules } self::validateSlugLength($slug); + self::validateSlugProtectedPaths($page, $slug); $siblings = $page->parentModel()->children(); $drafts = $page->parentModel()->drafts(); @@ -85,14 +81,13 @@ class PageRules /** * Validates if the status for the page can be changed * - * @param \Kirby\Cms\Page $page - * @param string $status - * @param int|null $position - * @return bool * @throws \Kirby\Exception\InvalidArgumentException If the given status is invalid */ - public static function changeStatus(Page $page, string $status, int $position = null): bool - { + public static function changeStatus( + Page $page, + string $status, + int|null $position = null + ): bool { if (isset($page->blueprint()->status()[$status]) === false) { throw new InvalidArgumentException(['key' => 'page.status.invalid']); } @@ -108,11 +103,9 @@ class PageRules /** * Validates if a page can be converted to a draft * - * @param \Kirby\Cms\Page $page - * @return bool * @throws \Kirby\Exception\PermissionException If the user is not allowed to change the status or the page cannot be converted to a draft */ - public static function changeStatusToDraft(Page $page) + public static function changeStatusToDraft(Page $page): bool { if ($page->permissions()->changeStatus() !== true) { throw new PermissionException([ @@ -138,13 +131,10 @@ class PageRules /** * Validates if the status of a page can be changed to listed * - * @param \Kirby\Cms\Page $page - * @param int $position - * @return bool * @throws \Kirby\Exception\InvalidArgumentException If the given position is invalid * @throws \Kirby\Exception\PermissionException If the user is not allowed to change the status or the status for the page cannot be changed by any user */ - public static function changeStatusToListed(Page $page, int $position) + public static function changeStatusToListed(Page $page, int $position): bool { // no need to check for status changing permissions, // instead we need to check for sorting permissions @@ -173,8 +163,6 @@ class PageRules /** * Validates if the status of a page can be changed to unlisted * - * @param \Kirby\Cms\Page $page - * @return bool * @throws \Kirby\Exception\PermissionException If the user is not allowed to change the status */ public static function changeStatusToUnlisted(Page $page) @@ -187,9 +175,6 @@ class PageRules /** * Validates if the template of the page can be changed * - * @param \Kirby\Cms\Page $page - * @param string $template - * @return bool * @throws \Kirby\Exception\LogicException If the template of the page cannot be changed at all * @throws \Kirby\Exception\PermissionException If the user is not allowed to change the template */ @@ -222,9 +207,6 @@ class PageRules /** * Validates if the title of the page can be changed * - * @param \Kirby\Cms\Page $page - * @param string $title - * @return bool * @throws \Kirby\Exception\InvalidArgumentException If the new title is empty * @throws \Kirby\Exception\PermissionException If the user is not allowed to change the title */ @@ -239,11 +221,7 @@ class PageRules ]); } - if (Str::length($title) === 0) { - throw new InvalidArgumentException([ - 'key' => 'page.changeTitle.empty', - ]); - } + static::validateTitleLength($title); return true; } @@ -251,8 +229,6 @@ class PageRules /** * Validates if the page can be created * - * @param \Kirby\Cms\Page $page - * @return bool * @throws \Kirby\Exception\DuplicateException If the same page or a draft already exists * @throws \Kirby\Exception\InvalidArgumentException If the slug is invalid * @throws \Kirby\Exception\PermissionException If the user is not allowed to create this page @@ -269,6 +245,7 @@ class PageRules } self::validateSlugLength($page->slug()); + self::validateSlugProtectedPaths($page, $page->slug()); if ($page->exists() === true) { throw new DuplicateException([ @@ -303,9 +280,6 @@ class PageRules /** * Validates if the page can be deleted * - * @param \Kirby\Cms\Page $page - * @param bool $force - * @return bool * @throws \Kirby\Exception\LogicException If the page has children and should not be force-deleted * @throws \Kirby\Exception\PermissionException If the user is not allowed to delete the page */ @@ -330,14 +304,13 @@ class PageRules /** * Validates if the page can be duplicated * - * @param \Kirby\Cms\Page $page - * @param string $slug - * @param array $options - * @return bool * @throws \Kirby\Exception\PermissionException If the user is not allowed to duplicate the page */ - public static function duplicate(Page $page, string $slug, array $options = []): bool - { + public static function duplicate( + Page $page, + string $slug, + array $options = [] + ): bool { if ($page->permissions()->duplicate() !== true) { throw new PermissionException([ 'key' => 'page.duplicate.permission', @@ -352,12 +325,95 @@ class PageRules return true; } + /** + * Check if the page can be moved + * to the given parent + */ + public static function move(Page $page, Site|Page $parent): bool + { + // if nothing changes, there's no need for checks + if ($parent->is($page->parent()) === true) { + return true; + } + + if ($page->permissions()->move() !== true) { + throw new PermissionException([ + 'key' => 'page.move.permission', + 'data' => [ + 'slug' => $page->slug() + ] + ]); + } + + // the page cannot be moved into itself + if ($parent instanceof Page && ($page->is($parent) === true || $page->isAncestorOf($parent) === true)) { + throw new LogicException([ + 'key' => 'page.move.ancestor', + ]); + } + + // check for duplicates + if ($parent->childrenAndDrafts()->find($page->slug())) { + throw new DuplicateException([ + 'key' => 'page.move.duplicate', + 'data' => [ + 'slug' => $page->slug(), + ] + ]); + } + + $allowed = []; + + // collect all allowed subpage templates + // from all pages sections in the blueprint + // (only consider page sections that list pages + // of the targeted new parent page) + $sections = array_filter( + $parent->blueprint()->sections(), + fn ($section) => + $section->type() === 'pages' && + $section->parent()->is($parent) + ); + + // check if the parent has at least one pages section + if ($sections === []) { + throw new LogicException([ + 'key' => 'page.move.noSections', + 'data' => [ + 'parent' => $parent->id() ?? '/', + ] + ]); + } + + // go through all allowed templates and + // add the name to the allowlist + foreach ($sections as $section) { + foreach ($section->templates() as $template) { + $allowed[] = $template; + } + } + + // check if the template of this page is allowed as subpage type + // for the potential new parent + if ( + $allowed !== [] && + in_array($page->intendedTemplate()->name(), $allowed) === false + ) { + throw new PermissionException([ + 'key' => 'page.move.template', + 'data' => [ + 'template' => $page->intendedTemplate()->name(), + 'parent' => $parent->id() ?? '/', + ] + ]); + } + + return true; + } + /** * Check if the page can be published * (status change from draft to listed or unlisted) - * - * @param Page $page - * @return bool */ public static function publish(Page $page): bool { @@ -383,9 +439,6 @@ class PageRules /** * Validates if the page can be updated * - * @param \Kirby\Cms\Page $page - * @param array $content - * @return bool * @throws \Kirby\Exception\PermissionException If the user is not allowed to update the page */ public static function update(Page $page, array $content = []): bool @@ -406,11 +459,9 @@ class PageRules * Ensures that the slug is not empty and doesn't exceed the maximum length * to make sure that the directory name will be accepted by the filesystem * - * @param string $slug New slug to check - * @return void * @throws \Kirby\Exception\InvalidArgumentException If the slug is empty or too long */ - protected static function validateSlugLength(string $slug): void + public static function validateSlugLength(string $slug): void { $slugLength = Str::length($slug); @@ -433,4 +484,48 @@ class PageRules } } } + + + /** + * Ensure that a top-level page path does not start with one of + * the reserved URL paths, e.g. for API or the Panel + * + * @throws \Kirby\Exception\InvalidArgumentException If the page ID starts as one of the disallowed paths + */ + protected static function validateSlugProtectedPaths( + Page $page, + string $slug + ): void { + if ($page->parent() === null) { + $paths = A::map( + ['api', 'assets', 'media', 'panel'], + fn ($url) => $page->kirby()->url($url, true)->path()->toString() + ); + + $index = array_search($slug, $paths); + + if ($index !== false) { + throw new InvalidArgumentException([ + 'key' => 'page.changeSlug.reserved', + 'data' => [ + 'path' => $paths[$index] + ] + ]); + } + } + } + + /** + * Ensures that the page title is not empty + * + * @throws \Kirby\Exception\InvalidArgumentException If the title is empty + */ + public static function validateTitleLength(string $title): void + { + if (Str::length($title) === 0) { + throw new InvalidArgumentException([ + 'key' => 'page.changeTitle.empty', + ]); + } + } } diff --git a/kirby/src/Cms/PageSiblings.php b/kirby/src/Cms/PageSiblings.php index 22014d2..04f9f63 100644 --- a/kirby/src/Cms/PageSiblings.php +++ b/kirby/src/Cms/PageSiblings.php @@ -18,8 +18,6 @@ trait PageSiblings * page in the siblings collection * * @param \Kirby\Cms\Collection|null $collection - * - * @return bool */ public function hasNextListed($collection = null): bool { @@ -31,8 +29,6 @@ trait PageSiblings * page in the siblings collection * * @param \Kirby\Cms\Collection|null $collection - * - * @return bool */ public function hasNextUnlisted($collection = null): bool { @@ -44,8 +40,6 @@ trait PageSiblings * page in the siblings collection * * @param \Kirby\Cms\Collection|null $collection - * - * @return bool */ public function hasPrevListed($collection = null): bool { @@ -57,8 +51,6 @@ trait PageSiblings * page in the siblings collection * * @param \Kirby\Cms\Collection|null $collection - * - * @return bool */ public function hasPrevUnlisted($collection = null): bool { @@ -130,7 +122,6 @@ trait PageSiblings /** * Returns siblings with the same template * - * @param bool $self * @return \Kirby\Cms\Pages */ public function templateSiblings(bool $self = true) diff --git a/kirby/src/Cms/Pages.php b/kirby/src/Cms/Pages.php index e5773dd..8c96708 100644 --- a/kirby/src/Cms/Pages.php +++ b/kirby/src/Cms/Pages.php @@ -41,10 +41,8 @@ class Pages extends Collection /** * All registered pages methods - * - * @var array */ - public static $methods = []; + public static array $methods = []; /** * Adds a single page or @@ -55,7 +53,7 @@ class Pages extends Collection * @return $this * @throws \Kirby\Exception\InvalidArgumentException When no `Page` or `Pages` object or an ID of an existing page is passed */ - public function add($object) + public function add($object): static { $site = App::instance()->site(); @@ -63,19 +61,19 @@ class Pages extends Collection if ($object instanceof self) { $this->data = array_merge($this->data, $object->data); - // add a page by id + // add a page by id } elseif ( is_string($object) === true && $page = $site->find($object) ) { $this->__set($page->id(), $page); - // add a page object + // add a page object } elseif ($object instanceof Page) { $this->__set($object->id(), $object); - // give a useful error message on invalid input; - // silently ignore "empty" values for compatibility with existing setups + // give a useful error message on invalid input; + // silently ignore "empty" values for compatibility with existing setups } elseif (in_array($object, [null, false, true], true) !== true) { throw new InvalidArgumentException('You must pass a Pages or Page object or an ID of an existing page to the Pages collection'); } @@ -85,20 +83,16 @@ class Pages extends Collection /** * Returns all audio files of all children - * - * @return \Kirby\Cms\Files */ - public function audio() + public function audio(): Files { return $this->files()->filter('type', 'audio'); } /** * Returns all children for each page in the array - * - * @return \Kirby\Cms\Pages */ - public function children() + public function children(): Pages { $children = new Pages([]); @@ -113,30 +107,24 @@ class Pages extends Collection /** * Returns all code files of all children - * - * @return \Kirby\Cms\Files */ - public function code() + public function code(): Files { return $this->files()->filter('type', 'code'); } /** * Returns all documents of all children - * - * @return \Kirby\Cms\Files */ - public function documents() + public function documents(): Files { return $this->files()->filter('type', 'document'); } /** * Fetch all drafts for all pages in the collection - * - * @return \Kirby\Cms\Pages */ - public function drafts() + public function drafts(): Pages { $drafts = new Pages([]); @@ -151,17 +139,14 @@ class Pages extends Collection /** * Creates a pages collection from an array of props - * - * @param array $pages - * @param \Kirby\Cms\Model|null $model - * @param bool|null $draft - * @return static */ - public static function factory(array $pages, Model $model = null, bool $draft = null) - { + public static function factory( + array $pages, + Page|Site|null $model = null, + bool|null $draft = null + ): static { $model ??= App::instance()->site(); $children = new static([], $model); - $kirby = $model->kirby(); if ($model instanceof Page) { $parent = $model; @@ -172,7 +157,6 @@ class Pages extends Collection } foreach ($pages as $props) { - $props['kirby'] = $kirby; $props['parent'] = $parent; $props['site'] = $site; $props['isDraft'] = $draft ?? $props['isDraft'] ?? $props['draft'] ?? false; @@ -187,10 +171,8 @@ class Pages extends Collection /** * Returns all files of all children - * - * @return \Kirby\Cms\Files */ - public function files() + public function files(): Files { $files = new Files([], $this->parent); @@ -206,11 +188,8 @@ class Pages extends Collection /** * Finds a page by its ID or URI * @internal Use `$pages->find()` instead - * - * @param string|null $key - * @return \Kirby\Cms\Page|null */ - public function findByKey(string|null $key = null) + public function findByKey(string|null $key = null): Page|null { if ($key === null) { return null; @@ -239,17 +218,21 @@ class Pages extends Collection return $page; } - // try to find the page by its (translated) URI by stepping through the page tree + $kirby = App::instance(); + $multiLang = $kirby->multilang(); + + // try to find the page by its (translated) URI + // by stepping through the page tree $start = $this->parent instanceof Page ? $this->parent->id() : ''; - if ($page = $this->findByKeyRecursive($key, $start, App::instance()->multilang())) { + if ($page = $this->findByKeyRecursive($key, $start, $multiLang)) { return $page; } // for secondary languages, try the full translated URI // (for collections without parent that won't have a result above) if ( - App::instance()->multilang() === true && - App::instance()->language()->isDefault() === false && + $multiLang === true && + $kirby->language()->isDefault() === false && $page = $this->findBy('uri', $key) ) { return $page; @@ -263,8 +246,11 @@ class Pages extends Collection * * @return mixed */ - protected function findByKeyRecursive(string $id, string $startAt = null, bool $multiLang = false) - { + protected function findByKeyRecursive( + string $id, + string|null $startAt = null, + bool $multiLang = false + ) { $path = explode('/', $id); $item = null; $query = $startAt; @@ -274,9 +260,14 @@ class Pages extends Collection $query = ltrim($query . '/' . $key, '/'); $item = $collection->get($query) ?? null; - if ($item === null && $multiLang === true && !App::instance()->language()->isDefault()) { + if ( + $item === null && + $multiLang === true && + App::instance()->language()->isDefault() === false + ) { if (count($path) > 1 || $collection->parent()) { - // either the desired path is definitely not a slug, or collection is the children of another collection + // either the desired path is definitely not a slug, + // or collection is the children of another collection $item = $collection->findBy('slug', $key); } else { // desired path _could_ be a slug or a "top level" uri @@ -294,10 +285,8 @@ class Pages extends Collection /** * Finds the currently open page - * - * @return \Kirby\Cms\Page|null */ - public function findOpen() + public function findOpen(): Page|null { return $this->findBy('isOpen', true); } @@ -325,10 +314,8 @@ class Pages extends Collection /** * Returns all images of all children - * - * @return \Kirby\Cms\Files */ - public function images() + public function images(): Files { return $this->files()->filter('type', 'image'); } @@ -337,7 +324,6 @@ class Pages extends Collection * Create a recursive flat index of all * pages and subpages, etc. * - * @param bool $drafts * @return \Kirby\Cms\Pages */ public function index(bool $drafts = false) @@ -371,20 +357,16 @@ class Pages extends Collection /** * Returns all listed pages in the collection - * - * @return \Kirby\Cms\Pages */ - public function listed() + public function listed(): static { return $this->filter('isListed', '==', true); } /** * Returns all unlisted pages in the collection - * - * @return \Kirby\Cms\Pages */ - public function unlisted() + public function unlisted(): static { return $this->filter('isUnlisted', '==', true); } @@ -461,27 +443,22 @@ class Pages extends Collection $templates = [$templates]; } - return $this->filter(function ($page) use ($templates) { - return !in_array($page->intendedTemplate()->name(), $templates); - }); + return $this->filter( + fn ($page) => + !in_array($page->intendedTemplate()->name(), $templates) + ); } /** * Returns an array with all page numbers - * - * @return array */ public function nums(): array { return $this->pluck('num'); } - /* - * Returns all listed and unlisted pages in the collection - * - * @return \Kirby\Cms\Pages - */ - public function published() + // Returns all listed and unlisted pages in the collection + public function published(): static { return $this->filter('isDraft', '==', false); } @@ -502,17 +479,16 @@ class Pages extends Collection $templates = [$templates]; } - return $this->filter(function ($page) use ($templates) { - return in_array($page->intendedTemplate()->name(), $templates); - }); + return $this->filter( + fn ($page) => + in_array($page->intendedTemplate()->name(), $templates) + ); } /** * Returns all video files of all children - * - * @return \Kirby\Cms\Files */ - public function videos() + public function videos(): Files { return $this->files()->filter('type', 'video'); } diff --git a/kirby/src/Cms/Pagination.php b/kirby/src/Cms/Pagination.php index c88f749..223d80a 100644 --- a/kirby/src/Cms/Pagination.php +++ b/kirby/src/Cms/Pagination.php @@ -60,8 +60,6 @@ class Pagination extends BasePagination * 'url' => new Uri('https://getkirby.com/blog') * ]); * ``` - * - * @param array $params */ public function __construct(array $params = []) { @@ -95,8 +93,6 @@ class Pagination extends BasePagination /** * Returns the Url for the first page - * - * @return string|null */ public function firstPageUrl(): string|null { @@ -105,8 +101,6 @@ class Pagination extends BasePagination /** * Returns the Url for the last page - * - * @return string|null */ public function lastPageUrl(): string|null { @@ -116,8 +110,6 @@ class Pagination extends BasePagination /** * Returns the Url for the next page. * Returns null if there's no next page. - * - * @return string|null */ public function nextPageUrl(): string|null { @@ -132,11 +124,8 @@ class Pagination extends BasePagination * Returns the URL of the current page. * If the `$page` variable is set, the URL * for that page will be returned. - * - * @param int|null $page - * @return string|null */ - public function pageUrl(int $page = null): string|null + public function pageUrl(int|null $page = null): string|null { if ($page === null) { return $this->pageUrl($this->page()); @@ -165,8 +154,6 @@ class Pagination extends BasePagination /** * Returns the Url for the previous page. * Returns null if there's no previous page. - * - * @return string|null */ public function prevPageUrl(): string|null { diff --git a/kirby/src/Cms/Permissions.php b/kirby/src/Cms/Permissions.php index c9b2362..731eebc 100644 --- a/kirby/src/Cms/Permissions.php +++ b/kirby/src/Cms/Permissions.php @@ -17,15 +17,9 @@ use Kirby\Exception\InvalidArgumentException; */ class Permissions { - /** - * @var array - */ - public static $extendedActions = []; + public static array $extendedActions = []; - /** - * @var array - */ - protected $actions = [ + protected array $actions = [ 'access' => [ 'account' => true, 'languages' => true, @@ -35,12 +29,15 @@ class Permissions 'users' => true, ], 'files' => [ - 'changeName' => true, - 'create' => true, - 'delete' => true, - 'read' => true, - 'replace' => true, - 'update' => true + 'access' => true, + 'changeName' => true, + 'changeTemplate' => true, + 'create' => true, + 'delete' => true, + 'list' => true, + 'read' => true, + 'replace' => true, + 'update' => true ], 'languages' => [ 'create' => true, @@ -48,6 +45,7 @@ class Permissions 'update' => true ], 'pages' => [ + 'access' => true, 'changeSlug' => true, 'changeStatus' => true, 'changeTemplate' => true, @@ -55,6 +53,8 @@ class Permissions 'create' => true, 'delete' => true, 'duplicate' => true, + 'list' => true, + 'move' => true, 'preview' => true, 'read' => true, 'sort' => true, @@ -88,10 +88,9 @@ class Permissions /** * Permissions constructor * - * @param array $settings * @throws \Kirby\Exception\InvalidArgumentException */ - public function __construct($settings = []) + public function __construct(array|bool|null $settings = []) { // dynamically register the extended actions foreach (static::$extendedActions as $key => $actions) { @@ -111,55 +110,46 @@ class Permissions } } - /** - * @param string|null $category - * @param string|null $action - * @return bool - */ - public function for(string $category = null, string $action = null): bool - { + public function for( + string|null $category = null, + string|null $action = null, + bool $default = false + ): bool { if ($action === null) { if ($this->hasCategory($category) === false) { - return false; + return $default; } return $this->actions[$category]; } if ($this->hasAction($category, $action) === false) { - return false; + return $default; } return $this->actions[$category][$action]; } - /** - * @param string $category - * @param string $action - * @return bool - */ protected function hasAction(string $category, string $action): bool { - return $this->hasCategory($category) === true && array_key_exists($action, $this->actions[$category]) === true; + return + $this->hasCategory($category) === true && + array_key_exists($action, $this->actions[$category]) === true; } - /** - * @param string $category - * @return bool - */ protected function hasCategory(string $category): bool { return array_key_exists($category, $this->actions) === true; } /** - * @param string $category - * @param string $action - * @param $setting * @return $this */ - protected function setAction(string $category, string $action, $setting) - { + protected function setAction( + string $category, + string $action, + $setting + ): static { // wildcard to overwrite the entire category if ($action === '*') { return $this->setCategory($category, $setting); @@ -171,10 +161,9 @@ class Permissions } /** - * @param bool $setting * @return $this */ - protected function setAll(bool $setting) + protected function setAll(bool $setting): static { foreach ($this->actions as $categoryName => $actions) { $this->setCategory($categoryName, $setting); @@ -184,10 +173,9 @@ class Permissions } /** - * @param array $settings * @return $this */ - protected function setCategories(array $settings) + protected function setCategories(array $settings): static { foreach ($settings as $categoryName => $categoryActions) { if (is_bool($categoryActions) === true) { @@ -205,12 +193,10 @@ class Permissions } /** - * @param string $category - * @param bool $setting * @return $this * @throws \Kirby\Exception\InvalidArgumentException */ - protected function setCategory(string $category, bool $setting) + protected function setCategory(string $category, bool $setting): static { if ($this->hasCategory($category) === false) { throw new InvalidArgumentException('Invalid permissions category'); @@ -223,9 +209,6 @@ class Permissions return $this; } - /** - * @return array - */ public function toArray(): array { return $this->actions; diff --git a/kirby/src/Cms/Picker.php b/kirby/src/Cms/Picker.php index 7c26ad6..f3bcce0 100644 --- a/kirby/src/Cms/Picker.php +++ b/kirby/src/Cms/Picker.php @@ -14,25 +14,12 @@ namespace Kirby\Cms; */ abstract class Picker { - /** - * @var \Kirby\Cms\App - */ - protected $kirby; - - /** - * @var array - */ - protected $options; - - /** - * @var \Kirby\Cms\Site - */ - protected $site; + protected App $kirby; + protected array $options; + protected Site $site; /** * Creates a new Picker instance - * - * @param array $params */ public function __construct(array $params = []) { @@ -43,8 +30,6 @@ abstract class Picker /** * Return the array of default values - * - * @return array */ protected function defaults(): array { @@ -55,7 +40,7 @@ abstract class Picker // query template for the info field 'info' => false, // listing style: list, cards, cardlets - 'layout' =>'list', + 'layout' => 'list', // number of users displayed per pagination page 'limit' => 20, // optional mapping function for the result array @@ -75,20 +60,15 @@ abstract class Picker /** * Fetches all items for the picker - * - * @return \Kirby\Cms\Collection|null */ - abstract public function items(); + abstract public function items(): Collection|null; /** * Converts all given items to an associative * array that is already optimized for the * panel picker component. - * - * @param \Kirby\Cms\Collection|null $items - * @return array */ - public function itemsToArray($items = null): array + public function itemsToArray(Collection|null $items = null): array { if ($items === null) { return []; @@ -116,11 +96,8 @@ abstract class Picker /** * Apply pagination to the collection * of items according to the options. - * - * @param \Kirby\Cms\Collection $items - * @return \Kirby\Cms\Collection */ - public function paginate(Collection $items) + public function paginate(Collection $items): Collection { return $items->paginate([ 'limit' => $this->options['limit'], @@ -131,9 +108,6 @@ abstract class Picker /** * Return the most relevant pagination * info as array - * - * @param \Kirby\Cms\Pagination $pagination - * @return array */ public function paginationToArray(Pagination $pagination): array { @@ -147,11 +121,8 @@ abstract class Picker /** * Search through the collection of items * if not deactivate in the options - * - * @param \Kirby\Cms\Collection $items - * @return \Kirby\Cms\Collection */ - public function search(Collection $items) + public function search(Collection $items): Collection { if (empty($this->options['search']) === false) { return $items->search($this->options['search']); @@ -164,8 +135,6 @@ abstract class Picker * Returns an associative array * with all information for the picker. * This will be passed directly to the API. - * - * @return array */ public function toArray(): array { diff --git a/kirby/src/Cms/Plugin.php b/kirby/src/Cms/Plugin.php index 84d1f1d..cc6cc8f 100644 --- a/kirby/src/Cms/Plugin.php +++ b/kirby/src/Cms/Plugin.php @@ -23,36 +23,79 @@ use Throwable; * @copyright Bastian Allgeier * @license https://getkirby.com/license */ -class Plugin extends Model +class Plugin { - protected array $extends; - protected string $name; - protected string $root; - - // caches - protected array|null $info = null; + protected PluginAssets $assets; protected UpdateStatus|null $updateStatus = null; + /** + * @param string $name Plugin name within Kirby (`vendor/plugin`) + * @param array $extends Associative array of plugin extensions + * + * @throws \Kirby\Exception\InvalidArgumentException If the plugin name has an invalid format + */ + public function __construct( + protected string $name, + protected array $extends = [], + protected array $info = [], + protected string|null $root = null, + protected string|null $version = null, + ) { + static::validateName($name); + + // TODO: Remove in v7 + if ($root = $extends['root'] ?? null) { + Helpers::deprecated('Plugin "' . $name . '": Passing the `root` inside the `extends` array has been deprecated. Pass it directly as named argument `root`.', 'plugin-extends-root'); + $this->root ??= $root; + unset($this->extends['root']); + } + + $this->root ??= dirname(debug_backtrace()[0]['file']); + + // TODO: Remove in v7 + if ($info = $extends['info'] ?? null) { + Helpers::deprecated('Plugin "' . $name . '": Passing an `info` array inside the `extends` array has been deprecated. Pass the individual entries directly as named `info` argument.', 'plugin-extends-root'); + + if (empty($info) === false && is_array($info) === true) { + $this->info = [...$info, ...$this->info]; + } + + unset($this->extends['info']); + } + + // read composer.json and use as info fallback + try { + $info = Data::read($this->manifest()); + } catch (Exception) { + // there is no manifest file or it is invalid + $info = []; + } + + $this->info = [...$info, ...$this->info]; + } + /** * Allows access to any composer.json field by method call */ - public function __call(string $key, array $arguments = null) + public function __call(string $key, array|null $arguments = null): mixed { return $this->info()[$key] ?? null; } /** - * @param string $name Plugin name within Kirby (`vendor/plugin`) - * @param array $extends Associative array of plugin extensions + * Returns the plugin asset object for a specific asset */ - public function __construct(string $name, array $extends = []) + public function asset(string $path): PluginAsset|null { - $this->setName($name); - $this->extends = $extends; - $this->root = $extends['root'] ?? dirname(debug_backtrace()[0]['file']); - $this->info = empty($extends['info']) === false && is_array($extends['info']) ? $extends['info'] : null; + return $this->assets()->get($path); + } - unset($this->extends['root'], $this->extends['info']); + /** + * Returns the plugin assets collection + */ + public function assets(): PluginAssets + { + return $this->assets ??= PluginAssets::factory($this); } /** @@ -96,22 +139,19 @@ class Plugin extends Model } /** - * Returns the raw data from composer.json + * Returns the info data (from composer.json) */ public function info(): array { - if (is_array($this->info) === true) { - return $this->info; - } + return $this->info; + } - try { - $info = Data::read($this->manifest()); - } catch (Exception) { - // there is no manifest file or it is invalid - $info = []; - } - - return $this->info = $info; + /** + * Current $kirby instance + */ + public function kirby(): App + { + return App::instance(); } /** @@ -185,23 +225,6 @@ class Plugin extends Model return $this->root; } - /** - * Validates and sets the plugin name - * - * @return $this - * - * @throws \Kirby\Exception\InvalidArgumentException If the plugin name has an invalid format - */ - protected function setName(string $name): static - { - if (preg_match('!^[a-z0-9-]+\/[a-z0-9-]+$!i', $name) !== 1) { - throw new InvalidArgumentException('The plugin name must follow the format "a-z0-9-/a-z0-9-"'); - } - - $this->name = $name; - return $this; - } - /** * Returns all available plugin metadata */ @@ -264,23 +287,39 @@ class Plugin extends Model return $this->updateStatus = new UpdateStatus($this, false, $data); } + /** + * Checks if the name follows the required pattern + * and throws an exception if not + * + * @throws \Kirby\Exception\InvalidArgumentException + */ + public static function validateName(string $name): void + { + if (preg_match('!^[a-z0-9-]+\/[a-z0-9-]+$!i', $name) !== 1) { + throw new InvalidArgumentException('The plugin name must follow the format "a-z0-9-/a-z0-9-"'); + } + } + /** * Returns the normalized version number * from the composer.json file */ public function version(): string|null { - $composerName = $this->info()['name'] ?? null; - $version = $this->info()['version'] ?? null; + $name = $this->info()['name'] ?? null; try { - // if plugin doesn't have version key in composer.json file - // try to get version from "vendor/composer/installed.php" - $version ??= InstalledVersions::getPrettyVersion($composerName); + // try to get version from "vendor/composer/installed.php", + // this is the most reliable source for the version + $version = InstalledVersions::getPrettyVersion($name); } catch (Throwable) { - return null; + $version = null; } + // fallback to the version provided in the plugin's index.php: as named + // argument, entry in the info array or from the composer.json file + $version ??= $this->version ?? $this->info()['version'] ?? null; + if ( is_string($version) !== true || $version === '' || diff --git a/kirby/src/Cms/PluginAsset.php b/kirby/src/Cms/PluginAsset.php new file mode 100644 index 0000000..f5f7f69 --- /dev/null +++ b/kirby/src/Cms/PluginAsset.php @@ -0,0 +1,120 @@ + + * @link https://getkirby.com + * @copyright Bastian Allgeier + * @license https://getkirby.com/license + */ +class PluginAsset +{ + public function __construct( + protected string $path, + protected string $root, + protected Plugin $plugin + ) { + } + + public function extension(): string + { + return F::extension($this->path()); + } + + public function filename(): string + { + return F::filename($this->path()); + } + + /** + * Create a unique media hash + */ + public function mediaHash(): string + { + return crc32($this->filename()) . '-' . $this->modified(); + } + + /** + * Absolute path to the asset file in the media folder + */ + public function mediaRoot(): string + { + return $this->plugin()->mediaRoot() . '/' . $this->mediaHash() . '/' . $this->path(); + } + + /** + * Public accessible url path for the asset + */ + public function mediaUrl(): string + { + return $this->plugin()->mediaUrl() . '/' . $this->mediaHash() . '/' . $this->path(); + } + + /** + * Timestamp when asset file was last modified + */ + public function modified(): int|false + { + return F::modified($this->root()); + } + + public function path(): string + { + return $this->path; + } + + public function plugin(): Plugin + { + return $this->plugin; + } + + /** + * Publishes the asset file to the plugin's media folder + * by creating a symlink + */ + public function publish(): void + { + F::link($this->root(), $this->mediaRoot(), 'symlink'); + } + + /** + * @internal + * @since 4.0.0 + * @deprecated 4.0.0 + * @codeCoverageIgnore + */ + public function publishAt(string $path): void + { + $media = $this->plugin()->mediaRoot() . '/' . $path; + F::link($this->root(), $media, 'symlink'); + } + + public function root(): string + { + return $this->root; + } + + /** + * @see ::mediaUrl + */ + public function url(): string + { + return $this->mediaUrl(); + } + + /** + * @see ::url + */ + public function __toString(): string + { + return $this->url(); + } +} diff --git a/kirby/src/Cms/PluginAssets.php b/kirby/src/Cms/PluginAssets.php index a05ffea..45c415f 100644 --- a/kirby/src/Cms/PluginAssets.php +++ b/kirby/src/Cms/PluginAssets.php @@ -2,9 +2,11 @@ namespace Kirby\Cms; +use Closure; use Kirby\Filesystem\Dir; use Kirby\Filesystem\F; use Kirby\Http\Response; +use Kirby\Toolkit\Str; /** * Plugin assets are automatically copied/linked @@ -13,65 +15,167 @@ use Kirby\Http\Response; * * @package Kirby Cms * @author Bastian Allgeier + * @author Nico Hoffmann * @link https://getkirby.com * @copyright Bastian Allgeier * @license https://getkirby.com/license */ -class PluginAssets +class PluginAssets extends Collection { /** * Clean old/deprecated assets on every resolve - * - * @param string $pluginName - * @return void */ public static function clean(string $pluginName): void { if ($plugin = App::instance()->plugin($pluginName)) { - $root = $plugin->root() . '/assets'; $media = $plugin->mediaRoot(); - $assets = Dir::index($media, true); + $assets = $plugin->assets(); - foreach ($assets as $asset) { - $original = $root . '/' . $asset; + // get all media files + $files = Dir::index($media, true); - if (file_exists($original) === false) { - $assetRoot = $media . '/' . $asset; + // get all active assets' paths from the plugin + $active = $assets->values( + function ($asset) { + $path = $asset->mediaHash() . '/' . $asset->path(); + $paths = []; + $parts = explode('/', $path); - if (is_file($assetRoot) === true) { - F::remove($assetRoot); - } else { - Dir::remove($assetRoot); + // collect all path segments + // (e.g. foo/, foo/bar/, foo/bar/baz.css) for the asset + for ($i = 1, $max = count($parts); $i <= $max; $i++) { + $paths[] = implode('/', array_slice($parts, 0, $i)); + + // TODO: remove when media hash is enforced as mandatory + $paths[] = implode('/', array_slice($parts, 1, $i)); } + + return $paths; + } + ); + + // flatten the array and remove duplicates + $active = array_unique(array_merge(...array_values($active))); + + // get outdated media files by comparing all + // files in the media folder against the set of asset paths + $stale = array_diff($files, $active); + + foreach ($stale as $file) { + $root = $media . '/' . $file; + + if (is_file($root) === true) { + F::remove($root); + } else { + Dir::remove($root); } } } } + /** + * Filters assets collection by CSS files + */ + public function css(): static + { + return $this->filter(fn ($asset) => $asset->extension() === 'css'); + } + + /** + * Creates a new collection for the plugin's assets + * by considering the plugin's `asset` extension + * (and `assets` directory as fallback) + */ + public static function factory(Plugin $plugin): static + { + // get assets defined in the plugin extension + if ($assets = $plugin->extends()['assets'] ?? null) { + if ($assets instanceof Closure) { + $assets = $assets(); + } + + // normalize array: use relative path as + // key when no key is defined + foreach ($assets as $key => $root) { + if (is_int($key) === true) { + unset($assets[$key]); + $path = Str::after($root, $plugin->root() . '/'); + $assets[$path] = $root; + } + } + } + + // fallback: if no assets are defined in the plugin extension, + // use all files in the plugin's `assets` directory + if ($assets === null) { + $assets = []; + $root = $plugin->root() . '/assets'; + + foreach (Dir::index($root, true) as $path) { + if (is_file($root . '/' . $path) === true) { + $assets[$path] = $root . '/' . $path; + } + } + } + + $collection = new static([], $plugin); + + foreach ($assets as $path => $root) { + $collection->data[$path] = new PluginAsset($path, $root, $plugin); + } + + return $collection; + } + + /** + * Filters assets collection by JavaScript files + */ + public function js(): static + { + return $this->filter(fn ($asset) => $asset->extension() === 'js'); + } + + public function plugin(): Plugin + { + return $this->parent; + } + /** * Create a symlink for a plugin asset and * return the public URL - * - * @param string $pluginName - * @param string $filename - * @return \Kirby\Cms\Response|null */ - public static function resolve(string $pluginName, string $filename) - { + public static function resolve( + string $pluginName, + string $hash, + string $path + ): Response|null { if ($plugin = App::instance()->plugin($pluginName)) { - $source = $plugin->root() . '/assets/' . $filename; + // do some spring cleaning for older files + static::clean($pluginName); - if (F::exists($source, $plugin->root()) === true) { - // do some spring cleaning for older files - static::clean($pluginName); + // @codeCoverageIgnoreStart + // TODO: deprecated media URL without hash + if (empty($hash) === true) { + $asset = $plugin->asset($path); + $asset->publishAt($path); + return Response::file($asset->root()); + } - $target = $plugin->mediaRoot() . '/' . $filename; + // TODO: deprecated media URL with hash (but path) + if ($asset = $plugin->asset($hash . '/' . $path)) { + $asset->publishAt($hash . '/' . $path); + return Response::file($asset->root()); + } + // @codeCoverageIgnoreEnd - // create a symlink if possible - F::link($source, $target, 'symlink'); + if ($asset = $plugin->asset($path)) { + if ($asset->mediaHash() === $hash) { + // create a symlink if possible + $asset->publish(); - // return the file response - return Response::file($source); + // return the file response + return Response::file($asset->root()); + } } } diff --git a/kirby/src/Cms/R.php b/kirby/src/Cms/R.php index 5ef5df9..312fc2b 100644 --- a/kirby/src/Cms/R.php +++ b/kirby/src/Cms/R.php @@ -2,6 +2,7 @@ namespace Kirby\Cms; +use Kirby\Http\Request; use Kirby\Toolkit\Facade; /** @@ -15,10 +16,7 @@ use Kirby\Toolkit\Facade; */ class R extends Facade { - /** - * @return \Kirby\Http\Request - */ - public static function instance() + public static function instance(): Request { return App::instance()->request(); } diff --git a/kirby/src/Cms/Responder.php b/kirby/src/Cms/Responder.php index 49640fd..640381e 100644 --- a/kirby/src/Cms/Responder.php +++ b/kirby/src/Cms/Responder.php @@ -20,68 +20,50 @@ class Responder /** * Timestamp when the response expires * in Kirby's cache - * - * @var int|null */ - protected $expires = null; + protected int|null $expires = null; /** * HTTP status code - * - * @var int */ - protected $code = null; + protected int|null $code = null; /** * Response body - * - * @var string */ - protected $body = null; + protected string|null $body = null; /** * Flag that defines whether the current * response can be cached by Kirby's cache - * - * @var bool */ - protected $cache = true; + protected bool $cache = true; /** * HTTP headers - * - * @var array */ - protected $headers = []; + protected array $headers = []; /** * Content type - * - * @var string */ - protected $type = null; + protected string|null $type = null; /** * Flag that defines whether the current * response uses the HTTP `Authorization` * request header - * - * @var bool */ - protected $usesAuth = false; + protected bool $usesAuth = false; /** * List of cookie names the response * relies on - * - * @var array */ - protected $usesCookies = []; + protected array $usesCookies = []; /** * Creates and sends the response - * - * @return string */ public function __toString(): string { @@ -91,10 +73,9 @@ class Responder /** * Setter and getter for the response body * - * @param string|null $body - * @return string|$this + * @return $this|string|null */ - public function body(string $body = null) + public function body(string|null $body = null): static|string|null { if ($body === null) { return $this->body; @@ -110,10 +91,9 @@ class Responder * by Kirby's cache * @since 3.5.5 * - * @param bool|null $cache * @return bool|$this */ - public function cache(bool|null $cache = null) + public function cache(bool|null $cache = null): bool|static { if ($cache === null) { // never ever cache private responses @@ -134,10 +114,9 @@ class Responder * `Authorization` request header * @since 3.7.0 * - * @param bool|null $usesAuth * @return bool|$this */ - public function usesAuth(bool|null $usesAuth = null) + public function usesAuth(bool|null $usesAuth = null): bool|static { if ($usesAuth === null) { return $this->usesAuth; @@ -151,9 +130,6 @@ class Responder * Setter for a cookie name that is * used by the response * @since 3.7.0 - * - * @param string $name - * @return void */ public function usesCookie(string $name): void { @@ -168,7 +144,6 @@ class Responder * names the response relies on * @since 3.7.0 * - * @param array|null $usesCookies * @return array|$this */ public function usesCookies(array|null $usesCookies = null) @@ -233,10 +208,9 @@ class Responder /** * Setter and getter for the status code * - * @param int|null $code * @return int|$this */ - public function code(int $code = null) + public function code(int|null $code = null) { if ($code === null) { return $this->code; @@ -248,8 +222,6 @@ class Responder /** * Construct response from an array - * - * @param array $response */ public function fromArray(array $response): void { @@ -266,7 +238,6 @@ class Responder /** * Setter and getter for a single header * - * @param string $key * @param string|false|null $value * @param bool $lazy If `true`, an existing header value is not overridden * @return string|$this @@ -293,10 +264,9 @@ class Responder /** * Setter and getter for all headers * - * @param array|null $headers * @return array|$this */ - public function headers(array $headers = null) + public function headers(array|null $headers = null) { if ($headers === null) { $injectedHeaders = []; @@ -333,10 +303,9 @@ class Responder /** * Shortcut to configure a json response * - * @param array|null $json * @return string|$this */ - public function json(array $json = null) + public function json(array|null $json = null) { if ($json !== null) { $this->body(json_encode($json)); @@ -348,12 +317,12 @@ class Responder /** * Shortcut to create a redirect response * - * @param string|null $location - * @param int|null $code * @return $this */ - public function redirect(string|null $location = null, int|null $code = null) - { + public function redirect( + string|null $location = null, + int|null $code = null + ) { $location = Url::to($location ?? '/'); $location = Url::unIdn($location); @@ -364,11 +333,8 @@ class Responder /** * Creates and returns the response object from the config - * - * @param string|null $body - * @return \Kirby\Cms\Response */ - public function send(string $body = null) + public function send(string|null $body = null): Response { if ($body !== null) { $this->body($body); @@ -380,8 +346,6 @@ class Responder /** * Converts the response configuration * to an array - * - * @return array */ public function toArray(): array { @@ -399,10 +363,9 @@ class Responder /** * Setter and getter for the content type * - * @param string|null $type * @return string|$this */ - public function type(string $type = null) + public function type(string|null $type = null) { if ($type === null) { return $this->type; @@ -423,10 +386,6 @@ class Responder * is actually used/relied on by the response * @since 3.7.0 * @internal - * - * @param bool $usesAuth - * @param array $usesCookies - * @return bool */ public static function isPrivate(bool $usesAuth, array $usesCookies): bool { diff --git a/kirby/src/Cms/Response.php b/kirby/src/Cms/Response.php index 804ec43..5a21bc6 100644 --- a/kirby/src/Cms/Response.php +++ b/kirby/src/Cms/Response.php @@ -19,8 +19,10 @@ class Response extends \Kirby\Http\Response * parses locations with the Url::to method * first. */ - public static function redirect(string $location = '/', int $code = 302): static - { + public static function redirect( + string $location = '/', + int $code = 302 + ): static { return parent::redirect(Url::to($location), $code); } } diff --git a/kirby/src/Cms/Role.php b/kirby/src/Cms/Role.php index 1860690..ecdf2a6 100644 --- a/kirby/src/Cms/Role.php +++ b/kirby/src/Cms/Role.php @@ -17,41 +17,38 @@ use Kirby\Toolkit\I18n; * @copyright Bastian Allgeier * @license https://getkirby.com/license */ -class Role extends Model +class Role { - protected $description; - protected $name; - protected $permissions; - protected $title; + protected string|null $description; + protected string $name; + protected Permissions $permissions; + protected string|null $title; public function __construct(array $props) { - $this->setProperties($props); + $this->name = $props['name']; + $this->permissions = new Permissions($props['permissions'] ?? null); + $title = $props['title'] ?? null; + $this->title = I18n::translate($title) ?? $title; + $description = $props['description'] ?? null; + $this->description = I18n::translate($description) ?? $description; } /** * Improved `var_dump` output - * - * @return array + * @codeCoverageIgnore */ public function __debugInfo(): array { return $this->toArray(); } - /** - * @return string - */ public function __toString(): string { return $this->name(); } - /** - * @param array $inject - * @return static - */ - public static function admin(array $inject = []) + public static function admin(array $inject = []): static { try { return static::load('admin'); @@ -60,9 +57,6 @@ class Role extends Model } } - /** - * @return array - */ protected static function defaults(): array { return [ @@ -81,54 +75,36 @@ class Role extends Model ]; } - /** - * @return mixed - */ - public function description() + public function description(): string|null { return $this->description; } - /** - * @param array $props - * @param array $inject - * @return static - */ - public static function factory(array $props, array $inject = []) + public static function factory(array $props, array $inject = []): static { - return new static($props + $inject); + // ensure to properly extend the blueprint + $props = $props + $inject; + $props = Blueprint::extend($props); + + return new static($props); } - /** - * @return string - */ public function id(): string { return $this->name(); } - /** - * @return bool - */ public function isAdmin(): bool { return $this->name() === 'admin'; } - /** - * @return bool - */ public function isNobody(): bool { return $this->name() === 'nobody'; } - /** - * @param string $file - * @param array $inject - * @return static - */ - public static function load(string $file, array $inject = []) + public static function load(string $file, array $inject = []): static { $data = Data::read($file); $data['name'] = F::name($file); @@ -136,19 +112,12 @@ class Role extends Model return static::factory($data, $inject); } - /** - * @return string - */ public function name(): string { return $this->name; } - /** - * @param array $inject - * @return static - */ - public static function nobody(array $inject = []) + public static function nobody(array $inject = []): static { try { return static::load('nobody'); @@ -157,57 +126,11 @@ class Role extends Model } } - /** - * @return \Kirby\Cms\Permissions - */ - public function permissions() + public function permissions(): Permissions { return $this->permissions; } - /** - * @param mixed $description - * @return $this - */ - protected function setDescription($description = null) - { - $this->description = I18n::translate($description, $description); - return $this; - } - - /** - * @param string $name - * @return $this - */ - protected function setName(string $name) - { - $this->name = $name; - return $this; - } - - /** - * @param mixed $permissions - * @return $this - */ - protected function setPermissions($permissions = null) - { - $this->permissions = new Permissions($permissions); - return $this; - } - - /** - * @param mixed $title - * @return $this - */ - protected function setTitle($title = null) - { - $this->title = I18n::translate($title, $title); - return $this; - } - - /** - * @return string - */ public function title(): string { return $this->title ??= ucfirst($this->name()); @@ -216,8 +139,6 @@ class Role extends Model /** * Converts the most important role * properties to an array - * - * @return array */ public function toArray(): array { diff --git a/kirby/src/Cms/Roles.php b/kirby/src/Cms/Roles.php index f4d37cd..4b33db6 100644 --- a/kirby/src/Cms/Roles.php +++ b/kirby/src/Cms/Roles.php @@ -20,22 +20,24 @@ class Roles extends Collection { /** * All registered roles methods - * - * @var array */ - public static $methods = []; + public static array $methods = []; /** * Returns a filtered list of all - * roles that can be created by the + * roles that can be changed by the * current user * + * Use with `$kirby->roles()`. For retrieving + * which roles are available for a specific user, + * use `$user->roles()` without additional filters. + * * @return $this|static * @throws \Exception */ - public function canBeChanged() + public function canBeChanged(): static { - if (App::instance()->user()) { + if (App::instance()->user()?->isAdmin() !== true) { return $this->filter(function ($role) { $newUser = new User([ 'email' => 'test@getkirby.com', @@ -52,14 +54,16 @@ class Roles extends Collection /** * Returns a filtered list of all * roles that can be created by the - * current user + * current user. + * + * Use with `$kirby->roles()`. * * @return $this|static * @throws \Exception */ - public function canBeCreated() + public function canBeCreated(): static { - if (App::instance()->user()) { + if (App::instance()->user()?->isAdmin() !== true) { return $this->filter(function ($role) { $newUser = new User([ 'email' => 'test@getkirby.com', @@ -73,12 +77,7 @@ class Roles extends Collection return $this; } - /** - * @param array $roles - * @param array $inject - * @return static - */ - public static function factory(array $roles, array $inject = []) + public static function factory(array $roles, array $inject = []): static { $collection = new static(); @@ -97,12 +96,7 @@ class Roles extends Collection return $collection->sort('name', 'asc'); } - /** - * @param string|null $root - * @param array $inject - * @return static - */ - public static function load(string $root = null, array $inject = []) + public static function load(string|null $root = null, array $inject = []): static { $kirby = App::instance(); $roles = new static(); diff --git a/kirby/src/Cms/S.php b/kirby/src/Cms/S.php index cced071..260ee30 100644 --- a/kirby/src/Cms/S.php +++ b/kirby/src/Cms/S.php @@ -2,6 +2,7 @@ namespace Kirby\Cms; +use Kirby\Session\Session; use Kirby\Toolkit\Facade; /** @@ -15,10 +16,7 @@ use Kirby\Toolkit\Facade; */ class S extends Facade { - /** - * @return \Kirby\Session\Session - */ - public static function instance() + public static function instance(): Session { return App::instance()->session(); } diff --git a/kirby/src/Cms/Search.php b/kirby/src/Cms/Search.php index 0169b40..b8e66f9 100644 --- a/kirby/src/Cms/Search.php +++ b/kirby/src/Cms/Search.php @@ -16,47 +16,36 @@ namespace Kirby\Cms; */ class Search { - /** - * @param string|null $query - * @param array $params - * @return \Kirby\Cms\Files - */ - public static function files(string $query = null, $params = []) - { + public static function files( + string|null $query = null, + array $params = [] + ): Files { return App::instance()->site()->index()->files()->search($query, $params); } /** * Native search method to search for anything within the collection - * - * @param \Kirby\Cms\Collection $collection - * @param string|null $query - * @param mixed $params - * @return \Kirby\Cms\Collection|bool */ - public static function collection(Collection $collection, string $query = null, $params = []) - { + public static function collection( + Collection $collection, + string|null $query = null, + string|array $params = [] + ): Collection { $kirby = App::instance(); return ($kirby->component('search'))($kirby, $collection, $query, $params); } - /** - * @param string|null $query - * @param array $params - * @return \Kirby\Cms\Pages - */ - public static function pages(string $query = null, $params = []) - { + public static function pages( + string|null $query = null, + array $params = [] + ): Pages { return App::instance()->site()->index()->search($query, $params); } - /** - * @param string|null $query - * @param array $params - * @return \Kirby\Cms\Users - */ - public static function users(string $query = null, $params = []) - { + public static function users( + string|null $query = null, + array $params = [] + ): Users { return App::instance()->users()->search($query, $params); } } diff --git a/kirby/src/Cms/Section.php b/kirby/src/Cms/Section.php index 51ea070..42ee388 100644 --- a/kirby/src/Cms/Section.php +++ b/kirby/src/Cms/Section.php @@ -2,6 +2,7 @@ namespace Kirby\Cms; +use Closure; use Kirby\Exception\InvalidArgumentException; use Kirby\Toolkit\Component; @@ -18,24 +19,15 @@ class Section extends Component { /** * Registry for all component mixins - * - * @var array */ - public static $mixins = []; + public static array $mixins = []; /** * Registry for all component types - * - * @var array */ - public static $types = []; - + public static array $types = []; /** - * Section constructor. - * - * @param string $type - * @param array $attrs * @throws \Kirby\Exception\InvalidArgumentException */ public function __construct(string $type, array $attrs = []) @@ -44,7 +36,7 @@ class Section extends Component throw new InvalidArgumentException('Undefined section model'); } - if ($attrs['model'] instanceof Model === false) { + if ($attrs['model'] instanceof ModelWithContent === false) { throw new InvalidArgumentException('Invalid section model'); } @@ -55,6 +47,21 @@ class Section extends Component parent::__construct($type, $attrs); } + /** + * Returns field api call + */ + public function api(): mixed + { + if ( + isset($this->options['api']) === true && + $this->options['api'] instanceof Closure + ) { + return $this->options['api']->call($this); + } + + return null; + } + public function errors(): array { if (array_key_exists('errors', $this->methods) === true) { @@ -64,25 +71,16 @@ class Section extends Component return $this->errors ?? []; } - /** - * @return \Kirby\Cms\App - */ - public function kirby() + public function kirby(): App { return $this->model()->kirby(); } - /** - * @return \Kirby\Cms\Model - */ - public function model() + public function model(): ModelWithContent { return $this->model; } - /** - * @return array - */ public function toArray(): array { $array = parent::toArray(); @@ -92,9 +90,6 @@ class Section extends Component return $array; } - /** - * @return array - */ public function toResponse(): array { return array_merge([ diff --git a/kirby/src/Cms/Site.php b/kirby/src/Cms/Site.php index b27bdc3..cab166d 100644 --- a/kirby/src/Cms/Site.php +++ b/kirby/src/Cms/Site.php @@ -22,87 +22,83 @@ use Kirby\Toolkit\A; */ class Site extends ModelWithContent { - use SiteActions; use HasChildren; use HasFiles; use HasMethods; + use SiteActions; public const CLASS_ALIAS = 'site'; /** * The SiteBlueprint object - * - * @var \Kirby\Cms\SiteBlueprint */ - protected $blueprint; + protected SiteBlueprint|null $blueprint = null; /** * The error page object - * - * @var \Kirby\Cms\Page */ - protected $errorPage; + protected Page|null $errorPage = null; /** * The id of the error page, which is * fetched in the errorPage method - * - * @var string */ - protected $errorPageId = 'error'; + protected string $errorPageId; /** * The home page object - * - * @var \Kirby\Cms\Page */ - protected $homePage; + protected Page|null $homePage = null; /** * The id of the home page, which is * fetched in the errorPage method - * - * @var string */ - protected $homePageId = 'home'; + protected string $homePageId; /** * Cache for the inventory array - * - * @var array */ - protected $inventory; + protected array|null $inventory = null; /** * The current page object - * - * @var \Kirby\Cms\Page */ - protected $page; + protected Page|null $page; /** * The absolute path to the site directory - * - * @var string */ - protected $root; + protected string $root; /** * The page url - * - * @var string */ - protected $url; + protected string|null $url; + + /** + * Creates a new Site object + */ + public function __construct(array $props = []) + { + parent::__construct($props); + + $this->errorPageId = $props['errorPageId'] ?? 'error'; + $this->homePageId = $props['homePageId'] ?? 'home'; + $this->page = $props['page'] ?? null; + $this->url = $props['url'] ?? null; + + $this->setBlueprint($props['blueprint'] ?? null); + $this->setChildren($props['children'] ?? null); + $this->setDrafts($props['drafts'] ?? null); + $this->setFiles($props['files'] ?? null); + } /** * Modified getter to also return fields * from the content - * - * @param string $method - * @param array $arguments - * @return mixed */ - public function __call(string $method, array $arguments = []) + public function __call(string $method, array $arguments = []): mixed { // public property access if (isset($this->$method) === true) { @@ -118,20 +114,9 @@ class Site extends ModelWithContent return $this->content()->get($method); } - /** - * Creates a new Site object - * - * @param array $props - */ - public function __construct(array $props = []) - { - $this->setProperties($props); - } - /** * Improved `var_dump` output - * - * @return array + * @codeCoverageIgnore */ public function __debugInfo(): array { @@ -145,8 +130,6 @@ class Site extends ModelWithContent /** * Makes it possible to convert the site model * to a string. Mostly useful for debugging. - * - * @return string */ public function __toString(): string { @@ -155,10 +138,7 @@ class Site extends ModelWithContent /** * Returns the url to the api endpoint - * * @internal - * @param bool $relative - * @return string */ public function apiUrl(bool $relative = false): string { @@ -171,10 +151,8 @@ class Site extends ModelWithContent /** * Returns the blueprint object - * - * @return \Kirby\Cms\SiteBlueprint */ - public function blueprint() + public function blueprint(): SiteBlueprint { if ($this->blueprint instanceof SiteBlueprint) { return $this->blueprint; @@ -185,10 +163,8 @@ class Site extends ModelWithContent /** * Builds a breadcrumb collection - * - * @return \Kirby\Cms\Pages */ - public function breadcrumb() + public function breadcrumb(): Pages { // get all parents and flip the order $crumb = $this->page()->parents()->flip(); @@ -204,53 +180,39 @@ class Site extends ModelWithContent /** * Prepares the content for the write method - * * @internal - * @param array $data - * @param string|null $languageCode - * @return array */ - public function contentFileData(array $data, string|null $languageCode = null): array - { - return A::prepend($data, [ - 'title' => $data['title'] ?? null, - ]); + public function contentFileData( + array $data, + string|null $languageCode = null + ): array { + return A::prepend($data, ['title' => $data['title'] ?? null]); } /** * Filename for the content file - * * @internal - * @return string + * @deprecated 4.0.0 + * @todo Remove in v5 + * @codeCoverageIgnore */ public function contentFileName(): string { + Helpers::deprecated('The internal $model->contentFileName() method has been deprecated. Please let us know via a GitHub issue if you need this method and tell us your use case.', 'model-content-file'); return 'site'; } /** * Returns the error page object - * - * @return \Kirby\Cms\Page|null */ - public function errorPage() + public function errorPage(): Page|null { - if ($this->errorPage instanceof Page) { - return $this->errorPage; - } - - if ($error = $this->find($this->errorPageId())) { - return $this->errorPage = $error; - } - - return null; + return $this->errorPage ??= $this->find($this->errorPageId()); } /** * Returns the global error page id - * * @internal - * @return string */ public function errorPageId(): string { @@ -259,8 +221,6 @@ class Site extends ModelWithContent /** * Checks if the site exists on disk - * - * @return bool */ public function exists(): bool { @@ -269,27 +229,15 @@ class Site extends ModelWithContent /** * Returns the home page object - * - * @return \Kirby\Cms\Page|null */ - public function homePage() + public function homePage(): Page|null { - if ($this->homePage instanceof Page) { - return $this->homePage; - } - - if ($home = $this->find($this->homePageId())) { - return $this->homePage = $home; - } - - return null; + return $this->homePage ??= $this->find($this->homePageId()); } /** * Returns the global home page id - * * @internal - * @return string */ public function homePageId(): string { @@ -299,9 +247,7 @@ class Site extends ModelWithContent /** * Creates an inventory of all files * and children in the site directory - * * @internal - * @return array */ public function inventory(): array { @@ -323,7 +269,6 @@ class Site extends ModelWithContent * Compares the current object with the given site object * * @param mixed $site - * @return bool */ public function is($site): bool { @@ -336,9 +281,7 @@ class Site extends ModelWithContent /** * Returns the root to the media folder for the site - * * @internal - * @return string */ public function mediaRoot(): string { @@ -347,9 +290,7 @@ class Site extends ModelWithContent /** * The site's base url for any files - * * @internal - * @return string */ public function mediaUrl(): string { @@ -359,18 +300,12 @@ class Site extends ModelWithContent /** * Gets the last modification date of all pages * in the content folder. - * - * @param string|null $format - * @param string|null $handler - * @return int|string */ - public function modified(string|null $format = null, string|null $handler = null) - { - return Dir::modified( - $this->root(), - $format, - $handler ?? $this->kirby()->option('date.handler', 'date') - ); + public function modified( + string|null $format = null, + string|null $handler = null + ): int|string { + return Dir::modified($this->root(), $format, $handler); } /** @@ -384,9 +319,8 @@ class Site extends ModelWithContent * * @param string|null $path omit for current page, * otherwise e.g. `notes/across-the-ocean` - * @return \Kirby\Cms\Page|null */ - public function page(string|null $path = null) + public function page(string|null $path = null): Page|null { if ($path !== null) { return $this->find($path); @@ -405,39 +339,31 @@ class Site extends ModelWithContent /** * Alias for `Site::children()` - * - * @return \Kirby\Cms\Pages */ - public function pages() + public function pages(): Pages { return $this->children(); } /** * Returns the panel info object - * - * @return \Kirby\Panel\Site */ - public function panel() + public function panel(): Panel { return new Panel($this); } /** * Returns the permissions object for this site - * - * @return \Kirby\Cms\SitePermissions */ - public function permissions() + public function permissions(): SitePermissions { return new SitePermissions($this); } /** * Preview Url - * * @internal - * @return string|null */ public function previewUrl(): string|null { @@ -458,8 +384,6 @@ class Site extends ModelWithContent /** * Returns the absolute path to the content directory - * - * @return string */ public function root(): string { @@ -470,22 +394,16 @@ class Site extends ModelWithContent * Returns the SiteRules class instance * which is being used in various methods * to check for valid actions and input. - * - * @return \Kirby\Cms\SiteRules */ - protected function rules() + protected function rules(): SiteRules { return new SiteRules(); } /** * Search all pages in the site - * - * @param string|null $query - * @param array $params - * @return \Kirby\Cms\Pages */ - public function search(string|null $query = null, $params = []) + public function search(string|null $query = null, string|array $params = []): Pages { return $this->index()->search($query, $params); } @@ -493,10 +411,9 @@ class Site extends ModelWithContent /** * Sets the Blueprint object * - * @param array|null $blueprint * @return $this */ - protected function setBlueprint(array|null $blueprint = null) + protected function setBlueprint(array|null $blueprint = null): static { if ($blueprint !== null) { $blueprint['model'] = $this; @@ -506,86 +423,25 @@ class Site extends ModelWithContent return $this; } - /** - * Sets the id of the error page, which - * is used in the errorPage method - * to get the default error page if nothing - * else is set. - * - * @param string $id - * @return $this - */ - protected function setErrorPageId(string $id = 'error') - { - $this->errorPageId = $id; - return $this; - } - - /** - * Sets the id of the home page, which - * is used in the homePage method - * to get the default home page if nothing - * else is set. - * - * @param string $id - * @return $this - */ - protected function setHomePageId(string $id = 'home') - { - $this->homePageId = $id; - return $this; - } - - /** - * Sets the current page object - * - * @internal - * @param \Kirby\Cms\Page|null $page - * @return $this - */ - public function setPage(?Page $page = null) - { - $this->page = $page; - return $this; - } - - /** - * Sets the Url - * - * @param string|null $url - * @return $this - */ - protected function setUrl(string|null $url = null) - { - $this->url = $url; - return $this; - } - /** * Converts the most important site * properties to an array - * - * @return array */ public function toArray(): array { - return [ - 'children' => $this->children()->keys(), - 'content' => $this->content()->toArray(), - 'errorPage' => $this->errorPage() ? $this->errorPage()->id() : false, - 'files' => $this->files()->keys(), - 'homePage' => $this->homePage() ? $this->homePage()->id() : false, - 'page' => $this->page() ? $this->page()->id() : false, - 'title' => $this->title()->value(), - 'url' => $this->url(), - ]; + return array_merge(parent::toArray(), [ + 'children' => $this->children()->keys(), + 'errorPage' => $this->errorPage()?->id() ?? false, + 'files' => $this->files()->keys(), + 'homePage' => $this->homePage()?->id() ?? false, + 'page' => $this->page()?->id() ?? false, + 'title' => $this->title()->value(), + 'url' => $this->url(), + ]); } /** * Returns the Url - * - * @param string|null $language - * @return string */ public function url(string|null $language = null): string { @@ -598,14 +454,12 @@ class Site extends ModelWithContent /** * Returns the translated url - * * @internal - * @param string|null $languageCode - * @param array|null $options - * @return string */ - public function urlForLanguage(string|null $languageCode = null, array|null $options = null): string - { + public function urlForLanguage( + string|null $languageCode = null, + array|null $options = null + ): string { if ($language = $this->kirby()->language($languageCode)) { return $language->url(); } @@ -617,21 +471,19 @@ class Site extends ModelWithContent * Sets the current page by * id or page object and * returns the current page - * * @internal - * @param string|\Kirby\Cms\Page $page - * @param string|null $languageCode - * @return \Kirby\Cms\Page */ - public function visit($page, string|null $languageCode = null) - { + public function visit( + string|Page $page, + string|null $languageCode = null + ): Page { if ($languageCode !== null) { $this->kirby()->setCurrentTranslation($languageCode); $this->kirby()->setCurrentLanguage($languageCode); } // convert ids to a Page object - if (is_string($page)) { + if (is_string($page) === true) { $page = $this->find($page); } @@ -640,22 +492,16 @@ class Site extends ModelWithContent throw new InvalidArgumentException('Invalid page object'); } - // set the current active page - $this->setPage($page); - - // return the page - return $page; + // set and return the current active page + return $this->page = $page; } /** * Checks if any content of the site has been * modified after the given unix timestamp * This is mainly used to auto-update the cache - * - * @param mixed $time - * @return bool */ - public function wasModifiedAfter($time): bool + public function wasModifiedAfter(int $time): bool { return Dir::wasModifiedAfter($this->root(), $time); } diff --git a/kirby/src/Cms/SiteActions.php b/kirby/src/Cms/SiteActions.php index dd1ceb9..9d67d5d 100644 --- a/kirby/src/Cms/SiteActions.php +++ b/kirby/src/Cms/SiteActions.php @@ -23,14 +23,12 @@ trait SiteActions * 3. commits the store action * 4. sends the after hook * 5. returns the result - * - * @param string $action - * @param mixed ...$arguments - * @param Closure $callback - * @return mixed */ - protected function commit(string $action, array $arguments, Closure $callback) - { + protected function commit( + string $action, + array $arguments, + Closure $callback + ): mixed { $old = $this->hardcopy(); $kirby = $this->kirby(); $argumentValues = array_values($arguments); @@ -48,29 +46,35 @@ trait SiteActions /** * Change the site title - * - * @param string $title - * @param string|null $languageCode - * @return static */ - public function changeTitle(string $title, string $languageCode = null) - { - $site = $this; - $title = trim($title); - $arguments = compact('site', 'title', 'languageCode'); + public function changeTitle( + string $title, + string|null $languageCode = null + ): static { + // if the `$languageCode` argument is not set and is not the default language + // the `$languageCode` argument is sent as the current language + if ( + $languageCode === null && + $language = $this->kirby()->language() + ) { + if ($language->isDefault() === false) { + $languageCode = $language->code(); + } + } - return $this->commit('changeTitle', $arguments, function ($site, $title, $languageCode) { - return $site->save(['title' => $title], $languageCode); - }); + $arguments = ['site' => $this, 'title' => trim($title), 'languageCode' => $languageCode]; + + return $this->commit( + 'changeTitle', + $arguments, + fn ($site, $title, $languageCode) => $site->save(['title' => $title], $languageCode) + ); } /** * Creates a main page - * - * @param array $props - * @return \Kirby\Cms\Page */ - public function createChild(array $props) + public function createChild(array $props): Page { $props = array_merge($props, [ 'url' => null, @@ -87,14 +91,16 @@ trait SiteActions * * @return $this */ - public function purge() + public function purge(): static { - $this->blueprint = null; - $this->children = null; - $this->content = null; - $this->files = null; - $this->inventory = null; - $this->translations = null; + parent::purge(); + + $this->blueprint = null; + $this->children = null; + $this->childrenAndDrafts = null; + $this->drafts = null; + $this->files = null; + $this->inventory = null; return $this; } diff --git a/kirby/src/Cms/SiteBlueprint.php b/kirby/src/Cms/SiteBlueprint.php index ce38ab9..65b179c 100644 --- a/kirby/src/Cms/SiteBlueprint.php +++ b/kirby/src/Cms/SiteBlueprint.php @@ -17,8 +17,6 @@ class SiteBlueprint extends Blueprint /** * Creates a new page blueprint object * with the given props - * - * @param array $props */ public function __construct(array $props) { @@ -44,10 +42,8 @@ class SiteBlueprint extends Blueprint * The preview setting controls the "Open" * button in the panel and redirects it to a * different URL if necessary. - * - * @return string|bool */ - public function preview() + public function preview(): string|bool { $preview = $this->props['options']['preview'] ?? true; @@ -55,6 +51,6 @@ class SiteBlueprint extends Blueprint return $this->model->toString($preview); } - return $preview; + return $this->model->permissions()->can('preview', true); } } diff --git a/kirby/src/Cms/SitePermissions.php b/kirby/src/Cms/SitePermissions.php index b6ce350..8e58415 100644 --- a/kirby/src/Cms/SitePermissions.php +++ b/kirby/src/Cms/SitePermissions.php @@ -13,5 +13,5 @@ namespace Kirby\Cms; */ class SitePermissions extends ModelPermissions { - protected $category = 'site'; + protected string $category = 'site'; } diff --git a/kirby/src/Cms/SiteRules.php b/kirby/src/Cms/SiteRules.php index 07db1b9..08da997 100644 --- a/kirby/src/Cms/SiteRules.php +++ b/kirby/src/Cms/SiteRules.php @@ -20,9 +20,6 @@ class SiteRules /** * Validates if the site title can be changed * - * @param \Kirby\Cms\Site $site - * @param string $title - * @return bool * @throws \Kirby\Exception\InvalidArgumentException If the title is empty * @throws \Kirby\Exception\PermissionException If the user is not allowed to change the title */ @@ -42,9 +39,6 @@ class SiteRules /** * Validates if the site can be updated * - * @param \Kirby\Cms\Site $site - * @param array $content - * @return bool * @throws \Kirby\Exception\PermissionException If the user is not allowed to update the site */ public static function update(Site $site, array $content = []): bool diff --git a/kirby/src/Cms/Structure.php b/kirby/src/Cms/Structure.php index 791f4dd..efdfad1 100644 --- a/kirby/src/Cms/Structure.php +++ b/kirby/src/Cms/Structure.php @@ -2,8 +2,6 @@ namespace Kirby\Cms; -use Kirby\Exception\InvalidArgumentException; - /** * The Structure class wraps * array data into a nicely chainable @@ -18,56 +16,38 @@ use Kirby\Exception\InvalidArgumentException; * @copyright Bastian Allgeier * @license https://getkirby.com/license */ -class Structure extends Collection +class Structure extends Items { + public const ITEM_CLASS = StructureObject::class; + /** * All registered structure methods - * - * @var array */ - public static $methods = []; + public static array $methods = []; /** - * Creates a new Collection with the given objects - * - * @param array $objects Kirby\Cms\StructureObject` objects or props arrays - * @param object|null $parent + * Creates a new structure collection from a + * an array of item props */ - public function __construct($objects = [], $parent = null) - { - $this->parent = $parent; - $this->set($objects); - } + public static function factory( + array|null $items = null, + array $params = [] + ): static { + if (is_array($items) === true) { + $items = array_map(function ($item, $index) { + if (is_array($item) === true) { + // pass a clean content array without special `Item` keys + $item['content'] = $item; - /** - * The internal setter for collection items. - * This makes sure that nothing unexpected ends - * up in the collection. You can pass arrays or - * StructureObjects - * - * @param string $id - * @param array|StructureObject $props - * @return void - * - * @throws \Kirby\Exception\InvalidArgumentException - */ - public function __set(string $id, $props): void - { - if ($props instanceof StructureObject) { - $object = $props; - } else { - if (is_array($props) === false) { - throw new InvalidArgumentException('Invalid structure data'); - } + // bake-in index as ID for all items + // TODO: remove when adding UUID supports to Structures + $item['id'] ??= $index; + } - $object = new StructureObject([ - 'content' => $props, - 'id' => $props['id'] ?? $id, - 'parent' => $this->parent, - 'structure' => $this - ]); + return $item; + }, $items, array_keys($items)); } - parent::__set($object->id(), $object); + return parent::factory($items, $params); } } diff --git a/kirby/src/Cms/StructureObject.php b/kirby/src/Cms/StructureObject.php index 0805707..9806b7d 100644 --- a/kirby/src/Cms/StructureObject.php +++ b/kirby/src/Cms/StructureObject.php @@ -2,6 +2,8 @@ namespace Kirby\Cms; +use Kirby\Content\Content; + /** * The StructureObject represents each item * in a Structure collection. StructureObjects @@ -18,44 +20,38 @@ namespace Kirby\Cms; * @copyright Bastian Allgeier * @license https://getkirby.com/license */ -class StructureObject extends Model +class StructureObject extends Item { - use HasSiblings; + use HasMethods; + + public const ITEMS_CLASS = Structure::class; + + protected Content $content; /** - * The content - * - * @var Content + * Creates a new StructureObject with the given props */ - protected $content; + public function __construct(array $params = []) + { + parent::__construct($params); - /** - * @var string - */ - protected $id; - - /** - * @var \Kirby\Cms\Site|\Kirby\Cms\Page|\Kirby\Cms\File|\Kirby\Cms\User|null - */ - protected $parent; - - /** - * The parent Structure collection - * - * @var Structure - */ - protected $structure; + $this->content = new Content( + $params['content'] ?? $params['params'] ?? [], + $this->parent + ); + } /** * Modified getter to also return fields * from the object's content - * - * @param string $method - * @param array $arguments - * @return mixed */ - public function __call(string $method, array $arguments = []) + public function __call(string $method, array $args = []): mixed { + // structure object methods + if ($this->hasMethod($method) === true) { + return $this->callMethod($method, $args); + } + // public property access if (isset($this->$method) === true) { return $this->$method; @@ -64,146 +60,26 @@ class StructureObject extends Model return $this->content()->get($method); } - /** - * Creates a new StructureObject with the given props - * - * @param array $props - */ - public function __construct(array $props) - { - $this->setProperties($props); - } - /** * Returns the content - * - * @return \Kirby\Cms\Content */ - public function content() + public function content(): Content { - if ($this->content instanceof Content) { - return $this->content; - } - - if (is_array($this->content) !== true) { - $this->content = []; - } - - return $this->content = new Content($this->content, $this->parent()); - } - - /** - * Returns the required id - * - * @return string - */ - public function id(): string - { - return $this->id; - } - - /** - * Compares the current object with the given structure object - * - * @param mixed $structure - * @return bool - */ - public function is($structure): bool - { - if ($structure instanceof self === false) { - return false; - } - - return $this === $structure; - } - - /** - * Returns the parent Model object - * - * @return \Kirby\Cms\Model - */ - public function parent() - { - return $this->parent; - } - - /** - * Sets the Content object with the given parent - * - * @param array|null $content - * @return $this - */ - protected function setContent(array $content = null) - { - $this->content = $content; - return $this; - } - - /** - * Sets the id of the object. - * The id is required. The structure - * class will use the index, if no id is - * specified. - * - * @param string $id - * @return $this - */ - protected function setId(string $id) - { - $this->id = $id; - return $this; - } - - /** - * Sets the parent Model - * - * @return $this - * @param \Kirby\Cms\Site|\Kirby\Cms\Page|\Kirby\Cms\File|\Kirby\Cms\User|null $parent - */ - protected function setParent(Model $parent = null) - { - $this->parent = $parent; - return $this; - } - - /** - * Sets the parent Structure collection - * - * @param \Kirby\Cms\Structure|null $structure - * @return $this - */ - protected function setStructure(Structure $structure = null) - { - $this->structure = $structure; - return $this; - } - - /** - * Returns the parent Structure collection as siblings - * - * @return \Kirby\Cms\Structure - */ - protected function siblingsCollection() - { - return $this->structure; + return $this->content; } /** * Converts all fields in the object to a * plain associative array. The id is - * injected into the array afterwards + * injected from the parent into the array * to make sure it's always present and - * not overloaded in the content. - * - * @return array + * not overloaded by the content. */ public function toArray(): array { - $array = $this->content()->toArray(); - $array['id'] = $this->id(); - - ksort($array); - - return $array; + return array_merge( + $this->content()->toArray(), + parent::toArray() + ); } } diff --git a/kirby/src/Cms/System.php b/kirby/src/Cms/System.php index ad83ebf..aa6ff4e 100644 --- a/kirby/src/Cms/System.php +++ b/kirby/src/Cms/System.php @@ -3,16 +3,11 @@ namespace Kirby\Cms; use Kirby\Cms\System\UpdateStatus; -use Kirby\Data\Json; -use Kirby\Exception\Exception; use Kirby\Exception\InvalidArgumentException; use Kirby\Exception\PermissionException; use Kirby\Filesystem\Dir; -use Kirby\Filesystem\F; -use Kirby\Http\Remote; use Kirby\Toolkit\A; use Kirby\Toolkit\Str; -use Kirby\Toolkit\V; use Throwable; /** @@ -32,6 +27,7 @@ use Throwable; class System { // cache + protected License|null $license = null; protected UpdateStatus|null $updateStatus = null; public function __construct(protected App $app) @@ -79,11 +75,14 @@ class System switch ($folder) { case 'content': - return $url . '/' . basename($this->app->site()->contentFile()); + return $url . '/' . basename($this->app->site()->storage()->contentFile( + 'published', + 'default' + )); case 'git': return $url . '/config'; case 'kirby': - return $url . '/composer.json'; + return $url . '/LICENSE.md'; case 'site': $root = $this->app->root('site'); $files = glob($root . '/blueprints/*.yml'); @@ -163,6 +162,24 @@ class System ->toString(); } + /** + * Returns an array with relevant system information + * used for debugging + * @since 4.3.0 + */ + public function info(): array + { + return [ + 'kirby' => $this->app->version(), + 'php' => phpversion(), + 'server' => $this->serverSoftware(), + 'license' => $this->license()->label(), + 'languages' => $this->app->languages()->values( + fn ($lang) => $lang->code() + ) + ]; + } + /** * Create the most important folders * if they don't exist yet @@ -201,7 +218,25 @@ class System } /** - * Check if the panel is installable. + * Check if the Panel has 2FA activated + */ + public function is2FA(): bool + { + return ($this->loginMethods()['password']['2fa'] ?? null) === true; + } + + /** + * Check if the Panel has 2FA with TOTP activated + */ + public function is2FAWithTOTP(): bool + { + return + $this->is2FA() === true && + in_array('totp', $this->app->auth()->enabledChallenges()) === true; + } + + /** + * Check if the Panel is installable. * On a public server the panel.install * option must be explicitly set to true * to get the installer up and running. @@ -240,99 +275,10 @@ class System /** * Loads the license file and returns * the license information if available - * - * @return string|bool License key or `false` if the current user has - * permissions for access.settings, otherwise just a - * boolean that tells whether a valid license is active */ - public function license() + public function license(): License { - try { - $license = Json::read($this->app->root('license')); - } catch (Throwable) { - return false; - } - - // check for all required fields for the validation - if (isset( - $license['license'], - $license['order'], - $license['date'], - $license['email'], - $license['domain'], - $license['signature'] - ) !== true) { - return false; - } - - // build the license verification data - $data = [ - 'license' => $license['license'], - 'order' => $license['order'], - 'email' => hash('sha256', $license['email'] . 'kwAHMLyLPBnHEskzH9pPbJsBxQhKXZnX'), - 'domain' => $license['domain'], - 'date' => $license['date'] - ]; - - - // get the public key - $pubKey = F::read($this->app->root('kirby') . '/kirby.pub'); - - // verify the license signature - $data = json_encode($data); - $signature = hex2bin($license['signature']); - if (openssl_verify($data, $signature, $pubKey, 'RSA-SHA256') !== 1) { - return false; - } - - // verify the URL - if ($this->licenseUrl() !== $this->licenseUrl($license['domain'])) { - return false; - } - - // only return the actual license key if the - // current user has appropriate permissions - if ($this->app->user()?->isAdmin() === true) { - return $license['license']; - } - - return true; - } - - /** - * Normalizes the app's index URL for - * licensing purposes - * - * @param string|null $url Input URL, by default the app's index URL - * @return string Normalized URL - */ - protected function licenseUrl(string $url = null): string - { - $url ??= $this->indexUrl(); - - // remove common "testing" subdomains as well as www. - // to ensure that installations of the same site have - // the same license URL; only for installations at /, - // subdirectory installations are difficult to normalize - if (Str::contains($url, '/') === false) { - if (Str::startsWith($url, 'www.')) { - return substr($url, 4); - } - - if (Str::startsWith($url, 'dev.')) { - return substr($url, 4); - } - - if (Str::startsWith($url, 'test.')) { - return substr($url, 5); - } - - if (Str::startsWith($url, 'staging.')) { - return substr($url, 8); - } - } - - return $url; + return $this->license ??= License::read(); } /** @@ -418,7 +364,7 @@ class System { return version_compare(PHP_VERSION, '8.1.0', '>=') === true && - version_compare(PHP_VERSION, '8.4.0', '<') === true; + version_compare(PHP_VERSION, '8.5.0', '<') === true; } /** @@ -439,77 +385,34 @@ class System * @throws \Kirby\Exception\Exception * @throws \Kirby\Exception\InvalidArgumentException */ - public function register(string $license = null, string $email = null): bool + public function register(string|null $license = null, string|null $email = null): bool { - if (Str::startsWith($license, 'K3-PRO-') === false) { - throw new InvalidArgumentException(['key' => 'license.format']); - } - - if (V::email($email) === false) { - throw new InvalidArgumentException(['key' => 'license.email']); - } - - // @codeCoverageIgnoreStart - $response = Remote::get('https://hub.getkirby.com/register', [ - 'data' => [ - 'license' => $license, - 'email' => Str::lower(trim($email)), - 'domain' => $this->indexUrl() - ] - ]); - - if ($response->code() !== 200) { - throw new Exception($response->content()); - } - - // decode the response - $json = Json::decode($response->content()); - - // replace the email with the plaintext version - $json['email'] = $email; - - // where to store the license file - $file = $this->app->root('license'); - - // save the license information - Json::write($file, $json); - - if ($this->license() === false) { - throw new InvalidArgumentException([ - 'key' => 'license.verification' - ]); - } - // @codeCoverageIgnoreEnd + $license = new License( + code: $license, + domain: $this->indexUrl(), + email: $email, + ); + $this->license = $license->register(); return true; } - /** - * Check for a valid server environment - */ - public function server(): bool - { - return $this->serverSoftware() !== null; - } - /** * Returns the detected server software */ - public function serverSoftware(): string|null + public function serverSoftware(): string { - $servers = $this->app->option('servers', [ - 'apache', - 'caddy', - 'litespeed', - 'nginx', - 'php' - ]); + return $this->app->environment()->get('SERVER_SOFTWARE', '–'); + } - $software = $this->app->environment()->get('SERVER_SOFTWARE', ''); - - preg_match('!(' . implode('|', A::wrap($servers)) . ')!i', $software, $matches); - - return $matches[0] ?? null; + /** + * Returns the short version of the detected server software + * @since 4.6.0 + */ + public function serverSoftwareShort(): string + { + $software = $this->serverSoftware(); + return strtok($software, ' '); } /** @@ -526,14 +429,13 @@ class System public function status(): array { return [ - 'accounts' => $this->accounts(), - 'content' => $this->content(), - 'curl' => $this->curl(), - 'sessions' => $this->sessions(), - 'mbstring' => $this->mbstring(), - 'media' => $this->media(), - 'php' => $this->php(), - 'server' => $this->server(), + 'accounts' => $this->accounts(), + 'content' => $this->content(), + 'curl' => $this->curl(), + 'sessions' => $this->sessions(), + 'mbstring' => $this->mbstring(), + 'media' => $this->media(), + 'php' => $this->php() ]; } @@ -607,6 +509,7 @@ class System /** * Improved `var_dump` output + * @codeCoverageIgnore */ public function __debugInfo(): array { diff --git a/kirby/src/Cms/System/UpdateStatus.php b/kirby/src/Cms/System/UpdateStatus.php index 0daa25b..0579f68 100644 --- a/kirby/src/Cms/System/UpdateStatus.php +++ b/kirby/src/Cms/System/UpdateStatus.php @@ -26,7 +26,7 @@ class UpdateStatus /** * Host to request the update data from */ - public static string $host = 'https://assets.getkirby.com'; + public static string $host = 'https://getkirby.com'; /** * Marker that stores whether a previous remote @@ -158,7 +158,9 @@ class UpdateStatus // collect all matching custom messages $filters = [ 'kirby' => $this->app->version(), - 'php' => phpversion() + // some PHP version strings contain extra info that makes them + // invalid so we need to strip it off + 'php' => preg_replace('/^([^~+-]+).*$/', '$1', phpversion()) ]; if ($type === 'plugin') { @@ -390,6 +392,46 @@ class UpdateStatus }); } + /** + * Finds the maximum possible major update + * that is included with the current license + * + * @return string|null Version number of the update or + * `null` if no free update is possible + */ + protected function findMaximumFreeUpdate(): string|null + { + // get the timestamp of included updates + $renewal = $this->app->system()->license()->renewal(); + + if ($renewal === null || $this->data === null) { + return null; + } + + foreach ($this->data['versions'] ?? [] as $entry) { + $initialRelease = $entry['initialRelease'] ?? null; + $latest = $entry['latest'] ?? ''; + + // skip entries of irrelevant releases + if ( + is_string($initialRelease) !== true || + version_compare($latest, $this->currentVersion, '<=') === true + ) { + continue; + } + + $timestamp = strtotime($initialRelease); + + // update is free if the initial release was before the + // license renewal date + if (is_int($timestamp) === true && $timestamp < $renewal) { + return $latest; + } + } + + return null; + } + /** * Finds the minimum possible security update * to fix all known vulnerabilities @@ -653,7 +695,7 @@ class UpdateStatus ]; } - // check if free updates are possible from the current version + // check if updates within the same major version are possible $latest = $versionEntry['latest'] ?? null; if (is_string($latest) === true && $latest !== $this->currentVersion) { return $this->targetData = [ @@ -663,6 +705,19 @@ class UpdateStatus ]; } + // check if the license includes updates to a newer major version + if ($version = $this->findMaximumFreeUpdate()) { + // extract the part before the first dot + // to find the major release page URL + preg_match('/^(\w+)\./', $version, $matches); + + return $this->targetData = [ + 'status' => 'update', + 'url' => $this->urlFor($matches[1] . '.0', 'changes'), + 'version' => $version + ]; + } + // no free update is possible, but we are not on the latest version, // so the overall latest version must be an upgrade return $this->targetData = [ diff --git a/kirby/src/Cms/Translation.php b/kirby/src/Cms/Translation.php index 783bcfb..d71d336 100644 --- a/kirby/src/Cms/Translation.php +++ b/kirby/src/Cms/Translation.php @@ -18,30 +18,15 @@ use Kirby\Toolkit\Str; */ class Translation { - /** - * @var string - */ - protected $code; - - /** - * @var array - */ - protected $data = []; - - /** - * @param string $code - * @param array $data - */ - public function __construct(string $code, array $data) - { - $this->code = $code; - $this->data = $data; + public function __construct( + protected string $code, + protected array $data + ) { } /** * Improved `var_dump` output - * - * @return array + * @codeCoverageIgnore */ public function __debugInfo(): array { @@ -50,8 +35,6 @@ class Translation /** * Returns the translation author - * - * @return string */ public function author(): string { @@ -60,8 +43,6 @@ class Translation /** * Returns the official translation code - * - * @return string */ public function code(): string { @@ -71,8 +52,6 @@ class Translation /** * Returns an array with all * translation strings - * - * @return array */ public function data(): array { @@ -82,8 +61,6 @@ class Translation /** * Returns the translation data and merges * it with the data from the default translation - * - * @return array */ public function dataWithFallback(): array { @@ -100,8 +77,6 @@ class Translation /** * Returns the writing direction * (ltr or rtl) - * - * @return string */ public function direction(): string { @@ -111,12 +86,8 @@ class Translation /** * Returns a single translation * string by key - * - * @param string $key - * @param string|null $default - * @return string|null */ - public function get(string $key, string $default = null): string|null + public function get(string $key, string|null $default = null): string|null { return $this->data[$key] ?? $default; } @@ -124,8 +95,6 @@ class Translation /** * Returns the translation id, * which is also the code - * - * @return string */ public function id(): string { @@ -135,14 +104,12 @@ class Translation /** * Loads the translation from the * json file in Kirby's translations folder - * - * @param string $code - * @param string $root - * @param array $inject - * @return static */ - public static function load(string $code, string $root, array $inject = []) - { + public static function load( + string $code, + string $root, + array $inject = [] + ): static { try { $data = array_merge(Data::read($root), $inject); } catch (Exception) { @@ -154,8 +121,6 @@ class Translation /** * Returns the PHP locale of the translation - * - * @return string */ public function locale(): string { @@ -169,8 +134,6 @@ class Translation /** * Returns the human-readable translation name. - * - * @return string */ public function name(): string { @@ -180,8 +143,6 @@ class Translation /** * Converts the most important * properties to an array - * - * @return array */ public function toArray(): array { diff --git a/kirby/src/Cms/Translations.php b/kirby/src/Cms/Translations.php index b31f204..40c0b55 100644 --- a/kirby/src/Cms/Translations.php +++ b/kirby/src/Cms/Translations.php @@ -21,34 +21,10 @@ class Translations extends Collection { /** * All registered translations methods - * - * @var array */ - public static $methods = []; + public static array $methods = []; - /** - * @param string $code - * @return void - */ - public function start(string $code): void - { - F::move($this->parent->contentFile('', true), $this->parent->contentFile($code, true)); - } - - /** - * @param string $code - * @return void - */ - public function stop(string $code): void - { - F::move($this->parent->contentFile($code, true), $this->parent->contentFile('', true)); - } - - /** - * @param array $translations - * @return static - */ - public static function factory(array $translations) + public static function factory(array $translations): static { $collection = new static(); @@ -60,12 +36,7 @@ class Translations extends Collection return $collection; } - /** - * @param string $root - * @param array $inject - * @return static - */ - public static function load(string $root, array $inject = []) + public static function load(string $root, array $inject = []): static { $collection = new static(); diff --git a/kirby/src/Cms/Url.php b/kirby/src/Cms/Url.php index f10604f..eb5036f 100644 --- a/kirby/src/Cms/Url.php +++ b/kirby/src/Cms/Url.php @@ -3,6 +3,7 @@ namespace Kirby\Cms; use Kirby\Http\Url as BaseUrl; +use Kirby\Toolkit\Str; /** * The `Url` class extends the @@ -31,12 +32,34 @@ class Url extends BaseUrl return App::instance()->url(); } + /** + * Convert a string to a safe version to be used in a URL, + * obeying the `slugs.maxlength` option + * + * @param string $string The unsafe string + * @param string $separator To be used instead of space and + * other non-word characters. + * @param string $allowed List of all allowed characters (regex) + * @param int $maxlength The maximum length of the slug + * @return string The safe string + */ + public static function slug( + string|null $string = null, + string|null $separator = null, + string|null $allowed = null, + ): string { + $maxlength = App::instance()->option('slugs.maxlength', 255); + return Str::slug($string, $separator, $allowed, $maxlength); + } + /** * Creates an absolute Url to a template asset if it exists. * This is used in the `css()` and `js()` helpers */ - public static function toTemplateAsset(string $assetPath, string $extension): string|null - { + public static function toTemplateAsset( + string $assetPath, + string $extension + ): string|null { $kirby = App::instance(); $page = $kirby->site()->page(); $path = $assetPath . '/' . $page->template() . '.' . $extension; @@ -51,8 +74,10 @@ class Url extends BaseUrl * * @param array|string|null $options Either an array of options for the Uri class or a language string */ - public static function to(string|null $path = null, array|string|null $options = null): string - { + public static function to( + string|null $path = null, + array|string|null $options = null + ): string { $kirby = App::instance(); return ($kirby->component('url'))($kirby, $path, $options); } diff --git a/kirby/src/Cms/User.php b/kirby/src/Cms/User.php index 47fdb88..6b09900 100644 --- a/kirby/src/Cms/User.php +++ b/kirby/src/Cms/User.php @@ -2,9 +2,12 @@ namespace Kirby\Cms; +use Closure; use Exception; +use Kirby\Content\Field; use Kirby\Exception\InvalidArgumentException; use Kirby\Exception\NotFoundException; +use Kirby\Exception\PermissionException; use Kirby\Filesystem\Dir; use Kirby\Filesystem\F; use Kirby\Panel\User as Panel; @@ -31,81 +34,66 @@ class User extends ModelWithContent public const CLASS_ALIAS = 'user'; - /** - * @var UserBlueprint - */ - protected $blueprint; - - /** - * @var array - */ - protected $credentials; - - /** - * @var string - */ - protected $email; - - /** - * @var string - */ - protected $hash; - - /** - * @var string - */ - protected $id; - - /** - * @var array|null - */ - protected $inventory; - - /** - * @var string - */ - protected $language; - /** * All registered user methods - * - * @var array + * @todo Remove when support for PHP 8.2 is dropped */ - public static $methods = []; + public static array $methods = []; /** * Registry with all User models - * - * @var array */ - public static $models = []; + public static array $models = []; + + protected UserBlueprint|null $blueprint = null; + protected array $credentials; + protected string|null $email; + protected string $hash; + protected string $id; + protected array|null $inventory = null; + protected string|null $language; + protected Field|string|null $name; + protected string|null $password; + protected Role|string|null $role; /** - * @var \Kirby\Cms\Field + * Creates a new User object */ - protected $name; + public function __construct(array $props) + { + // helper function to easily edit values (if not null) + // before assigning them to their properties + $set = function (string $key, Closure $callback) use ($props) { + if ($value = $props[$key] ?? null) { + $value = $callback($value); + } - /** - * @var string - */ - protected $password; + return $value; + }; - /** - * The user role - * - * @var string - */ - protected $role; + // if no ID passed, generate one; + // do so before calling parent constructor + // so it also gets stored in propertyData prop + $props['id'] ??= $this->createId(); + + parent::__construct($props); + + $this->id = $props['id']; + $this->email = $set('email', fn ($email) => Str::lower(trim($email))); + $this->language = $set('language', fn ($language) => trim($language)); + $this->name = $set('name', fn ($name) => trim(strip_tags($name))); + $this->password = $props['password'] ?? null; + $this->role = $set('role', fn ($role) => Str::lower(trim($role))); + + $this->setBlueprint($props['blueprint'] ?? null); + $this->setFiles($props['files'] ?? null); + } /** * Modified getter to also return fields * from the content - * - * @param string $method - * @param array $arguments - * @return mixed */ - public function __call(string $method, array $arguments = []) + public function __call(string $method, array $arguments = []): mixed { // public property access if (isset($this->$method) === true) { @@ -121,22 +109,9 @@ class User extends ModelWithContent return $this->content()->get($method); } - /** - * Creates a new User object - * - * @param array $props - */ - public function __construct(array $props) - { - // TODO: refactor later to avoid redundant prop setting - $this->setProperty('id', $props['id'] ?? $this->createId(), true); - $this->setProperties($props); - } - /** * Improved `var_dump` output - * - * @return array + * @codeCoverageIgnore */ public function __debugInfo(): array { @@ -149,10 +124,7 @@ class User extends ModelWithContent /** * Returns the url to the api endpoint - * * @internal - * @param bool $relative - * @return string */ public function apiUrl(bool $relative = false): string { @@ -165,29 +137,21 @@ class User extends ModelWithContent /** * Returns the File object for the avatar or null - * - * @return \Kirby\Cms\File|null */ - public function avatar() + public function avatar(): File|null { return $this->files()->template('avatar')->first(); } /** * Returns the UserBlueprint object - * - * @return \Kirby\Cms\Blueprint */ - public function blueprint() + public function blueprint(): UserBlueprint { - if ($this->blueprint instanceof Blueprint) { - return $this->blueprint; - } - try { - return $this->blueprint = UserBlueprint::factory('users/' . $this->role(), 'users/default', $this); + return $this->blueprint ??= UserBlueprint::factory('users/' . $this->role(), 'users/default', $this); } catch (Exception) { - return $this->blueprint = new UserBlueprint([ + return $this->blueprint ??= new UserBlueprint([ 'model' => $this, 'name' => 'default', 'title' => 'Default', @@ -197,14 +161,13 @@ class User extends ModelWithContent /** * Prepares the content for the write method - * * @internal - * @param array $data * @param string $languageCode|null Not used so far - * @return array */ - public function contentFileData(array $data, string $languageCode = null): array - { + public function contentFileData( + array $data, + string|null $languageCode = null + ): array { // remove stuff that has nothing to do in the text files unset( $data['email'], @@ -221,10 +184,13 @@ class User extends ModelWithContent * Filename for the content file * * @internal - * @return string + * @deprecated 4.0.0 + * @todo Remove in v5 + * @codeCoverageIgnore */ public function contentFileName(): string { + Helpers::deprecated('The internal $model->contentFileName() method has been deprecated. Please let us know via a GitHub issue if you need this method and tell us your use case.', 'model-content-file'); return 'user'; } @@ -235,8 +201,6 @@ class User extends ModelWithContent /** * Returns the user email address - * - * @return string */ public function email(): string|null { @@ -245,23 +209,21 @@ class User extends ModelWithContent /** * Checks if the user exists - * - * @return bool */ public function exists(): bool { - return is_file($this->contentFile('default')) === true; + return $this->storage()->exists( + 'published', + 'default' + ); } /** * Constructs a User object and also * takes User models into account. - * * @internal - * @param mixed $props - * @return static */ - public static function factory($props) + public static function factory(mixed $props): static { if (empty($props['model']) === false) { return static::model($props['model'], $props); @@ -273,12 +235,11 @@ class User extends ModelWithContent /** * Hashes the user's password unless it is `null`, * which will leave it as `null` - * * @internal */ public static function hashPassword( #[SensitiveParameter] - string $password = null + string|null $password = null ): string|null { if ($password !== null) { $password = password_hash($password, PASSWORD_DEFAULT); @@ -289,8 +250,6 @@ class User extends ModelWithContent /** * Returns the user id - * - * @return string */ public function id(): string { @@ -300,8 +259,6 @@ class User extends ModelWithContent /** * Returns the inventory of files * children and content files - * - * @return array */ public function inventory(): array { @@ -321,11 +278,8 @@ class User extends ModelWithContent /** * Compares the current object with the given user object - * - * @param \Kirby\Cms\User|null $user - * @return bool */ - public function is(User $user = null): bool + public function is(User|null $user = null): bool { if ($user === null) { return false; @@ -336,8 +290,6 @@ class User extends ModelWithContent /** * Checks if this user has the admin role - * - * @return bool */ public function isAdmin(): bool { @@ -347,8 +299,6 @@ class User extends ModelWithContent /** * Checks if the current user is the virtual * Kirby user - * - * @return bool */ public function isKirby(): bool { @@ -357,8 +307,6 @@ class User extends ModelWithContent /** * Checks if the current user is this user - * - * @return bool */ public function isLoggedIn(): bool { @@ -368,8 +316,6 @@ class User extends ModelWithContent /** * Checks if the user is the last one * with the admin role - * - * @return bool */ public function isLastAdmin(): bool { @@ -380,8 +326,6 @@ class User extends ModelWithContent /** * Checks if the user is the last user - * - * @return bool */ public function isLastUser(): bool { @@ -391,8 +335,6 @@ class User extends ModelWithContent /** * Checks if the current user is the virtual * Nobody user - * - * @return bool */ public function isNobody(): bool { @@ -401,8 +343,6 @@ class User extends ModelWithContent /** * Returns the user language - * - * @return string */ public function language(): string { @@ -431,33 +371,43 @@ class User extends ModelWithContent * Logs the user in without checking the password * * @param \Kirby\Session\Session|array|null $session Session options or session object to set the user in - * @return void */ - public function loginPasswordless($session = null): void - { - $kirby = $this->kirby(); + public function loginPasswordless( + Session|array|null $session = null + ): void { + if ($this->id() === 'kirby') { + throw new PermissionException('The almighty user "kirby" cannot be used for login, only for raising permissions in code via `$kirby->impersonate()`'); + } + $kirby = $this->kirby(); $session = $this->sessionFromOptions($session); - $kirby->trigger('user.login:before', ['user' => $this, 'session' => $session]); + $kirby->trigger( + 'user.login:before', + ['user' => $this, 'session' => $session] + ); $session->regenerateToken(); // privilege change $session->data()->set('kirby.userId', $this->id()); + if ($this->passwordTimestamp() !== null) { $session->data()->set('kirby.loginTimestamp', time()); } - $this->kirby()->auth()->setUser($this); - $kirby->trigger('user.login:after', ['user' => $this, 'session' => $session]); + $kirby->auth()->setUser($this); + + $kirby->trigger( + 'user.login:after', + ['user' => $this, 'session' => $session] + ); } /** * Logs the user out * * @param \Kirby\Session\Session|array|null $session Session options or session object to unset the user in - * @return void */ - public function logout($session = null): void + public function logout(Session|array|null $session = null): void { $kirby = $this->kirby(); $session = $this->sessionFromOptions($session); @@ -486,9 +436,7 @@ class User extends ModelWithContent /** * Returns the root to the media folder for the user - * * @internal - * @return string */ public function mediaRoot(): string { @@ -497,9 +445,7 @@ class User extends ModelWithContent /** * Returns the media url for the user object - * * @internal - * @return string */ public function mediaUrl(): string { @@ -508,13 +454,9 @@ class User extends ModelWithContent /** * Creates a user model if it has been registered - * * @internal - * @param string $name - * @param array $props - * @return \Kirby\Cms\User */ - public static function model(string $name, array $props = []) + public static function model(string $name, array $props = []): static { if ($class = (static::$models[$name] ?? null)) { $object = new $class($props); @@ -529,47 +471,36 @@ class User extends ModelWithContent /** * Returns the last modification date of the user - * - * @param string $format - * @param string|null $handler - * @param string|null $languageCode - * @return int|string */ - public function modified(string $format = 'U', string $handler = null, string $languageCode = null) - { - $modifiedContent = F::modified($this->contentFile($languageCode)); + public function modified( + string $format = 'U', + string|null $handler = null, + string|null $languageCode = null + ): int|string|false { + $modifiedContent = $this->storage()->modified('published', $languageCode); $modifiedIndex = F::modified($this->root() . '/index.php'); $modifiedTotal = max([$modifiedContent, $modifiedIndex]); - $handler ??= $this->kirby()->option('date.handler', 'date'); return Str::date($modifiedTotal, $format, $handler); } /** * Returns the user's name - * - * @return \Kirby\Cms\Field */ - public function name() + public function name(): Field { if (is_string($this->name) === true) { return new Field($this, 'name', $this->name); } - if ($this->name !== null) { - return $this->name; - } - - return $this->name = new Field($this, 'name', $this->credentials()['name'] ?? null); + return $this->name ??= new Field($this, 'name', $this->credentials()['name'] ?? null); } /** * Returns the user's name or, * if empty, the email address - * - * @return \Kirby\Cms\Field */ - public function nameOrEmail() + public function nameOrEmail(): Field { $name = $this->name(); return $name->isNotEmpty() ? $name : new Field($this, 'email', $this->email()); @@ -577,11 +508,9 @@ class User extends ModelWithContent /** * Create a dummy nobody - * * @internal - * @return static */ - public static function nobody() + public static function nobody(): static { return new static([ 'email' => 'nobody@getkirby.com', @@ -591,26 +520,18 @@ class User extends ModelWithContent /** * Returns the panel info object - * - * @return \Kirby\Panel\User */ - public function panel() + public function panel(): Panel { return new Panel($this); } /** * Returns the encrypted user password - * - * @return string|null */ public function password(): string|null { - if ($this->password !== null) { - return $this->password; - } - - return $this->password = $this->readPassword(); + return $this->password ??= $this->readPassword(); } /** @@ -619,7 +540,7 @@ class User extends ModelWithContent */ public function passwordTimestamp(): int|null { - $file = $this->passwordFile(); + $file = $this->secretsFile(); // ensure we have the latest information // to prevent cache attacks @@ -633,20 +554,15 @@ class User extends ModelWithContent return filemtime($file); } - /** - * @return \Kirby\Cms\UserPermissions - */ - public function permissions() + public function permissions(): UserPermissions { return new UserPermissions($this); } /** * Returns the user role - * - * @return \Kirby\Cms\Role */ - public function role() + public function role(): Role { if ($this->role instanceof Role) { return $this->role; @@ -658,41 +574,28 @@ class User extends ModelWithContent } /** - * Returns all available roles - * for this user, that can be selected - * by the authenticated user + * Returns all available roles for this user, + * that the authenticated user can change to. * - * @return \Kirby\Cms\Roles + * For all roles the current user can create + * use `$kirby->roles()->canBeCreated()`. */ - public function roles() + public function roles(): Roles { $kirby = $this->kirby(); $roles = $kirby->roles(); - // a collection with just the one role of the user - $myRole = $roles->filter('id', $this->role()->id()); - - // if there's an authenticated user … - // admin users can select pretty much any role - if ($kirby->user()?->isAdmin() === true) { - // except if the user is the last admin - if ($this->isLastAdmin() === true) { - // in which case they have to stay admin - return $myRole; - } - - // return all roles for mighty admins - return $roles; + // if the authenticated user doesn't have the permission to change + // the role of this user, only the current role is available + if ($this->permissions()->can('changeRole') === false) { + return $roles->filter('id', $this->role()->id()); } - // any other user can only keep their role - return $myRole; + return $roles->canBeCreated(); } /** * The absolute path to the user directory - * - * @return string */ public function root(): string { @@ -702,21 +605,27 @@ class User extends ModelWithContent /** * Returns the UserRules class to * validate any important action. - * - * @return \Kirby\Cms\UserRules */ - protected function rules() + protected function rules(): UserRules { return new UserRules(); } + /** + * Reads a specific secret from the user secrets file on disk + * @since 4.0.0 + */ + public function secret(string $key): mixed + { + return $this->readSecrets()[$key] ?? null; + } + /** * Sets the Blueprint object * - * @param array|null $blueprint * @return $this */ - protected function setBlueprint(array $blueprint = null) + protected function setBlueprint(array|null $blueprint = null): static { if ($blueprint !== null) { $blueprint['model'] = $this; @@ -726,88 +635,12 @@ class User extends ModelWithContent return $this; } - /** - * Sets the user email - * - * @param string $email|null - * @return $this - */ - protected function setEmail(string $email = null) - { - if ($email !== null) { - $this->email = Str::lower(trim($email)); - } - return $this; - } - - /** - * Sets the user id - * - * @param string $id|null - * @return $this - */ - protected function setId(string $id = null) - { - $this->id = $id; - return $this; - } - - /** - * Sets the user language - * - * @param string $language|null - * @return $this - */ - protected function setLanguage(string $language = null) - { - $this->language = $language !== null ? trim($language) : null; - return $this; - } - - /** - * Sets the user name - * - * @param string $name|null - * @return $this - */ - protected function setName(string $name = null) - { - $this->name = $name !== null ? trim(strip_tags($name)) : null; - return $this; - } - - /** - * Sets the user's password hash - * - * @return $this - */ - protected function setPassword( - #[SensitiveParameter] - string $password = null - ): static { - $this->password = $password; - return $this; - } - - /** - * Sets the user role - * - * @param string $role|null - * @return $this - */ - protected function setRole(string $role = null) - { - $this->role = $role !== null ? Str::lower(trim($role)) : null; - return $this; - } - /** * Converts session options into a session object * * @param \Kirby\Session\Session|array $session Session options or session object to unset the user in - * @return \Kirby\Session\Session */ - protected function sessionFromOptions($session) + protected function sessionFromOptions(Session|array|null $session): Session { // use passed session options or session object if set if (is_array($session) === true) { @@ -821,44 +654,40 @@ class User extends ModelWithContent /** * Returns the parent Users collection - * - * @return \Kirby\Cms\Users */ - protected function siblingsCollection() + protected function siblingsCollection(): Users { - return $this->kirby()->users(); + return $this->kirby()->users()->sortBy('username', 'asc'); } /** * Converts the most important user properties * to an array - * - * @return array */ public function toArray(): array { - return [ - 'avatar' => $this->avatar() ? $this->avatar()->toArray() : null, - 'content' => $this->content()->toArray(), + return array_merge(parent::toArray(), [ + 'avatar' => $this->avatar()?->toArray(), 'email' => $this->email(), 'id' => $this->id(), 'language' => $this->language(), 'role' => $this->role()->name(), 'username' => $this->username() - ]; + ]); } /** * String template builder * - * @param string|null $template - * @param array|null $data * @param string|null $fallback Fallback for tokens in the template that cannot be replaced * (`null` to keep the original token) - * @return string */ - public function toString(string $template = null, array $data = [], string|null $fallback = '', string $handler = 'template'): string - { + public function toString( + string|null $template = null, + array $data = [], + string|null $fallback = '', + string $handler = 'template' + ): string { $template ??= $this->email(); return parent::toString($template, $data, $fallback, $handler); } @@ -867,8 +696,6 @@ class User extends ModelWithContent * Returns the username * which is the given name or the email * as a fallback - * - * @return string|null */ public function username(): string|null { @@ -884,7 +711,7 @@ class User extends ModelWithContent */ public function validatePassword( #[SensitiveParameter] - string $password = null + string|null $password = null ): bool { if (empty($this->password()) === true) { throw new NotFoundException(['key' => 'user.password.undefined']); @@ -909,9 +736,20 @@ class User extends ModelWithContent } /** - * Returns the path to the password file + * @deprecated 4.0.0 Use `->secretsFile()` instead + * @codeCoverageIgnore */ protected function passwordFile(): string + { + return $this->secretsFile(); + } + + /** + * Returns the path to the file containing + * all user secrets, including the password + * @since 4.0.0 + */ + protected function secretsFile(): string { return $this->root() . '/.htpasswd'; } diff --git a/kirby/src/Cms/UserActions.php b/kirby/src/Cms/UserActions.php index 2c89043..57bf09a 100644 --- a/kirby/src/Cms/UserActions.php +++ b/kirby/src/Cms/UserActions.php @@ -4,12 +4,14 @@ namespace Kirby\Cms; use Closure; use Kirby\Data\Data; +use Kirby\Data\Json; use Kirby\Exception\LogicException; use Kirby\Exception\PermissionException; use Kirby\Filesystem\Dir; use Kirby\Filesystem\F; use Kirby\Form\Form; use Kirby\Http\Idn; +use Kirby\Toolkit\A; use Kirby\Toolkit\Str; use SensitiveParameter; use Throwable; @@ -27,11 +29,8 @@ trait UserActions { /** * Changes the user email address - * - * @param string $email - * @return static */ - public function changeEmail(string $email) + public function changeEmail(string $email): static { $email = trim($email); @@ -53,11 +52,8 @@ trait UserActions /** * Changes the user language - * - * @param string $language - * @return static */ - public function changeLanguage(string $language) + public function changeLanguage(string $language): static { return $this->commit('changeLanguage', ['user' => $this, 'language' => $language], function ($user, $language) { $user = $user->clone([ @@ -77,11 +73,8 @@ trait UserActions /** * Changes the screen name of the user - * - * @param string $name - * @return static */ - public function changeName(string $name) + public function changeName(string $name): static { $name = trim($name); @@ -103,6 +96,9 @@ trait UserActions /** * Changes the user password + * + * If this method is used with user input, it is recommended to also + * confirm the current password by the user via `::validatePassword()` */ public function changePassword( #[SensitiveParameter] @@ -131,11 +127,8 @@ trait UserActions /** * Changes the user role - * - * @param string $role - * @return static */ - public function changeRole(string $role) + public function changeRole(string $role): static { return $this->commit('changeRole', ['user' => $this, 'role' => $role], function ($user, $role) { $user = $user->clone([ @@ -153,6 +146,28 @@ trait UserActions }); } + /** + * Changes the user's TOTP secret + * @since 4.0.0 + */ + public function changeTotp( + #[SensitiveParameter] + string|null $secret + ): static { + return $this->commit('changeTotp', ['user' => $this, 'secret' => $secret], function ($user, $secret) { + $this->writeSecret('totp', $secret); + + // keep the user logged in to the current browser + // if they changed their own TOTP secret + // (regenerate the session token, update the login timestamp) + if ($user->isLoggedIn() === true) { + $user->loginPasswordless(); + } + + return $user; + }); + } + /** * Commits a user action, by following these steps * @@ -162,14 +177,13 @@ trait UserActions * 4. sends the after hook * 5. returns the result * - * @param string $action - * @param array $arguments - * @param \Closure $callback - * @return mixed * @throws \Kirby\Exception\PermissionException */ - protected function commit(string $action, array $arguments, Closure $callback) - { + protected function commit( + string $action, + array $arguments, + Closure $callback + ): mixed { if ($this->isKirby() === true) { throw new PermissionException('The Kirby user cannot be changed'); } @@ -183,13 +197,12 @@ trait UserActions $result = $callback(...$argumentValues); - if ($action === 'create') { - $argumentsAfter = ['user' => $result]; - } elseif ($action === 'delete') { - $argumentsAfter = ['status' => $result, 'user' => $old]; - } else { - $argumentsAfter = ['newUser' => $result, 'oldUser' => $old]; - } + $argumentsAfter = match ($action) { + 'create' => ['user' => $result], + 'delete' => ['status' => $result, 'user' => $old], + default => ['newUser' => $result, 'oldUser' => $old] + }; + $kirby->trigger('user.' . $action . ':after', $argumentsAfter); $kirby->cache('pages')->flush(); @@ -198,11 +211,8 @@ trait UserActions /** * Creates a new User from the given props and returns a new User object - * - * @param array|null $props - * @return static */ - public static function create(array $props = null) + public static function create(array|null $props = null): User { $data = $props; @@ -254,8 +264,6 @@ trait UserActions /** * Returns a random user id - * - * @return string */ public function createId(): string { @@ -280,7 +288,6 @@ trait UserActions /** * Deletes the user * - * @return bool * @throws \Kirby\Exception\LogicException */ public function delete(): bool @@ -307,8 +314,6 @@ trait UserActions /** * Read the account information from disk - * - * @return array */ protected function readCredentials(): array { @@ -325,24 +330,47 @@ trait UserActions /** * Reads the user password from disk - * - * @return string|false */ - protected function readPassword() + protected function readPassword(): string|false { - return F::read($this->passwordFile()); + return $this->secret('password') ?? false; + } + + /** + * Reads the secrets from the user secrets file on disk + * @since 4.0.0 + */ + protected function readSecrets(): array + { + $file = $this->secretsFile(); + $secrets = []; + + if (is_file($file) === true) { + $lines = explode("\n", file_get_contents($file)); + + if (isset($lines[1]) === true) { + $secrets = Json::decode($lines[1]); + } + + $secrets['password'] = $lines[0]; + } + + // an empty password hash means that no password was set + if (($secrets['password'] ?? null) === '') { + unset($secrets['password']); + } + + return $secrets; } /** * Updates the user data - * - * @param array|null $input - * @param string|null $languageCode - * @param bool $validate - * @return static */ - public function update(array $input = null, string $languageCode = null, bool $validate = false) - { + public function update( + array|null $input = null, + string|null $languageCode = null, + bool $validate = false + ): static { $user = parent::update($input, $languageCode, $validate); // set auth user data only if the current user is this user @@ -359,9 +387,6 @@ trait UserActions /** * This always merges the existing credentials * with the given input. - * - * @param array $credentials - * @return bool */ protected function updateCredentials(array $credentials): bool { @@ -375,9 +400,6 @@ trait UserActions /** * Writes the account information to disk - * - * @param array $credentials - * @return bool */ protected function writeCredentials(array $credentials): bool { @@ -389,8 +411,41 @@ trait UserActions */ protected function writePassword( #[SensitiveParameter] - string $password = null + string|null $password = null ): bool { - return F::write($this->passwordFile(), $password); + return $this->writeSecret('password', $password); + } + + /** + * Writes a specific secret to the user secrets file on disk; + * `password` is the first line, the rest is stored as JSON + * @since 4.0.0 + */ + protected function writeSecret( + string $key, + #[SensitiveParameter] + mixed $secret + ): bool { + $secrets = $this->readSecrets(); + + if ($secret === null) { + unset($secrets[$key]); + } else { + $secrets[$key] = $secret; + } + + // first line is always the password + $lines = $secrets['password'] ?? ''; + + // everything else is for the second line + $secondLine = Json::encode( + A::without($secrets, 'password') + ); + + if ($secondLine !== '[]') { + $lines .= "\n" . $secondLine; + } + + return F::write($this->secretsFile(), $lines); } } diff --git a/kirby/src/Cms/UserBlueprint.php b/kirby/src/Cms/UserBlueprint.php index 0f86fe1..d44d852 100644 --- a/kirby/src/Cms/UserBlueprint.php +++ b/kirby/src/Cms/UserBlueprint.php @@ -17,7 +17,6 @@ class UserBlueprint extends Blueprint /** * UserBlueprint constructor. * - * @param array $props * @throws \Kirby\Exception\InvalidArgumentException */ public function __construct(array $props) diff --git a/kirby/src/Cms/UserPermissions.php b/kirby/src/Cms/UserPermissions.php index bc91161..cab3776 100644 --- a/kirby/src/Cms/UserPermissions.php +++ b/kirby/src/Cms/UserPermissions.php @@ -13,35 +13,35 @@ namespace Kirby\Cms; */ class UserPermissions extends ModelPermissions { - /** - * @var string - */ - protected $category = 'users'; + protected string $category = 'users'; - /** - * UserPermissions constructor - * - * @param \Kirby\Cms\Model $model - */ - public function __construct(Model $model) + public function __construct(User $model) { parent::__construct($model); - // change the scope of the permissions, when the current user is this user - $this->category = $this->user && $this->user->is($model) ? 'user' : 'users'; + // change the scope of the permissions, + // when the current user is this user + $this->category = $this->user?->is($model) ? 'user' : 'users'; } - /** - * @return bool - */ protected function canChangeRole(): bool { - return $this->model->roles()->count() > 1; + // protect admin from role changes by non-admin + if ( + $this->model->isAdmin() === true && + $this->user->isAdmin() !== true + ) { + return false; + } + + // prevent demoting the last admin + if ($this->model->isLastAdmin() === true) { + return false; + } + + return true; } - /** - * @return bool - */ protected function canCreate(): bool { // the admin can always create new users @@ -57,9 +57,6 @@ class UserPermissions extends ModelPermissions return true; } - /** - * @return bool - */ protected function canDelete(): bool { return $this->model->isLastAdmin() !== true; diff --git a/kirby/src/Cms/UserPicker.php b/kirby/src/Cms/UserPicker.php index 0861bc8..0e9ab3e 100644 --- a/kirby/src/Cms/UserPicker.php +++ b/kirby/src/Cms/UserPicker.php @@ -19,8 +19,6 @@ class UserPicker extends Picker { /** * Extends the basic defaults - * - * @return array */ public function defaults(): array { @@ -33,21 +31,21 @@ class UserPicker extends Picker /** * Search all users for the picker * - * @return \Kirby\Cms\Users|null * @throws \Kirby\Exception\InvalidArgumentException */ - public function items() + public function items(): Users|null { $model = $this->options['model']; // find the right default query - if (empty($this->options['query']) === false) { - $query = $this->options['query']; - } elseif ($model instanceof User) { - $query = 'user.siblings'; - } else { - $query = 'kirby.users'; - } + $query = match (true) { + empty($this->options['query']) === false + => $this->options['query'], + $model instanceof User + => 'user.siblings', + default + => 'kirby.users' + }; // fetch all users for the picker $users = $model->query($query); diff --git a/kirby/src/Cms/UserRules.php b/kirby/src/Cms/UserRules.php index 8c6402a..df5918e 100644 --- a/kirby/src/Cms/UserRules.php +++ b/kirby/src/Cms/UserRules.php @@ -7,6 +7,7 @@ use Kirby\Exception\InvalidArgumentException; use Kirby\Exception\LogicException; use Kirby\Exception\PermissionException; use Kirby\Toolkit\Str; +use Kirby\Toolkit\Totp; use Kirby\Toolkit\V; use SensitiveParameter; @@ -24,9 +25,6 @@ class UserRules /** * Validates if the email address can be changed * - * @param \Kirby\Cms\User $user - * @param string $email - * @return bool * @throws \Kirby\Exception\PermissionException If the user is not allowed to change the address */ public static function changeEmail(User $user, string $email): bool @@ -44,9 +42,6 @@ class UserRules /** * Validates if the language can be changed * - * @param \Kirby\Cms\User $user - * @param string $language - * @return bool * @throws \Kirby\Exception\PermissionException If the user is not allowed to change the language */ public static function changeLanguage(User $user, string $language): bool @@ -64,9 +59,6 @@ class UserRules /** * Validates if the name can be changed * - * @param \Kirby\Cms\User $user - * @param string $name - * @return bool * @throws \Kirby\Exception\PermissionException If the user is not allowed to change the name */ public static function changeName(User $user, string $name): bool @@ -104,25 +96,11 @@ class UserRules /** * Validates if the role can be changed * - * @param \Kirby\Cms\User $user - * @param string $role - * @return bool * @throws \Kirby\Exception\LogicException If the user is the last admin * @throws \Kirby\Exception\PermissionException If the user is not allowed to change the role */ public static function changeRole(User $user, string $role): bool { - // protect admin from role changes by non-admin - if ( - $user->kirby()->user()->isAdmin() === false && - $user->isAdmin() === true - ) { - throw new PermissionException([ - 'key' => 'user.changeRole.permission', - 'data' => ['name' => $user->username()] - ]); - } - // prevent non-admins making a user to admin if ( $user->kirby()->user()->isAdmin() === false && @@ -133,8 +111,7 @@ class UserRules ]); } - static::validRole($user, $role); - + // prevent demoting the last admin if ($role !== 'admin' && $user->isLastAdmin() === true) { throw new LogicException([ 'key' => 'user.changeRole.lastAdmin', @@ -142,6 +119,7 @@ class UserRules ]); } + // check permissions if ($user->permissions()->changeRole() !== true) { throw new PermissionException([ 'key' => 'user.changeRole.permission', @@ -149,15 +127,48 @@ class UserRules ]); } + // prevent changing to role that is not available for user + if ($user->roles()->find($role) instanceof Role === false) { + throw new InvalidArgumentException([ + 'key' => 'user.role.invalid', + ]); + } + + return true; + } + + /** + * Validates if the TOTP can be changed + * @since 4.0.0 + * + * @throws \Kirby\Exception\PermissionException If the user is not allowed to change the password + */ + public static function changeTotp( + User $user, + #[SensitiveParameter] + string|null $secret + ): bool { + $currentUser = $user->kirby()->user(); + + if ( + $currentUser->is($user) === false && + $currentUser->isAdmin() === false + ) { + throw new PermissionException('You cannot change the time-based code for ' . $user->email()); + } + + // safety check to avoid accidental insecure secrets; + // throws an exception for secrets of the wrong length + if ($secret !== null) { + new Totp($secret); + } + return true; } /** * Validates if the user can be created * - * @param \Kirby\Cms\User $user - * @param array $props - * @return bool * @throws \Kirby\Exception\PermissionException If the user is not allowed to create a new user */ public static function create(User $user, array $props = []): bool @@ -184,22 +195,27 @@ class UserRules return true; } - // only admins are allowed to add admins - $role = $props['role'] ?? null; + // allow to create the first user + if ($user->kirby()->users()->count() === 0) { + return true; + } - if ($role === 'admin' && $currentUser?->isAdmin() === false) { + // check user permissions + if ($user->permissions()->create() !== true) { throw new PermissionException([ 'key' => 'user.create.permission' ]); } - // check user permissions (if not on install) + $role = $props['role'] ?? null; + + // prevent creating a role that is not available for user if ( - $user->kirby()->users()->count() > 0 && - $user->permissions()->create() !== true + in_array($role, [null, 'default', 'nobody'], true) === false && + $user->kirby()->roles()->canBeCreated()->find($role) instanceof Role === false ) { - throw new PermissionException([ - 'key' => 'user.create.permission' + throw new InvalidArgumentException([ + 'key' => 'user.role.invalid', ]); } @@ -209,8 +225,6 @@ class UserRules /** * Validates if the user can be deleted * - * @param \Kirby\Cms\User $user - * @return bool * @throws \Kirby\Exception\LogicException If this is the last user or last admin, which cannot be deleted * @throws \Kirby\Exception\PermissionException If the user is not allowed to delete this user */ @@ -239,14 +253,13 @@ class UserRules /** * Validates if the user can be updated * - * @param \Kirby\Cms\User $user - * @param array $values - * @param array $strings - * @return bool * @throws \Kirby\Exception\PermissionException If the user it not allowed to update this user */ - public static function update(User $user, array $values = [], array $strings = []): bool - { + public static function update( + User $user, + array $values = [], + array $strings = [] + ): bool { if ($user->permissions()->update() !== true) { throw new PermissionException([ 'key' => 'user.update.permission', @@ -260,15 +273,14 @@ class UserRules /** * Validates an email address * - * @param \Kirby\Cms\User $user - * @param string $email - * @param bool $strict - * @return bool * @throws \Kirby\Exception\DuplicateException If the email address already exists * @throws \Kirby\Exception\InvalidArgumentException If the email address is invalid */ - public static function validEmail(User $user, string $email, bool $strict = false): bool - { + public static function validEmail( + User $user, + string $email, + bool $strict = false + ): bool { if (V::email($email ?? null) === false) { throw new InvalidArgumentException([ 'key' => 'user.email.invalid', @@ -294,9 +306,6 @@ class UserRules /** * Validates a user id * - * @param \Kirby\Cms\User $user - * @param string $id - * @return bool * @throws \Kirby\Exception\DuplicateException If the user already exists */ public static function validId(User $user, string $id): bool @@ -315,9 +324,6 @@ class UserRules /** * Validates a user language code * - * @param \Kirby\Cms\User $user - * @param string $language - * @return bool * @throws \Kirby\Exception\InvalidArgumentException If the language does not exist */ public static function validLanguage(User $user, string $language): bool @@ -364,10 +370,8 @@ class UserRules /** * Validates a user role * - * @param \Kirby\Cms\User $user - * @param string $role - * @return bool * @throws \Kirby\Exception\InvalidArgumentException If the user role does not exist + * @deprecated 4.5.0 */ public static function validRole(User $user, string $role): bool { diff --git a/kirby/src/Cms/Users.php b/kirby/src/Cms/Users.php index b9e51fb..4014402 100644 --- a/kirby/src/Cms/Users.php +++ b/kirby/src/Cms/Users.php @@ -26,12 +26,10 @@ class Users extends Collection /** * All registered users methods - * - * @var array */ - public static $methods = []; + public static array $methods = []; - public function create(array $data) + public function create(array $data): User { return User::create($data); } @@ -45,25 +43,25 @@ class Users extends Collection * @return $this * @throws \Kirby\Exception\InvalidArgumentException When no `User` or `Users` object or an ID of an existing user is passed */ - public function add($object) + public function add($object): static { // add a users collection if ($object instanceof self) { $this->data = array_merge($this->data, $object->data); - // add a user by id + // add a user by id } elseif ( is_string($object) === true && $user = App::instance()->user($object) ) { $this->__set($user->id(), $user); - // add a user object + // add a user object } elseif ($object instanceof User) { $this->__set($object->id(), $object); - // give a useful error message on invalid input; - // silently ignore "empty" values for compatibility with existing setups + // give a useful error message on invalid input; + // silently ignore "empty" values for compatibility with existing setups } elseif (in_array($object, [null, false, true], true) !== true) { throw new InvalidArgumentException('You must pass a Users or User object or an ID of an existing user to the Users collection'); } @@ -72,13 +70,10 @@ class Users extends Collection } /** - * Takes an array of user props and creates a nice and clean user collection from it - * - * @param array $users - * @param array $inject - * @return static + * Takes an array of user props and creates a nice + * and clean user collection from it */ - public static function factory(array $users, array $inject = []) + public static function factory(array $users, array $inject = []): static { $collection = new static(); @@ -93,10 +88,8 @@ class Users extends Collection /** * Returns all files of all users - * - * @return \Kirby\Cms\Files */ - public function files() + public function files(): Files { $files = new Files([], $this->parent); @@ -112,11 +105,8 @@ class Users extends Collection /** * Finds a user in the collection by ID or email address * @internal Use `$users->find()` instead - * - * @param string $key - * @return \Kirby\Cms\User|null */ - public function findByKey(string $key) + public function findByKey(string $key): User|null { if ($user = $this->findByUuid($key, 'user')) { return $user; @@ -131,12 +121,8 @@ class Users extends Collection /** * Loads a user from disk by passing the absolute path (root) - * - * @param string $root - * @param array $inject - * @return static */ - public static function load(string $root, array $inject = []) + public static function load(string $root, array $inject = []): static { $users = new static(); @@ -165,11 +151,8 @@ class Users extends Collection /** * Shortcut for `$users->filter('role', 'admin')` - * - * @param string $role - * @return static */ - public function role(string $role) + public function role(string $role): static { return $this->filter('role', $role); } diff --git a/kirby/src/Cms/Visitor.php b/kirby/src/Cms/Visitor.php index eae9f3a..511eb8e 100644 --- a/kirby/src/Cms/Visitor.php +++ b/kirby/src/Cms/Visitor.php @@ -2,6 +2,7 @@ namespace Kirby\Cms; +use Kirby\Http\Visitor as BaseVisitor; use Kirby\Toolkit\Facade; /** @@ -15,10 +16,7 @@ use Kirby\Toolkit\Facade; */ class Visitor extends Facade { - /** - * @return \Kirby\Http\Visitor - */ - public static function instance() + public static function instance(): BaseVisitor { return App::instance()->visitor(); } diff --git a/kirby/src/Cms/Content.php b/kirby/src/Content/Content.php similarity index 72% rename from kirby/src/Cms/Content.php rename to kirby/src/Content/Content.php index cfe6c0c..0169287 100644 --- a/kirby/src/Cms/Content.php +++ b/kirby/src/Content/Content.php @@ -1,14 +1,16 @@ * @link https://getkirby.com * @copyright Bastian Allgeier @@ -18,39 +20,29 @@ class Content { /** * The raw data array - * - * @var array */ - protected $data = []; + protected array $data = []; /** * Cached field objects * Once a field is being fetched * it is added to this array for * later reuse - * - * @var array */ - protected $fields = []; + protected array $fields = []; /** * A potential parent object. * Not necessarily needed. Especially * for testing, but field methods might * need it. - * - * @var Model */ - protected $parent; + protected ModelWithContent|null $parent; /** * Magic getter for content fields - * - * @param string $name - * @param array $arguments - * @return \Kirby\Cms\Field */ - public function __call(string $name, array $arguments = []) + public function __call(string $name, array $arguments = []): Field { return $this->get($name); } @@ -58,12 +50,13 @@ class Content /** * Creates a new Content object * - * @param array|null $data - * @param object|null $parent * @param bool $normalize Set to `false` if the input field keys are already lowercase */ - public function __construct(array $data = [], $parent = null, bool $normalize = true) - { + public function __construct( + array $data = [], + ModelWithContent|null $parent = null, + bool $normalize = true + ) { if ($normalize === true) { $data = array_change_key_case($data, CASE_LOWER); } @@ -75,9 +68,9 @@ class Content /** * Same as `self::data()` to improve * `var_dump` output + * @codeCoverageIgnore * * @see self::data() - * @return array */ public function __debugInfo(): array { @@ -86,9 +79,6 @@ class Content /** * Converts the content to a new blueprint - * - * @param string $to - * @return array */ public function convertTo(string $to): array { @@ -99,11 +89,21 @@ class Content // blueprints $old = $this->parent->blueprint(); $subfolder = dirname($old->name()); - $new = Blueprint::factory($subfolder . '/' . $to, $subfolder . '/default', $this->parent); + $new = Blueprint::factory( + $subfolder . '/' . $to, + $subfolder . '/default', + $this->parent + ); // forms - $oldForm = new Form(['fields' => $old->fields(), 'model' => $this->parent]); - $newForm = new Form(['fields' => $new->fields(), 'model' => $this->parent]); + $oldForm = new Form([ + 'fields' => $old->fields(), + 'model' => $this->parent + ]); + $newForm = new Form([ + 'fields' => $new->fields(), + 'model' => $this->parent + ]); // fields $oldFields = $oldForm->fields(); @@ -128,8 +128,6 @@ class Content /** * Returns the raw data array - * - * @return array */ public function data(): array { @@ -138,8 +136,6 @@ class Content /** * Returns all registered field objects - * - * @return array */ public function fields(): array { @@ -152,11 +148,8 @@ class Content /** * Returns either a single field object * or all registered fields - * - * @param string|null $key - * @return \Kirby\Cms\Field|array */ - public function get(string $key = null) + public function get(string|null $key = null): Field|array { if ($key === null) { return $this->fields(); @@ -173,9 +166,6 @@ class Content /** * Checks if a content field is set - * - * @param string $key - * @return bool */ public function has(string $key): bool { @@ -184,8 +174,6 @@ class Content /** * Returns all field keys - * - * @return array */ public function keys(): array { @@ -196,14 +184,11 @@ class Content * Returns a clone of the content object * without the fields, specified by the * passed key(s) - * - * @param string ...$keys - * @return static */ - public function not(...$keys) + public function not(string ...$keys): static { $copy = clone $this; - $copy->fields = null; + $copy->fields = []; foreach ($keys as $key) { unset($copy->data[strtolower($key)]); @@ -215,10 +200,8 @@ class Content /** * Returns the parent * Site, Page, File or User object - * - * @return \Kirby\Cms\Model */ - public function parent() + public function parent(): ModelWithContent|null { return $this->parent; } @@ -226,10 +209,9 @@ class Content /** * Set the parent model * - * @param \Kirby\Cms\Model $parent * @return $this */ - public function setParent(Model $parent) + public function setParent(ModelWithContent $parent): static { $this->parent = $parent; return $this; @@ -239,7 +221,6 @@ class Content * Returns the raw data array * * @see self::data() - * @return array */ public function toArray(): array { @@ -250,12 +231,12 @@ class Content * Updates the content and returns * a cloned object * - * @param array|null $content - * @param bool $overwrite * @return $this */ - public function update(array $content = null, bool $overwrite = false) - { + public function update( + array|null $content = null, + bool $overwrite = false + ): static { $content = array_change_key_case((array)$content, CASE_LOWER); $this->data = $overwrite === true ? $content : array_merge($this->data, $content); diff --git a/kirby/src/Content/ContentStorage.php b/kirby/src/Content/ContentStorage.php new file mode 100644 index 0000000..23eab40 --- /dev/null +++ b/kirby/src/Content/ContentStorage.php @@ -0,0 +1,314 @@ + + * @author Nico Hoffmann + * @link https://getkirby.com + * @copyright Bastian Allgeier + * @license https://getkirby.com/license + */ +class ContentStorage +{ + protected ContentStorageHandler $handler; + + public function __construct( + protected ModelWithContent $model, + string $handler = PlainTextContentStorageHandler::class + ) { + $this->handler = new $handler($model); + } + + /** + * Magic caller for handler methods + */ + public function __call(string $name, array $args): mixed + { + return $this->handler->$name(...$args); + } + + /** + * Returns generator for all existing versions-languages combinations + * + * @return Generator + * @todo 4.0.0 consider more descpritive name + */ + public function all(): Generator + { + foreach ($this->model->kirby()->languages()->codes() as $lang) { + foreach ($this->dynamicVersions() as $version) { + if ($this->exists($version, $lang) === true) { + yield $version => $lang; + } + } + } + } + + /** + * Returns the absolute path to the content file + * @internal eventually should only exists in PlainTextContentStorage, + * when not relying anymore on language helper + * + * @param string $lang Code `'default'` in a single-lang installation + * + * @throws \Kirby\Exception\LogicException If the model type doesn't have a known content filename + */ + public function contentFile( + string $version, + string $lang, + bool $force = false + ): string { + $lang = $this->language($lang, $force); + return $this->handler->contentFile($version, $lang); + } + + /** + * Adapts all versions when converting languages + * @internal + */ + public function convertLanguage(string $from, string $to): void + { + $from = $this->language($from, true); + $to = $this->language($to, true); + + foreach ($this->dynamicVersions() as $version) { + $this->handler->move($version, $from, $version, $to); + } + } + + /** + * Creates a new version + * + * @param string|null $lang Code `'default'` in a single-lang installation + * @param array $fields Content fields + */ + public function create( + string $versionType, + string|null $lang, + array $fields + ): void { + $lang = $this->language($lang); + $this->handler->create($versionType, $lang, $fields); + } + + /** + * Returns the default version identifier for the model + * @internal + */ + public function defaultVersion(): string + { + if ( + $this->model instanceof Page === true && + $this->model->isDraft() === true + ) { + return 'changes'; + } + + return 'published'; + } + + /** + * Deletes an existing version in an idempotent way if it was already deleted + * + * @param string $lang Code `'default'` in a single-lang installation + */ + public function delete( + string $version, + string|null $lang = null, + bool $force = false + ): void { + $lang = $this->language($lang, $force); + $this->handler->delete($version, $lang); + } + + /** + * Deletes all versions when deleting a language + * @internal + */ + public function deleteLanguage(string|null $lang): void + { + $lang = $this->language($lang, true); + + foreach ($this->dynamicVersions() as $version) { + $this->handler->delete($version, $lang); + } + } + + /** + * Returns all versions availalbe for the model that can be updated + * @internal + */ + public function dynamicVersions(): array + { + $versions = ['changes']; + + if ( + $this->model instanceof Page === false || + $this->model->isDraft() === false + ) { + $versions[] = 'published'; + } + + return $versions; + } + + /** + * Checks if a version exists + * + * @param string|null $lang Code `'default'` in a single-lang installation; + * checks for "any language" if not provided + */ + public function exists( + string $version, + string|null $lang + ): bool { + if ($lang !== null) { + $lang = $this->language($lang); + } + + return $this->handler->exists($version, $lang); + } + + /** + * Returns the modification timestamp of a version + * if it exists + * + * @param string $lang Code `'default'` in a single-lang installation + */ + public function modified( + string $version, + string|null $lang = null + ): int|null { + $lang = $this->language($lang); + return $this->handler->modified($version, $lang); + } + + /** + * Returns the stored content fields + * + * @param string $lang Code `'default'` in a single-lang installation + * @return array + * + * @throws \Kirby\Exception\NotFoundException If the version does not exist + */ + public function read( + string $version, + string|null $lang = null + ): array { + $lang = $this->language($lang); + $this->ensureExistingVersion($version, $lang); + return $this->handler->read($version, $lang); + } + + /** + * Updates the modification timestamp of an existing version + * + * @param string $lang Code `'default'` in a single-lang installation + * + * @throws \Kirby\Exception\NotFoundException If the version does not exist + */ + public function touch( + string $version, + string|null $lang = null + ): void { + $lang = $this->language($lang); + $this->ensureExistingVersion($version, $lang); + $this->handler->touch($version, $lang); + } + + /** + * Touches all versions of a language + * @internal + */ + public function touchLanguage(string|null $lang): void + { + $lang = $this->language($lang, true); + + foreach ($this->dynamicVersions() as $version) { + if ($this->exists($version, $lang) === true) { + $this->handler->touch($version, $lang); + } + } + } + + /** + * Updates the content fields of an existing version + * + * @param string $lang Code `'default'` in a single-lang installation + * @param array $fields Content fields + * + * @throws \Kirby\Exception\NotFoundException If the version does not exist + */ + public function update( + string $version, + string|null $lang = null, + array $fields = [] + ): void { + $lang = $this->language($lang); + $this->ensureExistingVersion($version, $lang); + $this->handler->update($version, $lang, $fields); + } + + /** + * @throws \Kirby\Exception\NotFoundException If the version does not exist + */ + protected function ensureExistingVersion( + string $version, + string $lang + ): void { + if ($this->exists($version, $lang) !== true) { + throw new NotFoundException('Version "' . $version . ' (' . $lang . ')" does not already exist'); + } + } + + /** + * Converts a "user-facing" language code to a "raw" language code to be + * used for storage + * + * @param bool $force If set to `true`, the language code is not validated + * @return string Language code + */ + protected function language( + string|null $languageCode = null, + bool $force = false + ): string { + // in force mode, use the provided language code even in single-lang for + // compatibility with the previous behavior in `$model->contentFile()` + if ($force === true) { + return $languageCode ?? 'default'; + } + + // in multi-lang, … + if ($this->model->kirby()->multilang() === true) { + // look up the actual language object if possible + $language = $this->model->kirby()->language($languageCode); + + // validate the language code + if ($language === null) { + throw new InvalidArgumentException('Invalid language: ' . $languageCode); + } + + return $language->code(); + } + + // otherwise use hardcoded "default" code for single lang + return 'default'; + } +} diff --git a/kirby/src/Content/ContentStorageHandler.php b/kirby/src/Content/ContentStorageHandler.php new file mode 100644 index 0000000..6f39d11 --- /dev/null +++ b/kirby/src/Content/ContentStorageHandler.php @@ -0,0 +1,96 @@ + + * @link https://getkirby.com + * @copyright Bastian Allgeier + * @license https://getkirby.com/license + */ +interface ContentStorageHandler +{ + public function __construct(ModelWithContent $model); + + /** + * Creates a new version + * + * @param string $lang Code `'default'` in a single-lang installation + * @param array $fields Content fields + */ + public function create(string $versionType, string $lang, array $fields): void; + + /** + * Deletes an existing version in an idempotent way if it was already deleted + * + * @param string $lang Code `'default'` in a single-lang installation + */ + public function delete(string $version, string $lang): void; + + /** + * Checks if a version exists + * + * @param string|null $lang Code `'default'` in a single-lang installation; + * checks for "any language" if not provided + */ + public function exists(string $version, string|null $lang): bool; + + /** + * Returns the modification timestamp of a version if it exists + * + * @param string $lang Code `'default'` in a single-lang installation + */ + public function modified(string $version, string $lang): int|null; + + /** + * Moves content from one version-language combination to another + * + * @param string $fromLang Code `'default'` in a single-lang installation + * @param string $toLang Code `'default'` in a single-lang installation + */ + public function move( + string $fromVersion, + string $fromLang, + string $toVersion, + string $toLang + ): void; + + /** + * Returns the stored content fields + * + * @param string $lang Code `'default'` in a single-lang installation + * @return array + * + * @throws \Kirby\Exception\NotFoundException If the version does not exist + */ + public function read(string $version, string $lang): array; + + /** + * Updates the modification timestamp of an existing version + * + * @param string $lang Code `'default'` in a single-lang installation + * + * @throws \Kirby\Exception\NotFoundException If the version does not exist + */ + public function touch(string $version, string $lang): void; + + /** + * Updates the content fields of an existing version + * + * @param string $lang Code `'default'` in a single-lang installation + * @param array $fields Content fields + * + * @throws \Kirby\Exception\NotFoundException If the version does not exist + */ + public function update(string $version, string $lang, array $fields): void; +} diff --git a/kirby/src/Cms/ContentTranslation.php b/kirby/src/Content/ContentTranslation.php similarity index 54% rename from kirby/src/Cms/ContentTranslation.php rename to kirby/src/Content/ContentTranslation.php index 8f1e5be..e341d15 100644 --- a/kirby/src/Cms/ContentTranslation.php +++ b/kirby/src/Content/ContentTranslation.php @@ -1,15 +1,15 @@ * @link https://getkirby.com * @copyright Bastian Allgeier @@ -17,48 +17,31 @@ use Kirby\Toolkit\Properties; */ class ContentTranslation { - use Properties; - - /** - * @var string - */ - protected $code; - - /** - * @var array - */ - protected $content; - - /** - * @var string - */ - protected $contentFile; - - /** - * @var Model - */ - protected $parent; - - /** - * @var string - */ - protected $slug; + protected string $code; + protected array|null $content; + protected string $contentFile; + protected ModelWithContent $parent; + protected string|null $slug; /** * Creates a new translation object - * - * @param array $props */ public function __construct(array $props) { - $this->setRequiredProperties($props, ['parent', 'code']); - $this->setOptionalProperties($props, ['slug', 'content']); + $this->code = $props['code']; + $this->parent = $props['parent']; + $this->slug = $props['slug'] ?? null; + + if ($content = $props['content'] ?? null) { + $this->content = array_change_key_case($content); + } else { + $this->content = null; + } } /** * Improve `var_dump` output - * - * @return array + * @codeCoverageIgnore */ public function __debugInfo(): array { @@ -68,8 +51,6 @@ class ContentTranslation /** * Returns the language code of the * translation - * - * @return string */ public function code(): string { @@ -79,8 +60,6 @@ class ContentTranslation /** * Returns the translation content * as plain array - * - * @return array */ public function content(): array { @@ -92,9 +71,10 @@ class ContentTranslation $this->isDefault() === false && $defaultLanguage = $parent->kirby()->defaultLanguage() ) { - if ($default = $parent->translation($defaultLanguage->code())?->content()) { - $content = array_merge($default, $content); - } + $content = array_merge( + $parent->translation($defaultLanguage->code())?->content() ?? [], + $content + ); } return $content; @@ -102,12 +82,19 @@ class ContentTranslation /** * Absolute path to the translation content file - * - * @return string */ public function contentFile(): string { - return $this->contentFile = $this->parent->contentFile($this->code, true); + // temporary compatibility change (TODO: take this from the parent `ModelVersion` object) + $identifier = $this->parent::CLASS_ALIAS === 'page' && $this->parent->isDraft() === true ? + 'changes' : + 'published'; + + return $this->contentFile = $this->parent->storage()->contentFile( + $identifier, + $this->code, + true + ); } /** @@ -122,8 +109,6 @@ class ContentTranslation /** * Returns the translation code as id - * - * @return string */ public function id(): string { @@ -133,77 +118,22 @@ class ContentTranslation /** * Checks if the this is the default translation * of the model - * - * @return bool */ public function isDefault(): bool { - if ($defaultLanguage = $this->parent->kirby()->defaultLanguage()) { - return $this->code() === $defaultLanguage->code(); - } - - return false; + return $this->code() === $this->parent->kirby()->defaultLanguage()?->code(); } /** * Returns the parent page, file or site object - * - * @return \Kirby\Cms\Model */ - public function parent() + public function parent(): ModelWithContent { return $this->parent; } - /** - * @param string $code - * @return $this - */ - protected function setCode(string $code) - { - $this->code = $code; - return $this; - } - - /** - * @param array|null $content - * @return $this - */ - protected function setContent(array $content = null) - { - if ($content !== null) { - $this->content = array_change_key_case($content); - } else { - $this->content = null; - } - - return $this; - } - - /** - * @param \Kirby\Cms\Model $parent - * @return $this - */ - protected function setParent(Model $parent) - { - $this->parent = $parent; - return $this; - } - - /** - * @param string|null $slug - * @return $this - */ - protected function setSlug(string $slug = null) - { - $this->slug = $slug; - return $this; - } - /** * Returns the custom translation slug - * - * @return string|null */ public function slug(): string|null { @@ -213,22 +143,23 @@ class ContentTranslation /** * Merge the old and new data * - * @param array|null $data - * @param bool $overwrite * @return $this */ - public function update(array $data = null, bool $overwrite = false) + public function update(array|null $data = null, bool $overwrite = false): static { $data = array_change_key_case((array)$data); - $this->content = $overwrite === true ? $data : array_merge($this->content(), $data); + + $this->content = match ($overwrite) { + true => $data, + default => array_merge($this->content(), $data) + }; + return $this; } /** * Converts the most important translation * props to an array - * - * @return array */ public function toArray(): array { diff --git a/kirby/src/Cms/Field.php b/kirby/src/Content/Field.php similarity index 67% rename from kirby/src/Cms/Field.php rename to kirby/src/Content/Field.php index 710f36c..201ccc2 100644 --- a/kirby/src/Cms/Field.php +++ b/kirby/src/Content/Field.php @@ -1,8 +1,9 @@ myField()->lower(); * ``` * - * @package Kirby Cms + * @package Kirby Content * @author Bastian Allgeier * @link https://getkirby.com * @copyright Bastian Allgeier @@ -28,49 +29,48 @@ class Field { /** * Field method aliases - * - * @var array */ - public static $aliases = []; + public static array $aliases = []; /** * The field name - * - * @var string */ - protected $key; + protected string $key; /** * Registered field methods - * - * @var array */ - public static $methods = []; + public static array $methods = []; /** * The parent object if available. * This will be the page, site, user or file * to which the content belongs - * - * @var Model */ - protected $parent; + protected ModelWithContent|null $parent; /** * The value of the field - * - * @var mixed */ - public $value; + public mixed $value; + + /** + * Creates a new field object + */ + public function __construct( + ModelWithContent|null $parent, + string $key, + mixed $value + ) { + $this->key = $key; + $this->value = $value; + $this->parent = $parent; + } /** * Magic caller for field methods - * - * @param string $method - * @param array $arguments - * @return mixed */ - public function __call(string $method, array $arguments = []) + public function __call(string $method, array $arguments = []): mixed { $method = strtolower($method); @@ -89,27 +89,13 @@ class Field return $this; } - /** - * Creates a new field object - * - * @param object|null $parent - * @param string $key - * @param mixed $value - */ - public function __construct(object|null $parent, string $key, $value) - { - $this->key = $key; - $this->value = $value; - $this->parent = $parent; - } - /** * Simplifies the var_dump result + * @codeCoverageIgnore * * @see Field::toArray - * @return array */ - public function __debugInfo() + public function __debugInfo(): array { return $this->toArray(); } @@ -119,7 +105,6 @@ class Field * or stringify the entire object * * @see Field::toString - * @return string */ public function __toString(): string { @@ -128,8 +113,6 @@ class Field /** * Checks if the field exists in the content data array - * - * @return bool */ public function exists(): bool { @@ -138,18 +121,24 @@ class Field /** * Checks if the field content is empty - * - * @return bool */ public function isEmpty(): bool { - return empty($this->value) === true && in_array($this->value, [0, '0', false], true) === false; + $value = $this->value; + + if (is_string($value) === true) { + $value = trim($value); + } + + return + $value === null || + $value === '' || + $value === [] || + $value === '[]'; } /** * Checks if the field content is not empty - * - * @return bool */ public function isNotEmpty(): bool { @@ -158,8 +147,6 @@ class Field /** * Returns the name of the field - * - * @return string */ public function key(): string { @@ -168,9 +155,8 @@ class Field /** * @see Field::parent() - * @return \Kirby\Cms\Model|null */ - public function model() + public function model(): ModelWithContent|null { return $this->parent; } @@ -178,10 +164,9 @@ class Field /** * Provides a fallback if the field value is empty * - * @param mixed $fallback * @return $this|static */ - public function or($fallback = null) + public function or(mixed $fallback = null): static { if ($this->isNotEmpty()) { return $this; @@ -198,18 +183,14 @@ class Field /** * Returns the parent object of the field - * - * @return \Kirby\Cms\Model|null */ - public function parent() + public function parent(): ModelWithContent|null { return $this->parent; } /** * Converts the Field object to an array - * - * @return array */ public function toArray(): array { @@ -218,8 +199,6 @@ class Field /** * Returns the field value as string - * - * @return string */ public function toString(): string { @@ -230,27 +209,19 @@ class Field * Returns the field content. If a new value is passed, * the modified field will be returned. Otherwise it * will return the field value. - * - * @param string|\Closure $value - * @return mixed - * @throws \Kirby\Exception\InvalidArgumentException */ - public function value($value = null) + public function value(string|Closure|null $value = null): mixed { if ($value === null) { return $this->value; } - if (is_scalar($value)) { - $value = (string)$value; - } elseif (is_callable($value)) { - $value = (string)$value->call($this, $this->value); - } else { - throw new InvalidArgumentException('Invalid field value type: ' . gettype($value)); + if ($value instanceof Closure) { + $value = $value->call($this, $this->value); } $clone = clone $this; - $clone->value = $value; + $clone->value = (string)$value; return $clone; } diff --git a/kirby/src/Content/PlainTextContentStorageHandler.php b/kirby/src/Content/PlainTextContentStorageHandler.php new file mode 100644 index 0000000..756816a --- /dev/null +++ b/kirby/src/Content/PlainTextContentStorageHandler.php @@ -0,0 +1,253 @@ + + * @link https://getkirby.com + * @copyright Bastian Allgeier + * @license https://getkirby.com/license + */ +class PlainTextContentStorageHandler implements ContentStorageHandler +{ + public function __construct(protected ModelWithContent $model) + { + } + + /** + * Creates a new version + * + * @param string $lang Code `'default'` in a single-lang installation + * @param array $fields Content fields + */ + public function create(string $versionType, string $lang, array $fields): void + { + $success = Data::write($this->contentFile($versionType, $lang), $fields); + + // @codeCoverageIgnoreStart + if ($success !== true) { + throw new Exception('Could not write new content file'); + } + // @codeCoverageIgnoreEnd + } + + /** + * Deletes an existing version in an idempotent way if it was already deleted + * + * @param string $lang Code `'default'` in a single-lang installation + */ + public function delete(string $version, string $lang): void + { + $contentFile = $this->contentFile($version, $lang); + $success = F::unlink($contentFile); + + // @codeCoverageIgnoreStart + if ($success !== true) { + throw new Exception('Could not delete content file'); + } + // @codeCoverageIgnoreEnd + + // clean up empty directories + $contentDir = dirname($contentFile); + if ( + Dir::exists($contentDir) === true && + Dir::isEmpty($contentDir) === true + ) { + $success = rmdir($contentDir); + + // @codeCoverageIgnoreStart + if ($success !== true) { + throw new Exception('Could not delete empty content directory'); + } + // @codeCoverageIgnoreEnd + } + } + + /** + * Checks if a version exists + * + * @param string|null $lang Code `'default'` in a single-lang installation; + * checks for "any language" if not provided + */ + public function exists(string $version, string|null $lang): bool + { + if ($lang === null) { + foreach ($this->contentFiles($version) as $file) { + if (is_file($file) === true) { + return true; + } + } + + return false; + } + + return is_file($this->contentFile($version, $lang)) === true; + } + + /** + * Returns the modification timestamp of a version + * if it exists + * + * @param string $lang Code `'default'` in a single-lang installation + */ + public function modified(string $version, string $lang): int|null + { + $modified = F::modified($this->contentFile($version, $lang)); + + if (is_int($modified) === true) { + return $modified; + } + + return null; + } + + /** + * Returns the stored content fields + * + * @param string $lang Code `'default'` in a single-lang installation + * @return array + * + * @throws \Kirby\Exception\NotFoundException If the version does not exist + */ + public function read(string $version, string $lang): array + { + return Data::read($this->contentFile($version, $lang)); + } + + /** + * Updates the modification timestamp of an existing version + * + * @param string $lang Code `'default'` in a single-lang installation + * + * @throws \Kirby\Exception\NotFoundException If the version does not exist + */ + public function touch(string $version, string $lang): void + { + $success = touch($this->contentFile($version, $lang)); + + // @codeCoverageIgnoreStart + if ($success !== true) { + throw new Exception('Could not touch existing content file'); + } + // @codeCoverageIgnoreEnd + } + + /** + * Updates the content fields of an existing version + * + * @param string $lang Code `'default'` in a single-lang installation + * @param array $fields Content fields + * + * @throws \Kirby\Exception\NotFoundException If the version does not exist + */ + public function update(string $version, string $lang, array $fields): void + { + $success = Data::write($this->contentFile($version, $lang), $fields); + + // @codeCoverageIgnoreStart + if ($success !== true) { + throw new Exception('Could not write existing content file'); + } + // @codeCoverageIgnoreEnd + } + + /** + * Returns the absolute path to the content file + * @internal To be made `protected` when the CMS core no longer relies on it + * + * @param string $lang Code `'default'` in a single-lang installation + * + * @throws \Kirby\Exception\LogicException If the model type doesn't have a known content filename + */ + public function contentFile(string $version, string $lang): string + { + if (in_array($version, ['published', 'changes']) !== true) { + throw new InvalidArgumentException('Invalid version identifier "' . $version . '"'); + } + + $extension = $this->model->kirby()->contentExtension(); + $directory = $this->model->root(); + + $directory = match ($this->model::CLASS_ALIAS) { + 'file' => dirname($this->model->root()), + default => $this->model->root() + }; + + $filename = match ($this->model::CLASS_ALIAS) { + 'file' => $this->model->filename(), + 'page' => $this->model->intendedTemplate()->name(), + 'site', + 'user' => $this->model::CLASS_ALIAS, + // @codeCoverageIgnoreStart + default => throw new LogicException('Cannot determine content filename for model type "' . $this->model::CLASS_ALIAS . '"') + // @codeCoverageIgnoreEnd + }; + + if ($this->model::CLASS_ALIAS === 'page' && $this->model->isDraft() === true) { + // changes versions don't need anything extra + // (drafts already have the `_drafts` prefix in their root), + // but a published version is not possible + if ($version === 'published') { + throw new LogicException('Drafts cannot have a published content file'); + } + } elseif ($version === 'changes') { + // other model type or published page that has a changes subfolder + $directory .= '/_changes'; + } + + if ($lang !== 'default') { + return $directory . '/' . $filename . '.' . $lang . '.' . $extension; + } + + return $directory . '/' . $filename . '.' . $extension; + } + + /** + * Returns an array with content files of all languages + * @internal To be made `protected` when the CMS core no longer relies on it + */ + public function contentFiles(string $version): array + { + if ($this->model->kirby()->multilang() === true) { + return $this->model->kirby()->languages()->values( + fn ($lang) => $this->contentFile($version, $lang) + ); + } + + return [ + $this->contentFile($version, 'default') + ]; + } + + /** + * Moves content from one version-language combination to another + * + * @param string $fromLang Code `'default'` in a single-lang installation + * @param string $toLang Code `'default'` in a single-lang installation + */ + public function move( + string $fromVersion, + string $fromLang, + string $toVersion, + string $toLang + ): void { + F::move( + $this->contentFile($fromVersion, $fromLang), + $this->contentFile($toVersion, $toLang) + ); + } +} diff --git a/kirby/src/Data/Data.php b/kirby/src/Data/Data.php index c4b89f9..b6a1cb6 100644 --- a/kirby/src/Data/Data.php +++ b/kirby/src/Data/Data.php @@ -38,11 +38,11 @@ class Data * All registered handlers */ public static array $handlers = [ - 'json' => 'Kirby\Data\Json', - 'php' => 'Kirby\Data\PHP', - 'txt' => 'Kirby\Data\Txt', - 'xml' => 'Kirby\Data\Xml', - 'yaml' => 'Kirby\Data\Yaml', + 'json' => Json::class, + 'php' => PHP::class, + 'txt' => Txt::class, + 'xml' => Xml::class, + 'yaml' => Yaml::class ]; /** @@ -54,10 +54,11 @@ class Data $type = strtolower($type); // find a handler or alias - $alias = static::$aliases[$type] ?? null; - $handler = - static::$handlers[$type] ?? - ($alias ? static::$handlers[$alias] ?? null : null); + $handler = static::$handlers[$type] ?? null; + + if ($alias = static::$aliases[$type] ?? null) { + $handler ??= static::$handlers[$alias] ?? null; + } if ($handler === null || class_exists($handler) === false) { throw new Exception('Missing handler for type: "' . $type . '"'); @@ -95,7 +96,9 @@ class Data */ public static function read(string $file, string|null $type = null): array { - return static::handler($type ?? F::extension($file))->read($file); + $type ??= F::extension($file); + $handler = static::handler($type); + return $handler->read($file); } /** @@ -108,6 +111,8 @@ class Data $data = [], string|null $type = null ): bool { - return static::handler($type ?? F::extension($file))->write($file, $data); + $type ??= F::extension($file); + $handler = static::handler($type); + return $handler->write($file, $data); } } diff --git a/kirby/src/Data/Txt.php b/kirby/src/Data/Txt.php index 432d255..985f5ea 100644 --- a/kirby/src/Data/Txt.php +++ b/kirby/src/Data/Txt.php @@ -45,7 +45,7 @@ class Txt extends Handler // avoid problems with arrays if (is_array($value) === true) { $value = Data::encode($value, 'yaml'); - // avoid problems with localized floats + // avoid problems with localized floats } elseif (is_float($value) === true) { $value = Str::float($value); } @@ -65,11 +65,10 @@ class Txt extends Handler $result = $key . ':'; // multi-line content - if (preg_match('!\R!', $value) === 1) { - $result .= "\n\n"; - } else { - $result .= ' '; - } + $result .= match (preg_match('!\R!', $value)) { + 1 => "\n\n", + default => ' ', + }; $result .= $value; diff --git a/kirby/src/Database/Database.php b/kirby/src/Database/Database.php index 9d480e9..9db17ae 100644 --- a/kirby/src/Database/Database.php +++ b/kirby/src/Database/Database.php @@ -3,6 +3,8 @@ namespace Kirby\Database; use Closure; +use Kirby\Database\Sql\Mysql; +use Kirby\Database\Sql\Sqlite; use Kirby\Exception\InvalidArgumentException; use Kirby\Toolkit\A; use Kirby\Toolkit\Collection; @@ -303,7 +305,23 @@ class Database // try to prepare and execute the sql try { $this->statement = $this->connection->prepare($query); - $this->statement->execute($bindings); + // bind parameters to statement + foreach ($bindings as $parameter => $value) { + // positional parameters start at 1 + if (is_int($parameter)) { + $parameter++; + } + + $type = match (gettype($value)) { + 'integer' => PDO::PARAM_INT, + 'boolean' => PDO::PARAM_BOOL, + 'NULL' => PDO::PARAM_NULL, + default => PDO::PARAM_STR + }; + + $this->statement->bindValue($parameter, $value, $type); + } + $this->statement->execute(); $this->affected = $this->statement->rowCount(); $this->lastId = Str::startsWith($query, 'insert ', true) ? $this->connection->lastInsertId() : null; @@ -536,7 +554,7 @@ class Database * MySQL database connector */ Database::$types['mysql'] = [ - 'sql' => 'Kirby\Database\Sql\Mysql', + 'sql' => Mysql::class, 'dsn' => function (array $params): string { if (isset($params['host']) === false && isset($params['socket']) === false) { throw new InvalidArgumentException('The mysql connection requires either a "host" or a "socket" parameter'); @@ -564,7 +582,7 @@ Database::$types['mysql'] = [ $parts[] = 'dbname=' . $params['database']; } - $parts[] = 'charset=' . ($params['charset'] ?? 'utf8'); + $parts[] = 'charset=' . ($params['charset'] ?? 'utf8mb4'); return 'mysql:' . implode(';', $parts); } @@ -574,7 +592,7 @@ Database::$types['mysql'] = [ * SQLite database connector */ Database::$types['sqlite'] = [ - 'sql' => 'Kirby\Database\Sql\Sqlite', + 'sql' => Sqlite::class, 'dsn' => function (array $params): string { if (isset($params['database']) === false) { throw new InvalidArgumentException('The sqlite connection requires a "database" parameter'); diff --git a/kirby/src/Database/Db.php b/kirby/src/Database/Db.php index 01c3f22..82d07b5 100644 --- a/kirby/src/Database/Db.php +++ b/kirby/src/Database/Db.php @@ -47,7 +47,8 @@ class Db 'password' => Config::get('db.password', ''), 'database' => Config::get('db.database', ''), 'prefix' => Config::get('db.prefix', ''), - 'port' => Config::get('db.port', '') + 'port' => Config::get('db.port', ''), + 'charset' => Config::get('db.charset') ]; return static::$connection = new Database($params); @@ -191,7 +192,7 @@ Db::$queries['column'] = function ( * @param array $values An array of values which should be inserted * @return mixed Returns the last inserted id on success or false */ -Db::$queries['insert'] = function (string $table, array $values) { +Db::$queries['insert'] = function (string $table, array $values): mixed { return Db::table($table)->insert($values); }; @@ -225,9 +226,8 @@ Db::$queries['delete'] = function (string $table, $where = null): bool { * * @param string $table The name of the table which should be queried * @param mixed $where An optional WHERE clause - * @return int */ -Db::$queries['count'] = function (string $table, $where = null): int { +Db::$queries['count'] = function (string $table, mixed $where = null): int { return Db::table($table)->where($where)->count(); }; diff --git a/kirby/src/Database/Query.php b/kirby/src/Database/Query.php index 7e82e62..73dfe10 100644 --- a/kirby/src/Database/Query.php +++ b/kirby/src/Database/Query.php @@ -259,8 +259,11 @@ class Query * @param string $type The join type. Uses an inner join by default * @return $this */ - public function join(string $table, string $on, string $type = 'JOIN'): static - { + public function join( + string $table, + string $on, + string $type = 'JOIN' + ): static { $join = [ 'table' => $table, 'on' => $on, @@ -418,7 +421,7 @@ class Query * @param string|null $order * @return $this */ - public function order(string $order = null) + public function order(string|null $order = null) { $this->order = $order; return $this; @@ -678,7 +681,7 @@ class Query $collection = $this ->offset($pagination->offset()) ->limit($pagination->limit()) - ->iterator('Kirby\Toolkit\Collection') + ->iterator(Collection::class) ->all(); $this->iterator($iterator); @@ -825,13 +828,13 @@ class Query if ($args[0] === null) { return $current; - // ->where('username like "myuser"'); + // ->where('username like "myuser"'); } elseif (is_string($args[0]) === true) { // simply add the entire string to the where clause // escaping or using bindings has to be done before calling this method $result = $args[0]; - // ->where(['username' => 'myuser']); + // ->where(['username' => 'myuser']); } elseif (is_array($args[0]) === true) { // simple array mode (AND operator) $sql = $this->database->sql()->values($this->table, $args[0], ' AND ', true, true); @@ -865,8 +868,8 @@ class Query // store the bindings $this->bindings($args[1]); - // ->where('username like ?', 'myuser') - } elseif (is_string($args[0]) === true && is_string($args[1]) === true) { + // ->where('username like ?', 'myuser') + } elseif (is_string($args[0]) === true && is_scalar($args[1]) === true) { // prepared where clause $result = $args[0]; @@ -884,9 +887,10 @@ class Query $key = $sql->columnName($this->table, $args[0]); // ->where('username', 'in', ['myuser', 'myotheruser']); + // ->where('quantity', 'between', [10, 50]); $predicate = trim(strtoupper($args[1])); if (is_array($args[2]) === true) { - if (in_array($predicate, ['IN', 'NOT IN']) === false) { + if (in_array($predicate, ['IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN']) === false) { throw new InvalidArgumentException('Invalid predicate ' . $predicate); } @@ -900,15 +904,20 @@ class Query $values[] = $valueBinding; } - // add that to the where clause in parenthesis - $result = $key . ' ' . $predicate . ' (' . implode(', ', $values) . ')'; + // add that to the where clause in parenthesis or seperated by AND + $values = match ($predicate) { + 'IN', + 'NOT IN' => '(' . implode(', ', $values) . ')', + 'BETWEEN', + 'NOT BETWEEN' => $values[0] . ' AND ' . $values[1] + }; + $result = $key . ' ' . $predicate . ' ' . $values; - // ->where('username', 'like', 'myuser'); + // ->where('username', 'like', 'myuser'); } else { $predicates = [ '=', '>=', '>', '<=', '<', '<>', '!=', '<=>', 'IS', 'IS NOT', - 'BETWEEN', 'NOT BETWEEN', 'LIKE', 'NOT LIKE', 'SOUNDS LIKE', 'REGEXP', 'NOT REGEXP' diff --git a/kirby/src/Database/Sql.php b/kirby/src/Database/Sql.php index e5ec88e..877d61f 100644 --- a/kirby/src/Database/Sql.php +++ b/kirby/src/Database/Sql.php @@ -112,7 +112,7 @@ abstract class Sql public function columnName(string $table, string $column, bool $enforceQualified = false): string|null { // ensure we have clean $table and $column values without qualified identifiers - list($table, $column) = $this->splitIdentifier($table, $column); + [$table, $column] = $this->splitIdentifier($table, $column); // combine the identifiers again if ($this->database->validateColumn($table, $column) === true) { @@ -132,11 +132,13 @@ abstract class Sql { return [ 'id' => '{{ name }} INT(11) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY', - 'varchar' => '{{ name }} varchar(255) {{ null }} {{ default }} {{ unique }}', + 'varchar' => '{{ name }} varchar({{ size }}) {{ null }} {{ default }} {{ unique }}', 'text' => '{{ name }} TEXT {{ unique }}', - 'int' => '{{ name }} INT(11) UNSIGNED {{ null }} {{ default }} {{ unique }}', + 'int' => '{{ name }} INT(11) {{ unsigned }} {{ null }} {{ default }} {{ unique }}', 'timestamp' => '{{ name }} TIMESTAMP {{ null }} {{ default }} {{ unique }}', - 'bool' => '{{ name }} TINYINT(1) {{ null }} {{ default }} {{ unique }}' + 'bool' => '{{ name }} TINYINT(1) {{ null }} {{ default }} {{ unique }}', + 'float' => '{{ name }} DOUBLE {{ null }} {{ default }} {{ unique }}', + 'decimal' => '{{ name }} DECIMAL({{ precision }}, {{ decimalPlaces }}) {{ null }} {{ default }} {{ unique }}' ]; } @@ -144,7 +146,7 @@ abstract class Sql * Combines an identifier (table and column) * * @param $values bool Whether the identifier is going to be used for a VALUES clause; - * only relevant for SQLite + * only relevant for SQLite */ public function combineIdentifier(string $table, string $column, bool $values = false): string { @@ -157,6 +159,10 @@ abstract class Sql * @param string $name Column name * @param array $column Column definition array; valid keys: * - `type` (required): Column template to use + * - `unsigned`: Whether an int column is signed or unsigned (boolean) + * - `size`: The size of varchar (int) + * - `precision`: The precision of a decimal type + * - `decimalPlaces`: The number of decimal places for a decimal type * - `null`: Whether the column may be NULL (boolean) * - `key`: Index this column is part of; special values `'primary'` for PRIMARY KEY and `true` for automatic naming * - `unique`: Whether the index (or if not set the column itself) has a UNIQUE constraint @@ -191,6 +197,13 @@ abstract class Sql } } + // unsigned (defaults to true for backwards compatibility) + if (isset($column['unsigned']) === true && $column['unsigned'] === false) { + $unsigned = ''; + } else { + $unsigned = 'UNSIGNED'; + } + // unique $uniqueKey = false; $uniqueColumn = null; @@ -208,11 +221,15 @@ abstract class Sql $columnDefault = $this->columnDefault($name, $column); $query = trim(Str::template($template, [ - 'name' => $this->quoteIdentifier($name), - 'null' => $null, - 'default' => $columnDefault['query'], - 'unique' => $uniqueColumn - ], ['fallback' => ''])); + 'name' => $this->quoteIdentifier($name), + 'unsigned' => $unsigned, + 'size' => $column['size'] ?? 255, + 'precision' => $column['precision'] ?? 14, + 'decimalPlaces' => $column['decimalPlaces'] ?? 4, + 'null' => $null, + 'default' => $columnDefault['query'], + 'unique' => $uniqueColumn + ], ['fallback' => ''])); return [ 'query' => $query, @@ -607,7 +624,7 @@ abstract class Sql $result = []; foreach ($columns as $column) { - list($table, $columnPart) = $this->splitIdentifier($table, $column); + [$table, $columnPart] = $this->splitIdentifier($table, $column); if ($this->validateColumn($table, $columnPart) === true) { $result[] = $this->combineIdentifier($table, $columnPart); diff --git a/kirby/src/Database/Sql/Sqlite.php b/kirby/src/Database/Sql/Sqlite.php index 05097a5..267014e 100644 --- a/kirby/src/Database/Sql/Sqlite.php +++ b/kirby/src/Database/Sql/Sqlite.php @@ -42,7 +42,9 @@ class Sqlite extends Sql 'text' => '{{ name }} TEXT {{ null }} {{ default }} {{ unique }}', 'int' => '{{ name }} INTEGER {{ null }} {{ default }} {{ unique }}', 'timestamp' => '{{ name }} INTEGER {{ null }} {{ default }} {{ unique }}', - 'bool' => '{{ name }} INTEGER {{ null }} {{ default }} {{ unique }}' + 'bool' => '{{ name }} INTEGER {{ null }} {{ default }} {{ unique }}', + 'float' => '{{ name }} REAL {{ null }} {{ default }} {{ unique }}', + 'decimal' => '{{ name }} REAL {{ null }} {{ default }} {{ unique }}' ]; } @@ -128,7 +130,7 @@ class Sqlite extends Sql public function tables(): array { return [ - 'query' => 'SELECT name FROM sqlite_master WHERE type = "table" OR type = "view"', + 'query' => 'SELECT name FROM sqlite_master WHERE type = \'table\' OR type = \'view\'', 'bindings' => [] ]; } diff --git a/kirby/src/Email/Body.php b/kirby/src/Email/Body.php index 8fc0a0b..a25904a 100644 --- a/kirby/src/Email/Body.php +++ b/kirby/src/Email/Body.php @@ -17,17 +17,29 @@ use Kirby\Toolkit\Properties; */ class Body { - use Properties; - - protected string|null $html = null; - protected string|null $text = null; + protected string|null $html; + protected string|null $text; /** * Email body constructor */ public function __construct(array $props = []) { - $this->setProperties($props); + $this->html = $props['html'] ?? null; + $this->text = $props['text'] ?? null; + } + + /** + * Creates a new instance while + * merging initial and new properties + * @deprecated 4.0.0 + */ + public function clone(array $props = []): static + { + return new static(array_merge_recursive([ + 'html' => $this->html, + 'text' => $this->text + ], $props)); } /** @@ -47,24 +59,13 @@ class Body } /** - * Sets the HTML content for the email body - * - * @return $this + * @since 4.0.0 */ - protected function setHtml(string|null $html = null): static + public function toArray(): array { - $this->html = $html; - return $this; - } - - /** - * Sets the plain text content for the email body - * - * @return $this - */ - protected function setText(string|null $text = null): static - { - $this->text = $text; - return $this; + return [ + 'html' => $this->html(), + 'text' => $this->text() + ]; } } diff --git a/kirby/src/Email/Email.php b/kirby/src/Email/Email.php index 73fb883..2f1b15f 100644 --- a/kirby/src/Email/Email.php +++ b/kirby/src/Email/Email.php @@ -4,7 +4,7 @@ namespace Kirby\Email; use Closure; use Exception; -use Kirby\Toolkit\Properties; +use Kirby\Exception\InvalidArgumentException; use Kirby\Toolkit\V; /** @@ -19,8 +19,6 @@ use Kirby\Toolkit\V; */ class Email { - use Properties; - /** * If set to `true`, the debug mode is enabled * for all emails @@ -33,56 +31,48 @@ class Email */ public static array $emails = []; - /** - * @var array - */ - protected array|null $attachments = null; - - protected Body|null $body = null; - - /** - * @var array - */ - protected array|null $bcc = null; - - protected Closure|null $beforeSend = null; - - /** - * @var array - */ - protected array|null $cc = null; - - /** - * @var string - */ - protected string|null $from = null; - protected string|null $fromName = null; - protected bool $isSent = false; - /** - * @var string - */ - protected string|null $replyTo = null; - protected string|null $replyToName = null; - - /** - * @var string - */ - protected string|null $subject = null; - - /** - * @var array - */ - protected array|null $to = null; - protected array|null $transport = null; + protected array $attachments; + protected Body $body; + protected array $bcc; + protected Closure|null $beforeSend; + protected array $cc; + protected string $from; + protected string|null $fromName; + protected string $replyTo; + protected string|null $replyToName; + protected string $subject; + protected array $to; + protected array|null $transport; /** * Email constructor */ public function __construct(array $props = [], bool $debug = false) { - $this->setProperties($props); + foreach (['body', 'from', 'to', 'subject'] as $required) { + if (isset($props[$required]) === false) { + throw new InvalidArgumentException('The property "' . $required . '" is required'); + } + } + + if (is_string($props['body']) === true) { + $props['body'] = ['text' => $props['body']]; + } + + $this->attachments = $props['attachments'] ?? []; + $this->bcc = $this->resolveEmail($props['bcc'] ?? null); + $this->beforeSend = $props['beforeSend'] ?? null; + $this->body = new Body($props['body']); + $this->cc = $this->resolveEmail($props['cc'] ?? null); + $this->from = $this->resolveEmail($props['from'], false); + $this->fromName = $props['fromName'] ?? null; + $this->replyTo = $this->resolveEmail($props['replyTo'] ?? null, false); + $this->replyToName = $props['replyToName'] ?? null; + $this->subject = $props['subject']; + $this->to = $this->resolveEmail($props['to']); + $this->transport = $props['transport'] ?? null; // @codeCoverageIgnoreStart if (static::$debug === false && $debug === false) { @@ -134,6 +124,29 @@ class Email return $this->cc; } + /** + * Creates a new instance while + * merging initial and new properties + * @deprecated 4.0.0 + */ + public function clone(array $props = []): static + { + return new static(array_merge_recursive([ + 'attachments' => $this->attachments, + 'bcc' => $this->bcc, + 'beforeSend' => $this->beforeSend, + 'body' => $this->body->toArray(), + 'cc' => $this->cc, + 'from' => $this->from, + 'fromName' => $this->fromName, + 'replyTo' => $this->replyTo, + 'replyToName' => $this->replyToName, + 'subject' => $this->subject, + 'to' => $this->to, + 'transport' => $this->transport + ], $props)); + } + /** * Returns default transport settings */ @@ -237,142 +250,6 @@ class Email return $this->isSent = true; } - /** - * Sets the email attachments - * - * @return $this - */ - protected function setAttachments(array|null $attachments = null): static - { - $this->attachments = $attachments ?? []; - return $this; - } - - /** - * Sets the email body - * - * @return $this - */ - protected function setBody(string|array $body): static - { - if (is_string($body) === true) { - $body = ['text' => $body]; - } - - $this->body = new Body($body); - return $this; - } - - /** - * Sets "bcc" recipients - * - * @return $this - */ - protected function setBcc(string|array|null $bcc = null): static - { - $this->bcc = $this->resolveEmail($bcc); - return $this; - } - - /** - * Sets the "beforeSend" callback - * - * @return $this - */ - protected function setBeforeSend(Closure|null $beforeSend = null): static - { - $this->beforeSend = $beforeSend; - return $this; - } - - /** - * Sets "cc" recipients - * - * @return $this - */ - protected function setCc(string|array|null $cc = null): static - { - $this->cc = $this->resolveEmail($cc); - return $this; - } - - /** - * Sets the "from" email address - * - * @return $this - */ - protected function setFrom(string $from): static - { - $this->from = $this->resolveEmail($from, false); - return $this; - } - - /** - * Sets the "from" name - * - * @return $this - */ - protected function setFromName(string|null $fromName = null): static - { - $this->fromName = $fromName; - return $this; - } - - /** - * Sets the "reply to" email address - * - * @return $this - */ - protected function setReplyTo(string|null $replyTo = null): static - { - $this->replyTo = $this->resolveEmail($replyTo, false); - return $this; - } - - /** - * Sets the "reply to" name - * - * @return $this - */ - protected function setReplyToName(string|null $replyToName = null): static - { - $this->replyToName = $replyToName; - return $this; - } - - /** - * Sets the email subject - * - * @return $this - */ - protected function setSubject(string $subject): static - { - $this->subject = $subject; - return $this; - } - - /** - * Sets the recipients of the email - * - * @return $this - */ - protected function setTo(string|array $to): static - { - $this->to = $this->resolveEmail($to); - return $this; - } - - /** - * Sets the email transport settings - * - * @return $this - */ - protected function setTransport(array|null $transport = null): static - { - $this->transport = $transport; - return $this; - } - /** * Returns the email subject */ @@ -396,4 +273,24 @@ class Email { return $this->transport ?? $this->defaultTransport(); } + + /** + * @since 4.0.0 + */ + public function toArray(): array + { + return [ + 'attachments' => $this->attachments(), + 'bcc' => $this->bcc(), + 'body' => $this->body()->toArray(), + 'cc' => $this->cc(), + 'from' => $this->from(), + 'fromName' => $this->fromName(), + 'replyTo' => $this->replyTo(), + 'replyToName' => $this->replyToName(), + 'subject' => $this->subject(), + 'to' => $this->to(), + 'transport' => $this->transport() + ]; + } } diff --git a/kirby/src/Exception/AuthException.php b/kirby/src/Exception/AuthException.php new file mode 100644 index 0000000..1ea6203 --- /dev/null +++ b/kirby/src/Exception/AuthException.php @@ -0,0 +1,21 @@ + + * @link https://getkirby.com + * @copyright Bastian Allgeier + * @license https://opensource.org/licenses/MIT + */ +class AuthException extends Exception +{ + protected static string $defaultKey = 'auth'; + protected static string $defaultFallback = 'Unauthenticated'; + protected static int $defaultHttpCode = 401; +} diff --git a/kirby/src/Exception/BadMethodCallException.php b/kirby/src/Exception/BadMethodCallException.php index 68c1f05..f8a1d1b 100644 --- a/kirby/src/Exception/BadMethodCallException.php +++ b/kirby/src/Exception/BadMethodCallException.php @@ -14,8 +14,8 @@ namespace Kirby\Exception; */ class BadMethodCallException extends Exception { - protected static $defaultKey = 'invalidMethod'; - protected static $defaultFallback = 'The method "{ method }" does not exist'; - protected static $defaultHttpCode = 400; - protected static $defaultData = ['method' => null]; + protected static string $defaultKey = 'invalidMethod'; + protected static string $defaultFallback = 'The method "{ method }" does not exist'; + protected static int $defaultHttpCode = 400; + protected static array $defaultData = ['method' => null]; } diff --git a/kirby/src/Exception/DuplicateException.php b/kirby/src/Exception/DuplicateException.php index 74bc859..1bea2bf 100644 --- a/kirby/src/Exception/DuplicateException.php +++ b/kirby/src/Exception/DuplicateException.php @@ -15,7 +15,7 @@ namespace Kirby\Exception; */ class DuplicateException extends Exception { - protected static $defaultKey = 'duplicate'; - protected static $defaultFallback = 'The entry exists'; - protected static $defaultHttpCode = 400; + protected static string $defaultKey = 'duplicate'; + protected static string $defaultFallback = 'The entry exists'; + protected static int $defaultHttpCode = 400; } diff --git a/kirby/src/Exception/ErrorPageException.php b/kirby/src/Exception/ErrorPageException.php index 7e9ed69..bd26c6f 100644 --- a/kirby/src/Exception/ErrorPageException.php +++ b/kirby/src/Exception/ErrorPageException.php @@ -15,7 +15,7 @@ namespace Kirby\Exception; */ class ErrorPageException extends Exception { - protected static $defaultKey = 'errorPage'; - protected static $defaultFallback = 'Triggered error page'; - protected static $defaultHttpCode = 404; + protected static string $defaultKey = 'errorPage'; + protected static string $defaultFallback = 'Triggered error page'; + protected static int $defaultHttpCode = 404; } diff --git a/kirby/src/Exception/Exception.php b/kirby/src/Exception/Exception.php index d3a0ba9..ae16686 100644 --- a/kirby/src/Exception/Exception.php +++ b/kirby/src/Exception/Exception.php @@ -2,6 +2,7 @@ namespace Kirby\Exception; +use Kirby\Cms\App; use Kirby\Http\Environment; use Kirby\Toolkit\I18n; use Kirby\Toolkit\Str; @@ -21,48 +22,39 @@ class Exception extends \Exception { /** * Data variables that can be used inside the exception message - * - * @var array */ - protected $data; + protected array $data; /** * HTTP code that corresponds with the exception - * - * @var int */ - protected $httpCode; + protected int $httpCode; /** * Additional details that are not included in the exception message - * - * @var array */ - protected $details; + protected array $details; /** - * Whether the exception message could be translated into the user's language - * - * @var bool + * Whether the exception message could be translated + * into the user's language */ - protected $isTranslated = true; + protected bool $isTranslated = true; /** * Defaults that can be overridden by specific * exception classes */ - protected static $defaultKey = 'general'; - protected static $defaultFallback = 'An error occurred'; - protected static $defaultData = []; - protected static $defaultHttpCode = 500; - protected static $defaultDetails = []; + protected static string $defaultKey = 'general'; + protected static string $defaultFallback = 'An error occurred'; + protected static array $defaultData = []; + protected static int $defaultHttpCode = 500; + protected static array $defaultDetails = []; /** * Prefix for the exception key (e.g. 'error.general') - * - * @var string */ - private static $prefix = 'error'; + private static string $prefix = 'error'; /** * Class constructor @@ -71,7 +63,7 @@ class Exception extends \Exception * 'data', 'httpCode', 'details' and 'previous') or * just the message string */ - public function __construct($args = []) + public function __construct(array|string $args = []) { // set data and httpCode from provided arguments or defaults $this->data = $args['data'] ?? static::$defaultData; @@ -79,19 +71,25 @@ class Exception extends \Exception $this->details = $args['details'] ?? static::$defaultDetails; // define the Exception key - $key = self::$prefix . '.' . ($args['key'] ?? static::$defaultKey); + $key = $args['key'] ?? static::$defaultKey; + + if (Str::startsWith($key, self::$prefix . '.') === false) { + $key = self::$prefix . '.' . $key; + } if (is_string($args) === true) { $this->isTranslated = false; parent::__construct($args); } else { // define whether message can/should be translated - $translate = ($args['translate'] ?? true) === true && class_exists('Kirby\Cms\App') === true; + $translate = + ($args['translate'] ?? true) === true && + class_exists(App::class) === true; // fallback waterfall for message string $message = null; - if ($translate) { + if ($translate === true) { // 1. translation for provided key in current language // 2. translation for provided key in default language if (isset($args['key']) === true) { @@ -106,7 +104,7 @@ class Exception extends \Exception $this->isTranslated = false; } - if ($translate) { + if ($translate === true) { // 4. translation for default key in current language // 5. translation for default key in default language if ($message === null) { @@ -122,11 +120,7 @@ class Exception extends \Exception } // format message with passed data - $message = Str::template($message, $this->data, [ - 'fallback' => '-', - 'start' => '{', - 'end' => '}' - ]); + $message = Str::template($message, $this->data, ['fallback' => '-']); // handover to Exception parent class constructor parent::__construct($message, 0, $args['previous'] ?? null); @@ -139,8 +133,6 @@ class Exception extends \Exception /** * Returns the file in which the Exception was created * relative to the document root - * - * @return string */ final public function getFileRelative(): string { @@ -156,8 +148,6 @@ class Exception extends \Exception /** * Returns the data variables from the message - * - * @return array */ final public function getData(): array { @@ -167,8 +157,6 @@ class Exception extends \Exception /** * Returns the additional details that are * not included in the message - * - * @return array */ final public function getDetails(): array { @@ -177,8 +165,6 @@ class Exception extends \Exception /** * Returns the exception key (error type) - * - * @return string */ final public function getKey(): string { @@ -188,8 +174,6 @@ class Exception extends \Exception /** * Returns the HTTP code that corresponds * with the exception - * - * @return array */ final public function getHttpCode(): int { @@ -199,8 +183,6 @@ class Exception extends \Exception /** * Returns whether the exception message could * be translated into the user's language - * - * @return bool */ final public function isTranslated(): bool { @@ -209,8 +191,6 @@ class Exception extends \Exception /** * Converts the object to an array - * - * @return array */ public function toArray(): array { diff --git a/kirby/src/Exception/InvalidArgumentException.php b/kirby/src/Exception/InvalidArgumentException.php index d6cef17..25603b7 100644 --- a/kirby/src/Exception/InvalidArgumentException.php +++ b/kirby/src/Exception/InvalidArgumentException.php @@ -14,8 +14,8 @@ namespace Kirby\Exception; */ class InvalidArgumentException extends Exception { - protected static $defaultKey = 'invalidArgument'; - protected static $defaultFallback = 'Invalid argument "{ argument }" in method "{ method }"'; - protected static $defaultHttpCode = 400; - protected static $defaultData = ['argument' => null, 'method' => null]; + protected static string $defaultKey = 'invalidArgument'; + protected static string $defaultFallback = 'Invalid argument "{ argument }" in method "{ method }"'; + protected static int $defaultHttpCode = 400; + protected static array $defaultData = ['argument' => null, 'method' => null]; } diff --git a/kirby/src/Exception/LogicException.php b/kirby/src/Exception/LogicException.php index d1fbc55..8fac228 100644 --- a/kirby/src/Exception/LogicException.php +++ b/kirby/src/Exception/LogicException.php @@ -14,7 +14,7 @@ namespace Kirby\Exception; */ class LogicException extends Exception { - protected static $defaultKey = 'logic'; - protected static $defaultFallback = 'This task cannot be finished'; - protected static $defaultHttpCode = 400; + protected static string $defaultKey = 'logic'; + protected static string $defaultFallback = 'This task cannot be finished'; + protected static int $defaultHttpCode = 400; } diff --git a/kirby/src/Exception/NotFoundException.php b/kirby/src/Exception/NotFoundException.php index 1f48c4b..5c7e284 100644 --- a/kirby/src/Exception/NotFoundException.php +++ b/kirby/src/Exception/NotFoundException.php @@ -14,7 +14,7 @@ namespace Kirby\Exception; */ class NotFoundException extends Exception { - protected static $defaultKey = 'notFound'; - protected static $defaultFallback = 'Not found'; - protected static $defaultHttpCode = 404; + protected static string $defaultKey = 'notFound'; + protected static string $defaultFallback = 'Not found'; + protected static int $defaultHttpCode = 404; } diff --git a/kirby/src/Exception/PermissionException.php b/kirby/src/Exception/PermissionException.php index 4863f66..ae82a66 100644 --- a/kirby/src/Exception/PermissionException.php +++ b/kirby/src/Exception/PermissionException.php @@ -15,7 +15,7 @@ namespace Kirby\Exception; */ class PermissionException extends Exception { - protected static $defaultKey = 'permission'; - protected static $defaultFallback = 'You are not allowed to do this'; - protected static $defaultHttpCode = 403; + protected static string $defaultKey = 'permission'; + protected static string $defaultFallback = 'You are not allowed to do this'; + protected static int $defaultHttpCode = 403; } diff --git a/kirby/src/Filesystem/Asset.php b/kirby/src/Filesystem/Asset.php index 1ced36b..021d6c8 100644 --- a/kirby/src/Filesystem/Asset.php +++ b/kirby/src/Filesystem/Asset.php @@ -27,18 +27,19 @@ class Asset /** * Relative file path */ - protected string|null $path = null; + protected string|null $path; + /** * Creates a new Asset object for the given path. */ public function __construct(string $path) { - $this->setProperties([ - 'path' => dirname($path), - 'root' => $this->kirby()->root('index') . '/' . $path, - 'url' => $this->kirby()->url('base') . '/' . $path - ]); + $this->root = $this->kirby()->root('index') . '/' . $path; + $this->url = $this->kirby()->url('base') . '/' . $path; + + $path = dirname($path); + $this->path = $path === '.' ? '' : $path; } /** @@ -46,7 +47,7 @@ class Asset * * @throws \Kirby\Exception\BadMethodCallException */ - public function __call(string $method, array $arguments = []) + public function __call(string $method, array $arguments = []): mixed { // public property access if (isset($this->$method) === true) { @@ -114,15 +115,4 @@ class Asset { return $this->path; } - - /** - * Setter for the path - * - * @return $this - */ - protected function setPath(string $path): static - { - $this->path = $path === '.' ? '' : $path; - return $this; - } } diff --git a/kirby/src/Filesystem/Dir.php b/kirby/src/Filesystem/Dir.php index b51b80b..c12b7d1 100644 --- a/kirby/src/Filesystem/Dir.php +++ b/kirby/src/Filesystem/Dir.php @@ -149,7 +149,7 @@ class Dir string $dir, bool $recursive = false, array|false|null $ignore = [], - string $path = null + string|null $path = null ): array { $result = []; $dir = realpath($dir); @@ -169,7 +169,10 @@ class Dir $result[] = $entry; if ($recursive === true && is_dir($root) === true) { - $result = array_merge($result, static::index($root, true, $ignore, $entry)); + $result = [ + ...$result, + ...static::index($root, true, $ignore, $entry) + ]; } } @@ -217,143 +220,145 @@ class Dir array|null $contentIgnore = null, bool $multilang = false ): array { - $dir = realpath($dir); - $inventory = [ 'children' => [], 'files' => [], 'template' => 'default', ]; + $dir = realpath($dir); + if ($dir === false) { return $inventory; } - $items = static::read($dir, $contentIgnore); - // a temporary store for all content files $content = []; - // sort all items naturally to avoid sorting issues later + // read and sort all items naturally to avoid sorting issues later + $items = static::read($dir, $contentIgnore); natsort($items); + // loop through all directory items and collect all relevant information foreach ($items as $item) { - // ignore all items with a leading dot + // ignore all items with a leading dot or underscore if (in_array(substr($item, 0, 1), ['.', '_']) === true) { continue; } $root = $dir . '/' . $item; + // collect all directories as children if (is_dir($root) === true) { - // extract the slug and num of the directory - if (preg_match('/^([0-9]+)' . static::$numSeparator . '(.*)$/', $item, $match)) { - $num = (int)$match[1]; - $slug = $match[2]; - } else { - $num = null; - $slug = $item; - } - - $inventory['children'][] = [ - 'dirname' => $item, - 'model' => null, - 'num' => $num, - 'root' => $root, - 'slug' => $slug, - ]; - } else { - $extension = pathinfo($item, PATHINFO_EXTENSION); - - switch ($extension) { - case 'htm': - case 'html': - case 'php': - // don't track those files - break; - case $contentExtension: - $content[] = pathinfo($item, PATHINFO_FILENAME); - break; - default: - $inventory['files'][$item] = [ - 'filename' => $item, - 'extension' => $extension, - 'root' => $root, - ]; - } - } - } - - // remove the language codes from all content filenames - if ($multilang === true) { - foreach ($content as $key => $filename) { - $content[$key] = pathinfo($filename, PATHINFO_FILENAME); + $inventory['children'][] = static::inventoryChild( + $item, + $root, + $contentExtension, + $multilang + ); + continue; } - $content = array_unique($content); + $extension = pathinfo($item, PATHINFO_EXTENSION); + + // don't track files with these extensions + if (in_array($extension, ['htm', 'html', 'php']) === true) { + continue; + } + + // collect all content files separately, + // not as inventory entries + if ($extension === $contentExtension) { + $filename = pathinfo($item, PATHINFO_FILENAME); + + // remove the language codes from all content filenames + if ($multilang === true) { + $filename = pathinfo($filename, PATHINFO_FILENAME); + } + + $content[] = $filename; + continue; + } + + // collect all other files + $inventory['files'][$item] = [ + 'filename' => $item, + 'extension' => $extension, + 'root' => $root, + ]; } - $inventory = static::inventoryContent($inventory, $content); - $inventory = static::inventoryModels($inventory, $contentExtension, $multilang); + $content = array_unique($content); + + $inventory['template'] = static::inventoryTemplate( + $content, + $inventory['files'] + ); return $inventory; } /** - * Take all content files, - * remove those who are meta files and - * detect the main content file + * Collect information for a child for the inventory */ - protected static function inventoryContent(array $inventory, array $content): array - { - // filter meta files from the content file - if (empty($content) === true) { - $inventory['template'] = 'default'; - return $inventory; + protected static function inventoryChild( + string $item, + string $root, + string $contentExtension = 'txt', + bool $multilang = false + ): array { + // extract the slug and num of the directory + if ($separator = strpos($item, static::$numSeparator)) { + $num = (int)substr($item, 0, $separator); + $slug = substr($item, $separator + 1); } - foreach ($content as $contentName) { - // could be a meta file. i.e. cover.jpg - if (isset($inventory['files'][$contentName]) === true) { + // determine the model + if (empty(Page::$models) === false) { + if ($multilang === true) { + $code = App::instance()->defaultLanguage()->code(); + $contentExtension = $code . '.' . $contentExtension; + } + + // look if a content file can be found + // for any of the available models + foreach (Page::$models as $modelName => $modelClass) { + if (is_file($root . '/' . $modelName . '.' . $contentExtension) === true) { + $model = $modelName; + break; + } + } + } + + return [ + 'dirname' => $item, + 'model' => $model ?? null, + 'num' => $num ?? null, + 'root' => $root, + 'slug' => $slug ?? $item, + ]; + } + + /** + * Determines the main template for the inventory + * from all collected content files, ignore file meta files + */ + protected static function inventoryTemplate( + array $content, + array $files, + ): string { + foreach ($content as $name) { + // is a meta file corresponding to an actual file, i.e. cover.jpg + if (isset($files[$name]) === true) { continue; } // it's most likely the template - $inventory['template'] = $contentName; + // (will overwrite and use the last match for historic reasons) + $template = $name; } - return $inventory; - } - - /** - * Go through all inventory children - * and inject a model for each - */ - protected static function inventoryModels( - array $inventory, - string $contentExtension, - bool $multilang = false - ): array { - // inject models - if ( - empty($inventory['children']) === false && - empty(Page::$models) === false - ) { - if ($multilang === true) { - $contentExtension = App::instance()->defaultLanguage()->code() . '.' . $contentExtension; - } - - foreach ($inventory['children'] as $key => $child) { - foreach (Page::$models as $modelName => $modelClass) { - if (file_exists($child['root'] . '/' . $modelName . '.' . $contentExtension) === true) { - $inventory['children'][$key]['model'] = $modelName; - break; - } - } - } - } - - return $inventory; + return $template ?? 'default'; } /** @@ -402,10 +407,8 @@ class Dir $parent = dirname($dir); - if ($recursive === true) { - if (is_dir($parent) === false) { - static::make($parent, true); - } + if ($recursive === true && is_dir($parent) === false) { + static::make($parent, true); } if (is_writable($parent) === false) { @@ -426,19 +429,22 @@ class Dir * subfolders have been modified for the last time. * * @param string $dir The path of the directory + * @param 'date'|'intl'|'strftime'|null $handler Custom date handler or `null` + * for the globally configured one */ - public static function modified(string $dir, string $format = null, string $handler = 'date'): int|string - { + public static function modified( + string $dir, + string|null $format = null, + string|null $handler = null + ): int|string { $modified = filemtime($dir); $items = static::read($dir); foreach ($items as $item) { - if (is_file($dir . '/' . $item) === true) { - $newModified = filemtime($dir . '/' . $item); - } else { - $newModified = static::modified($dir . '/' . $item); - } - + $newModified = match (is_file($dir . '/' . $item)) { + true => filemtime($dir . '/' . $item), + false => static::modified($dir . '/' . $item) + }; $modified = ($newModified > $modified) ? $newModified : $modified; } @@ -593,7 +599,10 @@ class Dir return true; } - if (is_dir($subdir) === true && static::wasModifiedAfter($subdir, $time) === true) { + if ( + is_dir($subdir) === true && + static::wasModifiedAfter($subdir, $time) === true + ) { return true; } } diff --git a/kirby/src/Filesystem/F.php b/kirby/src/Filesystem/F.php index 10938a3..2f7e6e6 100644 --- a/kirby/src/Filesystem/F.php +++ b/kirby/src/Filesystem/F.php @@ -475,12 +475,13 @@ class F /** * Get the file's last modification time. * - * @param string $handler date, intl or strftime + * @param 'date'|'intl'|'strftime'|null $handler Custom date handler or `null` + * for the globally configured one */ public static function modified( string $file, string|IntlDateFormatter|null $format = null, - string $handler = 'date' + string|null $handler = null ): string|int|false { if (file_exists($file) !== true) { return false; @@ -579,7 +580,7 @@ class F } // the math magic - $size = round($size / pow(1024, ($unit = floor(log($size, 1024)))), 2); + $size = round($size / 1024 ** ($unit = floor(log($size, 1024))), 2); // format the number if requested if ($locale !== false) { @@ -727,7 +728,8 @@ class F } /** - * Sanitize a filename to strip unwanted special characters + * Sanitize a file's full name (filename and extension) + * to strip unwanted special characters * * * @@ -740,12 +742,46 @@ class F */ public static function safeName(string $string): string { - $name = static::name($string); - $extension = static::extension($string); - $safeName = Str::slug($name, '-', 'a-z0-9@._-'); - $safeExtension = empty($extension) === false ? '.' . Str::slug($extension) : ''; + $basename = static::safeBasename($string); + $extension = static::safeExtension($string); - return $safeName . $safeExtension; + if (empty($extension) === false) { + $extension = '.' . $extension; + } + + return $basename . $extension; + } + + /** + * Sanitize a file's name (without extension) + * @since 4.0.0 + */ + public static function safeBasename( + string $string, + bool $extract = true + ): string { + // extract only the name part from whole filename string + if ($extract === true) { + $string = static::name($string); + } + + return Str::slug($string, '-', 'a-z0-9@._-'); + } + + /** + * Sanitize a file's extension + * @since 4.0.0 + */ + public static function safeExtension( + string $string, + bool $extract = true + ): string { + // extract only the extension part from whole filename string + if ($extract === true) { + $string = static::extension($string); + } + + return Str::slug($string); } /** @@ -776,11 +812,11 @@ class F ); } - try { - return filesize($file); - } catch (Throwable) { - return 0; + if ($size = @filesize($file)) { + return $size; } + + return 0; } /** diff --git a/kirby/src/Filesystem/File.php b/kirby/src/Filesystem/File.php index b56ae0d..fc9beec 100644 --- a/kirby/src/Filesystem/File.php +++ b/kirby/src/Filesystem/File.php @@ -10,7 +10,6 @@ use Kirby\Http\Response; use Kirby\Sane\Sane; use Kirby\Toolkit\Escape; use Kirby\Toolkit\Html; -use Kirby\Toolkit\Properties; use Kirby\Toolkit\V; /** @@ -27,23 +26,21 @@ use Kirby\Toolkit\V; */ class File { - use Properties; - /** * Parent file model * The model object must use the `\Kirby\Filesystem\IsFile` trait */ - protected object|null $model = null; + protected object|null $model; /** * Absolute file path */ - protected string|null $root = null; + protected string|null $root; /** * Absolute file URL */ - protected string|null $url = null; + protected string|null $url; /** * Validation rules to be used for `::match()` @@ -58,6 +55,8 @@ class File * * @param array|string|null $props Properties or deprecated `$root` string * @param string|null $url Deprecated argument, use `$props['url']` instead + * + * @throws \Kirby\Exception\InvalidArgumentException When the model does not use the `Kirby\Filesystem\IsFile` trait */ public function __construct( array|string|null $props = null, @@ -65,7 +64,6 @@ class File ) { // Legacy support for old constructor of // the `Kirby\Image\Image` class - // @todo 4.0.0 remove if (is_array($props) === false) { $props = [ 'root' => $props, @@ -73,11 +71,21 @@ class File ]; } - $this->setProperties($props); + $this->root = $props['root'] ?? null; + $this->url = $props['url'] ?? null; + $this->model = $props['model'] ?? null; + + if ( + $this->model !== null && + method_exists($this->model, 'hasIsFileTrait') !== true + ) { + throw new InvalidArgumentException('The model object must use the "Kirby\Filesystem\IsFile" trait'); + } } /** * Improved `var_dump` output + * @codeCoverageIgnore */ public function __debugInfo(): array { @@ -269,6 +277,15 @@ class File if (is_array($rules['mime'] ?? null) === true) { $mime = $this->mime(); + // the MIME type could not be determined, but matching + // to it was requested explicitly + if ($mime === null) { + throw new Exception([ + 'key' => 'file.mime.missing', + 'data' => ['filename' => $this->filename()] + ]); + } + // determine if any pattern matches the MIME type; // once any pattern matches, `$carry` is `true` and the rest is skipped $matches = array_reduce( @@ -343,19 +360,14 @@ class File /** * Returns the file's last modification time * - * @param string|null $handler date, intl or strftime + * @param 'date'|'intl'|'strftime'|null $handler Custom date handler or `null` + * for the globally configured one */ public function modified( string|IntlDateFormatter|null $format = null, string|null $handler = null ): string|int|false { - $kirby = $this->kirby(); - - return F::modified( - $this->root(), - $format, - $handler ?? $kirby?->option('date.handler', 'date') ?? 'date' - ); + return F::modified($this->root(), $format, $handler); } /** @@ -435,45 +447,6 @@ class File return $this->root ??= $this->model?->root(); } - /** - * Setter for the parent file model, which uses this instance as proxied file asset - * - * @return $this - * - * @throws \Kirby\Exception\InvalidArgumentException When the model does not use the `Kirby\Filesystem\IsFile` trait - */ - protected function setModel(object|null $model = null): static - { - if ($model !== null && method_exists($model, 'hasIsFileTrait') !== true) { - throw new InvalidArgumentException('The model object must use the "Kirby\Filesystem\IsFile" trait'); - } - - $this->model = $model; - return $this; - } - - /** - * Setter for the root - * - * @return $this - */ - protected function setRoot(string|null $root = null): static - { - $this->root = $root; - return $this; - } - - /** - * Setter for the file url - * - * @return $this - */ - protected function setUrl(string|null $url = null): static - { - $this->url = $url; - return $this; - } - /** * Returns the absolute url for the file */ diff --git a/kirby/src/Filesystem/Filename.php b/kirby/src/Filesystem/Filename.php index ba817c1..247f06a 100644 --- a/kirby/src/Filesystem/Filename.php +++ b/kirby/src/Filesystem/Filename.php @@ -2,6 +2,7 @@ namespace Kirby\Filesystem; +use Kirby\Cms\Language; use Kirby\Toolkit\Str; /** @@ -28,44 +29,33 @@ use Kirby\Toolkit\Str; */ class Filename { - /** - * List of all applicable attributes - */ - protected array $attributes; - /** * The sanitized file extension */ protected string $extension; - /** - * The source original filename - */ - protected string $filename; - /** * The sanitized file name */ protected string $name; - /** - * The template for the final name - */ - protected string $template; - /** * Creates a new Filename object + * + * @param string $template for the final name + * @param array $attributes List of all applicable attributes */ - public function __construct(string $filename, string $template, array $attributes = []) - { - $this->filename = $filename; - $this->template = $template; - $this->attributes = $attributes; - $this->extension = $this->sanitizeExtension( + public function __construct( + protected string $filename, + protected string $template, + protected array $attributes = [], + protected string|null $language = null + ) { + $this->name = $this->sanitizeName($filename); + $this->extension = $this->sanitizeExtension( $attributes['format'] ?? pathinfo($filename, PATHINFO_EXTENSION) ); - $this->name = $this->sanitizeName(pathinfo($filename, PATHINFO_FILENAME)); } /** @@ -89,6 +79,7 @@ class Filename 'blur' => $this->blur(), 'bw' => $this->grayscale(), 'q' => $this->quality(), + 'sharpen' => $this->sharpen(), ]; $array = array_filter( @@ -227,24 +218,49 @@ class Filename /** * Sanitizes the file extension. - * The extension will be converted - * to lowercase and `jpeg` will be - * replaced with `jpg` + * It also replaces `jpeg` with `jpg`. */ protected function sanitizeExtension(string $extension): string { - $extension = strtolower($extension); + $extension = F::safeExtension('test.' . $extension); $extension = str_replace('jpeg', 'jpg', $extension); return $extension; } /** - * Sanitizes the name with Kirby's - * Str::slug function + * Sanitizes the file name */ protected function sanitizeName(string $name): string { - return Str::slug($name); + // temporarily store language rules + $rules = Str::$language; + + // add rules for a particular language to `Str` class + if ($this->language !== null) { + Str::$language = [ + ...Str::$language, + ...Language::loadRules($this->language)]; + } + + // sanitize name + $name = F::safeBasename($this->filename); + + // restore language rules + Str::$language = $rules; + + return $name; + } + + /** + * Normalizes the sharpen option value + */ + public function sharpen(): int|false + { + return match ($this->attributes['sharpen'] ?? false) { + false => false, + true => 50, + default => (int)$this->attributes['sharpen'] + }; } /** diff --git a/kirby/src/Filesystem/IsFile.php b/kirby/src/Filesystem/IsFile.php index 6093238..e5b1ab1 100644 --- a/kirby/src/Filesystem/IsFile.php +++ b/kirby/src/Filesystem/IsFile.php @@ -5,7 +5,6 @@ namespace Kirby\Filesystem; use Kirby\Cms\App; use Kirby\Exception\BadMethodCallException; use Kirby\Image\Image; -use Kirby\Toolkit\Properties; /** * Trait for all objects that represent an asset file. @@ -22,8 +21,6 @@ use Kirby\Toolkit\Properties; */ trait IsFile { - use Properties; - /** * File asset object */ @@ -32,19 +29,20 @@ trait IsFile /** * Absolute file path */ - protected string|null $root = null; + protected string|null $root; /** * Absolute file URL */ - protected string|null $url = null; + protected string|null $url; /** * Constructor sets all file properties */ public function __construct(array $props) { - $this->setProperties($props); + $this->root = $props['root'] ?? null; + $this->url = $props['url'] ?? null; } /** @@ -52,7 +50,7 @@ trait IsFile * * @throws \Kirby\Exception\BadMethodCallException */ - public function __call(string $method, array $arguments = []) + public function __call(string $method, array $arguments = []): mixed { // public property access if (isset($this->$method) === true) { @@ -136,28 +134,6 @@ trait IsFile return $this->root; } - /** - * Setter for the root - * - * @return $this - */ - protected function setRoot(string|null $root = null): static - { - $this->root = $root; - return $this; - } - - /** - * Setter for the file url - * - * @return $this - */ - protected function setUrl(string|null $url = null): static - { - $this->url = $url; - return $this; - } - /** * Returns the file type */ diff --git a/kirby/src/Filesystem/Mime.php b/kirby/src/Filesystem/Mime.php index c7a218c..2bb2a03 100644 --- a/kirby/src/Filesystem/Mime.php +++ b/kirby/src/Filesystem/Mime.php @@ -2,6 +2,7 @@ namespace Kirby\Filesystem; +use Kirby\Toolkit\A; use Kirby\Toolkit\Str; use SimpleXMLElement; @@ -30,7 +31,7 @@ class Mime 'aifc' => 'audio/x-aiff', 'aiff' => 'audio/x-aiff', 'avi' => 'video/x-msvideo', - 'avif' => 'image/avif', + 'avif' => 'image/avif', 'bmp' => 'image/bmp', 'css' => 'text/css', 'csv' => ['text/csv', 'text/x-comma-separated-values', 'text/comma-separated-values', 'application/octet-stream'], @@ -119,24 +120,22 @@ class Mime /** * Fixes an invalid MIME type guess for the given file - * - * @param string $file - * @param string $mime - * @param string $extension - * @return string|null */ - public static function fix(string $file, string $mime = null, string $extension = null) - { + public static function fix( + string $file, + string|null $mime = null, + string|null $extension = null + ): string|null { // fixing map $map = [ 'text/html' => [ - 'svg' => ['Kirby\Filesystem\Mime', 'fromSvg'], + 'svg' => [Mime::class, 'fromSvg'], ], 'text/plain' => [ 'css' => 'text/css', 'json' => 'application/json', 'mjs' => 'text/javascript', - 'svg' => ['Kirby\Filesystem\Mime', 'fromSvg'], + 'svg' => [Mime::class, 'fromSvg'], ], 'text/x-asm' => [ 'css' => 'text/css' @@ -167,9 +166,6 @@ class Mime /** * Guesses a MIME type by extension - * - * @param string $extension - * @return string|null */ public static function fromExtension(string $extension): string|null { @@ -179,11 +175,8 @@ class Mime /** * Returns the MIME type of a file - * - * @param string $file - * @return string|false */ - public static function fromFileInfo(string $file) + public static function fromFileInfo(string $file): string|false { if (function_exists('finfo_file') === true && file_exists($file) === true) { $finfo = finfo_open(FILEINFO_MIME_TYPE); @@ -197,13 +190,13 @@ class Mime /** * Returns the MIME type of a file - * - * @param string $file - * @return string|false */ - public static function fromMimeContentType(string $file) + public static function fromMimeContentType(string $file): string|false { - if (function_exists('mime_content_type') === true && file_exists($file) === true) { + if ( + function_exists('mime_content_type') === true && + file_exists($file) === true + ) { return mime_content_type($file); } @@ -212,11 +205,8 @@ class Mime /** * Tries to detect a valid SVG and returns the MIME type accordingly - * - * @param string $file - * @return string|false */ - public static function fromSvg(string $file) + public static function fromSvg(string $file): string|false { if (file_exists($file) === true) { libxml_use_internal_errors(true); @@ -234,10 +224,6 @@ class Mime /** * Tests if a given MIME type is matched by an `Accept` header * pattern; returns true if the MIME type is contained at all - * - * @param string $mime - * @param string $pattern - * @return bool */ public static function isAccepted(string $mime, string $pattern): bool { @@ -256,10 +242,6 @@ class Mime * Tests if a MIME wildcard pattern from an `Accept` header * matches a given type * @since 3.3.0 - * - * @param string $test - * @param string $wildcard - * @return bool */ public static function matches(string $test, string $wildcard): bool { @@ -268,11 +250,8 @@ class Mime /** * Returns the extension for a given MIME type - * - * @param string|null $mime - * @return string|false */ - public static function toExtension(string $mime = null) + public static function toExtension(string|null $mime = null): string|false { foreach (static::$types as $key => $value) { if (is_array($value) === true && in_array($mime, $value) === true) { @@ -289,22 +268,33 @@ class Mime /** * Returns all available extensions for a given MIME type - * - * @param string|null $mime - * @return array */ - public static function toExtensions(string $mime = null): array + public static function toExtensions(string|null $mime = null, bool $matchWildcards = false): array { $extensions = []; + $testMime = fn (string $v) => static::matches($v, $mime); foreach (static::$types as $key => $value) { - if (is_array($value) === true && in_array($mime, $value) === true) { - $extensions[] = $key; - continue; - } - - if ($value === $mime) { - $extensions[] = $key; + if (is_array($value) === true) { + if ($matchWildcards === true) { + if (A::some($value, $testMime)) { + $extensions[] = $key; + } + } else { + if (in_array($mime, $value) === true) { + $extensions[] = $key; + } + } + } else { + if ($matchWildcards === true) { + if ($testMime($value) === true) { + $extensions[] = $key; + } + } else { + if ($value === $mime) { + $extensions[] = $key; + } + } } } @@ -314,8 +304,10 @@ class Mime /** * Returns the MIME type of a file */ - public static function type(string $file, string|null $extension = null): string|null - { + public static function type( + string $file, + string|null $extension = null + ): string|null { // use the standard finfo extension $mime = static::fromFileInfo($file); @@ -338,8 +330,6 @@ class Mime /** * Returns all detectable MIME types - * - * @return array */ public static function types(): array { diff --git a/kirby/src/Form/Field.php b/kirby/src/Form/Field.php index 0e3a02a..114a826 100644 --- a/kirby/src/Form/Field.php +++ b/kirby/src/Form/Field.php @@ -4,6 +4,7 @@ namespace Kirby\Form; use Closure; use Exception; +use Kirby\Cms\App; use Kirby\Exception\InvalidArgumentException; use Kirby\Toolkit\A; use Kirby\Toolkit\Component; @@ -25,42 +26,32 @@ class Field extends Component { /** * An array of all found errors - * - * @var array|null */ - protected $errors; + protected array|null $errors = null; /** * Parent collection with all fields of the current form - * - * @var \Kirby\Form\Fields|null */ - protected $formFields; + protected Fields|null $formFields; /** * Registry for all component mixins - * - * @var array */ - public static $mixins = []; + public static array $mixins = []; /** * Registry for all component types - * - * @var array */ - public static $types = []; + public static array $types = []; /** - * Field constructor - * - * @param string $type - * @param array $attrs - * @param \Kirby\Form\Fields|null $formFields * @throws \Kirby\Exception\InvalidArgumentException */ - public function __construct(string $type, array $attrs = [], ?Fields $formFields = null) - { + public function __construct( + string $type, + array $attrs = [], + Fields|null $formFields = null + ) { if (isset(static::$types[$type]) === false) { throw new InvalidArgumentException([ 'key' => 'field.type.missing', @@ -83,10 +74,8 @@ class Field extends Component /** * Returns field api call - * - * @return mixed */ - public function api() + public function api(): mixed { if ( isset($this->options['api']) === true && @@ -94,15 +83,14 @@ class Field extends Component ) { return $this->options['api']->call($this); } + + return null; } /** * Returns field data - * - * @param bool $default - * @return mixed */ - public function data(bool $default = false) + public function data(bool $default = false): mixed { $save = $this->options['save'] ?? true; @@ -125,8 +113,6 @@ class Field extends Component /** * Default props and computed of the field - * - * @return array */ public static function defaults(): array { @@ -141,7 +127,7 @@ class Field extends Component /** * Sets the focus on this field when the form loads. Only the first field with this label gets */ - 'autofocus' => function (bool $autofocus = null): bool { + 'autofocus' => function (bool|null $autofocus = null): bool { return $autofocus ?? false; }, /** @@ -159,7 +145,7 @@ class Field extends Component /** * If `true`, the field is no longer editable and will not be saved */ - 'disabled' => function (bool $disabled = null): bool { + 'disabled' => function (bool|null $disabled = null): bool { return $disabled ?? false; }, /** @@ -171,7 +157,7 @@ class Field extends Component /** * Optional icon that will be shown at the end of the field */ - 'icon' => function (string $icon = null) { + 'icon' => function (string|null $icon = null) { return $icon; }, /** @@ -189,7 +175,7 @@ class Field extends Component /** * If `true`, the field has to be filled in correctly to be saved. */ - 'required' => function (bool $required = null): bool { + 'required' => function (bool|null $required = null): bool { return $required ?? false; }, /** @@ -264,15 +250,43 @@ class Field extends Component } /** - * Creates a new field instance - * - * @param string $type - * @param array $attrs - * @param Fields|null $formFields - * @return static + * Returns optional dialog routes for the field */ - public static function factory(string $type, array $attrs = [], ?Fields $formFields = null) + public function dialogs(): array { + if ( + isset($this->options['dialogs']) === true && + $this->options['dialogs'] instanceof Closure + ) { + return $this->options['dialogs']->call($this); + } + + return []; + } + + /** + * Returns optional drawer routes for the field + */ + public function drawers(): array + { + if ( + isset($this->options['drawers']) === true && + $this->options['drawers'] instanceof Closure + ) { + return $this->options['drawers']->call($this); + } + + return []; + } + + /** + * Creates a new field instance + */ + public static function factory( + string $type, + array $attrs = [], + Fields|null $formFields = null + ): static|FieldClass { $field = static::$types[$type] ?? null; if (is_string($field) && class_exists($field) === true) { @@ -285,18 +299,14 @@ class Field extends Component /** * Parent collection with all fields of the current form - * - * @return \Kirby\Form\Fields|null */ - public function formFields(): ?Fields + public function formFields(): Fields|null { return $this->formFields; } /** * Validates when run for the first time and returns any errors - * - * @return array */ public function errors(): array { @@ -309,29 +319,31 @@ class Field extends Component /** * Checks if the field is empty - * - * @param mixed ...$args - * @return bool */ - public function isEmpty(...$args): bool + public function isEmpty(mixed ...$args): bool { - if (count($args) === 0) { - $value = $this->value(); - } else { - $value = $args[0]; - } + $value = match (count($args)) { + 0 => $this->value(), + default => $args[0] + }; - if (isset($this->options['isEmpty']) === true) { - return $this->options['isEmpty']->call($this, $value); + if ($empty = $this->options['isEmpty'] ?? null) { + return $empty->call($this, $value); } return in_array($value, [null, '', []], true); } + /** + * Checks if the field is hidden + */ + public function isHidden(): bool + { + return ($this->options['hidden'] ?? false) === true; + } + /** * Checks if the field is invalid - * - * @return bool */ public function isInvalid(): bool { @@ -340,8 +352,6 @@ class Field extends Component /** * Checks if the field is required - * - * @return bool */ public function isRequired(): bool { @@ -350,8 +360,6 @@ class Field extends Component /** * Checks if the field is valid - * - * @return bool */ public function isValid(): bool { @@ -360,20 +368,16 @@ class Field extends Component /** * Returns the Kirby instance - * - * @return \Kirby\Cms\App */ - public function kirby() + public function kirby(): App { return $this->model()->kirby(); } /** * Returns the parent model - * - * @return mixed */ - public function model() + public function model(): mixed { return $this->model; } @@ -385,31 +389,33 @@ class Field extends Component * - The field is required * - The field is currently empty * - The field is not currently inactive because of a `when` rule - * - * @return bool */ protected function needsValue(): bool { // check simple conditions first - if ($this->save() === false || $this->isRequired() === false || $this->isEmpty() === false) { + if ( + $this->save() === false || + $this->isRequired() === false || + $this->isEmpty() === false + ) { return false; } // check the data of the relevant fields if there is a `when` option - if (empty($this->when) === false && is_array($this->when) === true) { - $formFields = $this->formFields(); + if ( + empty($this->when) === false && + is_array($this->when) === true && + $formFields = $this->formFields() + ) { + foreach ($this->when as $field => $value) { + $field = $formFields->get($field); + $inputValue = $field?->value() ?? ''; - if ($formFields !== null) { - foreach ($this->when as $field => $value) { - $field = $formFields->get($field); - $inputValue = $field?->value() ?? ''; - - // if the input data doesn't match the requested `when` value, - // that means that this field is not required and can be saved - // (*all* `when` conditions must be met for this field to be required) - if ($inputValue !== $value) { - return false; - } + // if the input data doesn't match the requested `when` value, + // that means that this field is not required and can be saved + // (*all* `when` conditions must be met for this field to be required) + if ($inputValue !== $value) { + return false; } } } @@ -420,8 +426,6 @@ class Field extends Component /** * Checks if the field is saveable - * - * @return bool */ public function save(): bool { @@ -430,8 +434,6 @@ class Field extends Component /** * Converts the field to a plain array - * - * @return array */ public function toArray(): array { @@ -439,6 +441,7 @@ class Field extends Component unset($array['model']); + $array['hidden'] = $this->isHidden(); $array['saveable'] = $this->save(); $array['signature'] = md5(json_encode($array)); @@ -452,8 +455,6 @@ class Field extends Component /** * Runs the validations defined for the field - * - * @return void */ protected function validate(): void { @@ -501,10 +502,8 @@ class Field extends Component /** * Returns the value of the field if saveable * otherwise it returns null - * - * @return mixed */ - public function value() + public function value(): mixed { return $this->save() ? $this->value : null; } diff --git a/kirby/src/Form/Field/BlocksField.php b/kirby/src/Form/Field/BlocksField.php index 29b4993..2d66a6b 100644 --- a/kirby/src/Form/Field/BlocksField.php +++ b/kirby/src/Form/Field/BlocksField.php @@ -5,7 +5,9 @@ namespace Kirby\Form\Field; use Kirby\Cms\App; use Kirby\Cms\Block; use Kirby\Cms\Blocks as BlocksCollection; +use Kirby\Cms\Fieldset; use Kirby\Cms\Fieldsets; +use Kirby\Cms\ModelWithContent; use Kirby\Exception\InvalidArgumentException; use Kirby\Exception\NotFoundException; use Kirby\Form\FieldClass; @@ -22,15 +24,17 @@ class BlocksField extends FieldClass use Max; use Min; - protected $blocks; - protected $fieldsets; - protected $group; - protected $pretty; - protected $value = []; + protected Fieldsets $fieldsets; + protected string|null $group; + protected bool $pretty; + protected mixed $value = []; public function __construct(array $params = []) { - $this->setFieldsets($params['fieldsets'] ?? null, $params['model'] ?? App::instance()->site()); + $this->setFieldsets( + $params['fieldsets'] ?? null, + $params['model'] ?? App::instance()->site() + ); parent::__construct($params); @@ -41,8 +45,10 @@ class BlocksField extends FieldClass $this->setPretty($params['pretty'] ?? false); } - public function blocksToValues($blocks, $to = 'values'): array - { + public function blocksToValues( + array $blocks, + string $to = 'values' + ): array { $result = []; $fields = []; @@ -54,53 +60,58 @@ class BlocksField extends FieldClass $fields[$type] ??= $this->fields($block['type']); // overwrite the block content with form values - $block['content'] = $this->form($fields[$type], $block['content'])->$to(); + $block['content'] = $this->form( + $fields[$type], + $block['content'] + )->$to(); - $result[] = $block; + // create id if not exists + $block['id'] ??= Str::uuid(); } catch (Throwable) { - $result[] = $block; - // skip invalid blocks - continue; + } finally { + $result[] = $block; } } return $result; } - public function fields(string $type) + public function fields(string $type): array { return $this->fieldset($type)->fields(); } - public function fieldset(string $type) + public function fieldset(string $type): Fieldset { if ($fieldset = $this->fieldsets->find($type)) { return $fieldset; } - throw new NotFoundException('The fieldset ' . $type . ' could not be found'); + throw new NotFoundException( + 'The fieldset ' . $type . ' could not be found' + ); } - public function fieldsets() + public function fieldsets(): Fieldsets { return $this->fieldsets; } public function fieldsetGroups(): array|null { - $fieldsetGroups = $this->fieldsets()->groups(); - return empty($fieldsetGroups) === true ? null : $fieldsetGroups; + $groups = $this->fieldsets()->groups(); + return empty($groups) === true ? null : $groups; } - public function fill($value = null) + public function fill(mixed $value = null): void { $value = BlocksCollection::parse($value); - $blocks = BlocksCollection::factory($value); - $this->value = $this->blocksToValues($blocks->toArray()); + $blocks = BlocksCollection::factory($value)->toArray(); + $this->value = $this->blocksToValues($blocks); } - public function form(array $fields, array $input = []) + public function form(array $fields, array $input = []): Form { return new Form([ 'fields' => $fields, @@ -125,6 +136,30 @@ class BlocksField extends FieldClass return $this->pretty; } + /** + * Paste action for blocks: + * - generates new uuids for the blocks + * - filters only supported fieldsets + * - applies max limit if defined + */ + public function pasteBlocks(array $blocks): array + { + $blocks = $this->blocksToValues($blocks); + + foreach ($blocks as $index => &$block) { + $block['id'] = Str::uuid(); + + // remove the block if it's not available + try { + $this->fieldset($block['type']); + } catch (Throwable) { + unset($blocks[$index]); + } + } + + return array_values($blocks); + } + public function props(): array { return [ @@ -144,23 +179,25 @@ class BlocksField extends FieldClass return [ [ 'pattern' => 'uuid', - 'action' => fn () => ['uuid' => Str::uuid()] + 'action' => fn (): array => ['uuid' => Str::uuid()] ], [ 'pattern' => 'paste', 'method' => 'POST', - 'action' => function () use ($field) { + 'action' => function () use ($field): array { $request = App::instance()->request(); + $value = BlocksCollection::parse($request->get('html')); + $blocks = BlocksCollection::factory($value); - $value = BlocksCollection::parse($request->get('html')); - $blocks = BlocksCollection::factory($value); - return $field->blocksToValues($blocks->toArray()); + return $field->pasteBlocks($blocks->toArray()); } ], [ 'pattern' => 'fieldsets/(:any)', 'method' => 'GET', - 'action' => function ($fieldsetType) use ($field) { + 'action' => function ( + string $fieldsetType + ) use ($field): array { $fields = $field->fields($fieldsetType); $defaults = $field->form($fields, [])->data(true); $content = $field->form($fields, $defaults)->values(); @@ -174,22 +211,33 @@ class BlocksField extends FieldClass [ 'pattern' => 'fieldsets/(:any)/fields/(:any)/(:all?)', 'method' => 'ALL', - 'action' => function (string $fieldsetType, string $fieldName, string $path = null) use ($field) { + 'action' => function ( + string $fieldsetType, + string $fieldName, + string|null $path = null + ) use ($field) { $fields = $field->fields($fieldsetType); $field = $field->form($fields)->field($fieldName); $fieldApi = $this->clone([ 'routes' => $field->api(), - 'data' => array_merge($this->data(), ['field' => $field]) + 'data' => array_merge( + $this->data(), + ['field' => $field] + ) ]); - return $fieldApi->call($path, $this->requestMethod(), $this->requestData()); + return $fieldApi->call( + $path, + $this->requestMethod(), + $this->requestData() + ); } ], ]; } - public function store($value) + public function store(mixed $value): mixed { $blocks = $this->blocksToValues((array)$value, 'content'); @@ -202,7 +250,7 @@ class BlocksField extends FieldClass return $this->valueToJson($blocks, $this->pretty()); } - protected function setDefault($default = null) + protected function setDefault(mixed $default = null): void { // set id for blocks if not exists if (is_array($default) === true) { @@ -214,23 +262,26 @@ class BlocksField extends FieldClass parent::setDefault($default); } - protected function setFieldsets($fieldsets, $model) - { + protected function setFieldsets( + string|array|null $fieldsets, + ModelWithContent $model + ): void { if (is_string($fieldsets) === true) { $fieldsets = []; } - $this->fieldsets = Fieldsets::factory($fieldsets, [ - 'parent' => $model - ]); + $this->fieldsets = Fieldsets::factory( + $fieldsets, + ['parent' => $model] + ); } - protected function setGroup(string $group = null) + protected function setGroup(string|null $group = null): void { $this->group = $group; } - protected function setPretty(bool $pretty = false) + protected function setPretty(bool $pretty = false): void { $this->pretty = $pretty; } @@ -262,21 +313,22 @@ class BlocksField extends FieldClass foreach ($value as $block) { $index++; - $blockType = $block['type']; + $type = $block['type']; try { - $fieldset = $this->fieldset($blockType); - $blockFields = $fields[$blockType] ?? $fieldset->fields() ?? []; + $fieldset = $this->fieldset($type); + $blockFields = $fields[$type] ?? $fieldset->fields() ?? []; } catch (Throwable) { // skip invalid blocks continue; } // store the fields for the next round - $fields[$blockType] = $blockFields; + $fields[$type] = $blockFields; // overwrite the content with the serialized form - foreach ($this->form($blockFields, $block['content'])->fields() as $field) { + $form = $this->form($blockFields, $block['content']); + foreach ($form->fields() as $field) { $errors = $field->errors(); // rough first validation diff --git a/kirby/src/Form/Field/LayoutField.php b/kirby/src/Form/Field/LayoutField.php index 7147234..d92c352 100644 --- a/kirby/src/Form/Field/LayoutField.php +++ b/kirby/src/Form/Field/LayoutField.php @@ -14,19 +14,21 @@ use Throwable; class LayoutField extends BlocksField { - protected $layouts; - protected $settings; + protected array|null $layouts; + protected array|null $selector; + protected Fieldset|null $settings; public function __construct(array $params) { $this->setModel($params['model'] ?? App::instance()->site()); $this->setLayouts($params['layouts'] ?? ['1/1']); + $this->setSelector($params['selector'] ?? null); $this->setSettings($params['settings'] ?? null); parent::__construct($params); } - public function fill($value = null) + public function fill(mixed $value = null): void { $value = $this->valueFromJson($value); $layouts = Layouts::factory($value, ['parent' => $this->model])->toArray(); @@ -44,7 +46,7 @@ class LayoutField extends BlocksField $this->value = $layouts; } - public function attrsForm(array $input = []) + public function attrsForm(array $input = []): Form { $settings = $this->settings(); @@ -61,13 +63,71 @@ class LayoutField extends BlocksField return $this->layouts; } + /** + * Creates form values for each layout + */ + public function layoutsToValues(array $layouts): array + { + foreach ($layouts as &$layout) { + $layout['id'] ??= Str::uuid(); + $layout['columns'] ??= []; + + array_walk($layout['columns'], function (&$column) { + $column['id'] ??= Str::uuid(); + $column['blocks'] = $this->blocksToValues($column['blocks'] ?? []); + }); + } + + return $layouts; + } + + /** + * Paste action for layouts: + * - generates new uuids for layout, column and blocks + * - filters only supported layouts + * - filters only supported fieldsets + */ + public function pasteLayouts(array $layouts): array + { + $layouts = $this->layoutsToValues($layouts); + + foreach ($layouts as $layoutIndex => &$layout) { + $layout['id'] = Str::uuid(); + + // remove the row if layout not available for the pasted layout field + $columns = array_column($layout['columns'], 'width'); + if (in_array($columns, $this->layouts()) === false) { + unset($layouts[$layoutIndex]); + continue; + } + + array_walk($layout['columns'], function (&$column) { + $column['id'] = Str::uuid(); + + array_walk($column['blocks'], function (&$block, $index) use ($column) { + $block['id'] = Str::uuid(); + + // remove the block if it's not available + try { + $this->fieldset($block['type']); + } catch (Throwable) { + unset($column['blocks'][$index]); + } + }); + }); + } + + return $layouts; + } + public function props(): array { $settings = $this->settings(); return array_merge(parent::props(), [ - 'settings' => $settings?->toArray(), - 'layouts' => $this->layouts() + 'layouts' => $this->layouts(), + 'selector' => $this->selector(), + 'settings' => $settings?->toArray() ]); } @@ -75,13 +135,15 @@ class LayoutField extends BlocksField { $field = $this; $routes = parent::routes(); + $routes[] = [ 'pattern' => 'layout', 'method' => 'POST', - 'action' => function () use ($field) { + 'action' => function () use ($field): array { $request = App::instance()->request(); - $defaults = $field->attrsForm([])->data(true); + $input = $request->get('attrs') ?? []; + $defaults = $field->attrsForm($input)->data(true); $attrs = $field->attrsForm($defaults)->values(); $columns = $request->get('columns') ?? ['1/1']; @@ -96,26 +158,53 @@ class LayoutField extends BlocksField }, ]; + $routes[] = [ + 'pattern' => 'layout/paste', + 'method' => 'POST', + 'action' => function () use ($field): array { + $request = App::instance()->request(); + $value = Layouts::parse($request->get('json')); + $layouts = Layouts::factory($value); + + return $field->pasteLayouts($layouts->toArray()); + } + ]; + $routes[] = [ 'pattern' => 'fields/(:any)/(:all?)', 'method' => 'ALL', - 'action' => function (string $fieldName, string $path = null) use ($field) { + 'action' => function ( + string $fieldName, + string|null $path = null + ) use ($field): array { $form = $field->attrsForm(); $field = $form->field($fieldName); $fieldApi = $this->clone([ 'routes' => $field->api(), - 'data' => array_merge($this->data(), ['field' => $field]) + 'data' => array_merge( + $this->data(), + ['field' => $field] + ) ]); - return $fieldApi->call($path, $this->requestMethod(), $this->requestData()); + return $fieldApi->call( + $path, + $this->requestMethod(), + $this->requestData() + ); } ]; return $routes; } - protected function setDefault($default = null) + public function selector(): array|null + { + return $this->selector; + } + + protected function setDefault(mixed $default = null): void { // set id for layouts, columns and blocks within layout if not exists if (is_array($default) === true) { @@ -141,7 +230,7 @@ class LayoutField extends BlocksField parent::setDefault($default); } - protected function setLayouts(array $layouts = []) + protected function setLayouts(array $layouts = []): void { $this->layouts = array_map( fn ($layout) => Str::split($layout), @@ -149,7 +238,15 @@ class LayoutField extends BlocksField ); } - protected function setSettings($settings = null) + /** + * Layout selector's styles such as size (`small`, `medium`, `large` or `huge`) and columns + */ + protected function setSelector(array|null $selector = null): void + { + $this->selector = $selector; + } + + protected function setSettings(array|string|null $settings = null): void { if (empty($settings) === true) { $this->settings = null; @@ -165,12 +262,12 @@ class LayoutField extends BlocksField $this->settings = Fieldset::factory($settings); } - public function settings() + public function settings(): Fieldset|null { return $this->settings; } - public function store($value) + public function store(mixed $value): mixed { $value = Layouts::factory($value, ['parent' => $this->model])->toArray(); @@ -204,7 +301,9 @@ class LayoutField extends BlocksField $layoutIndex++; // validate settings form - foreach ($this->attrsForm($layout['attrs'] ?? [])->fields() as $field) { + $form = $this->attrsForm($layout['attrs'] ?? []); + + foreach ($form->fields() as $field) { $errors = $field->errors(); if (empty($errors) === false) { @@ -237,7 +336,9 @@ class LayoutField extends BlocksField $fields[$blockType] = $blockFields; // overwrite the content with the serialized form - foreach ($this->form($blockFields, $block['content'])->fields() as $field) { + $form = $this->form($blockFields, $block['content']); + + foreach ($form->fields() as $field) { $errors = $field->errors(); // rough first validation diff --git a/kirby/src/Form/FieldClass.php b/kirby/src/Form/FieldClass.php index 6aa173b..613122c 100644 --- a/kirby/src/Form/FieldClass.php +++ b/kirby/src/Form/FieldClass.php @@ -27,117 +27,27 @@ abstract class FieldClass { use HasSiblings; - /** - * @var string|null - */ - protected $after; - - /** - * @var bool - */ - protected $autofocus; - - /** - * @var string|null - */ - protected $before; - - /** - * @var mixed - */ - protected $default; - - /** - * @var bool - */ - protected $disabled; - - /** - * @var string|null - */ - protected $help; - - /** - * @var string|null - */ - protected $icon; - - /** - * @var string|null - */ - protected $label; - - /** - * @var \Kirby\Cms\ModelWithContent - */ - protected $model; - - /** - * @var string - */ - protected $name; - - /** - * @var array - */ - protected $params; - - /** - * @var string|null - */ - protected $placeholder; - - /** - * @var bool - */ - protected $required; - - /** - * @var \Kirby\Form\Fields - */ - protected $siblings; - - /** - * @var bool - */ - protected $translate; - - /** - * @var mixed - */ - protected $value; - - /** - * @var array|null - */ - protected $when; - - /** - * @var string|null - */ - protected $width; - - /** - * @param string $param - * @param array $args - * @return mixed - */ - public function __call(string $param, array $args) - { - if (isset($this->$param) === true) { - return $this->$param; - } - - return $this->params[$param] ?? null; - } - - /** - * @param array $params - */ - public function __construct(array $params = []) - { - $this->params = $params; + protected string|null $after; + protected bool $autofocus; + protected string|null $before; + protected mixed $default; + protected bool $disabled; + protected string|null $help; + protected string|null $icon; + protected string|null $label; + protected ModelWithContent $model; + protected string|null $name; + protected string|null $placeholder; + protected bool $required; + protected Fields $siblings; + protected bool $translate; + protected mixed $value = null; + protected array|null $when; + protected string|null $width; + public function __construct( + protected array $params = [] + ) { $this->setAfter($params['after'] ?? null); $this->setAutofocus($params['autofocus'] ?? false); $this->setBefore($params['before'] ?? null); @@ -160,33 +70,30 @@ abstract class FieldClass } } - /** - * @return string|null - */ + public function __call(string $param, array $args): mixed + { + if (isset($this->$param) === true) { + return $this->$param; + } + + return $this->params[$param] ?? null; + } + public function after(): string|null { return $this->stringTemplate($this->after); } - /** - * @return array - */ public function api(): array { return $this->routes(); } - /** - * @return bool - */ public function autofocus(): bool { return $this->autofocus; } - /** - * @return string|null - */ public function before(): string|null { return $this->stringTemplate($this->before); @@ -199,11 +106,8 @@ abstract class FieldClass * Returns the field data * in a format to be stored * in Kirby's content fields - * - * @param bool $default - * @return mixed */ - public function data(bool $default = false) + public function data(bool $default = false): mixed { return $this->store($this->value($default)); } @@ -211,10 +115,8 @@ abstract class FieldClass /** * Returns the default value for the field, * which will be used when a page/file/user is created - * - * @return mixed */ - public function default() + public function default(): mixed { if (is_string($this->default) === false) { return $this->default; @@ -223,21 +125,33 @@ abstract class FieldClass return $this->stringTemplate($this->default); } + /** + * Returns optional dialog routes for the field + */ + public function dialogs(): array + { + return []; + } + /** * If `true`, the field is no longer editable and will not be saved - * - * @return bool */ public function disabled(): bool { return $this->disabled; } + /** + * Returns optional drawer routes for the field + */ + public function drawers(): array + { + return []; + } + /** * Runs all validations and returns an array of * error messages - * - * @return array */ public function errors(): array { @@ -246,19 +160,14 @@ abstract class FieldClass /** * Setter for the value - * - * @param mixed $value - * @return void */ - public function fill($value = null) + public function fill(mixed $value = null): void { $this->value = $value; } /** * Optional help text below the field - * - * @return string|null */ public function help(): string|null { @@ -271,79 +180,57 @@ abstract class FieldClass return null; } - /** - * @param string|array|null $param - * @return string|null - */ - protected function i18n($param = null): string|null + protected function i18n(string|array|null $param = null): string|null { return empty($param) === false ? I18n::translate($param, $param) : null; } /** * Optional icon that will be shown at the end of the field - * - * @return string|null */ public function icon(): string|null { return $this->icon; } - /** - * @return string - */ public function id(): string { return $this->name(); } - /** - * @return bool - */ public function isDisabled(): bool { return $this->disabled; } - /** - * @return bool - */ public function isEmpty(): bool { return $this->isEmptyValue($this->value()); } - /** - * @param mixed $value - * @return bool - */ - public function isEmptyValue($value = null): bool + public function isEmptyValue(mixed $value = null): bool { return in_array($value, [null, '', []], true); } + public function isHidden(): bool + { + return false; + } + /** * Checks if the field is invalid - * - * @return bool */ public function isInvalid(): bool { return $this->isValid() === false; } - /** - * @return bool - */ public function isRequired(): bool { return $this->required; } - /** - * @return bool - */ public function isSaveable(): bool { return true; @@ -351,8 +238,6 @@ abstract class FieldClass /** * Checks if the field is valid - * - * @return bool */ public function isValid(): bool { @@ -361,38 +246,32 @@ abstract class FieldClass /** * Returns the Kirby instance - * - * @return \Kirby\Cms\App */ - public function kirby() + public function kirby(): App { return $this->model->kirby(); } /** * The field label can be set as string or associative array with translations - * - * @return string */ public function label(): string { - return $this->stringTemplate($this->label ?? Str::ucfirst($this->name())); + return $this->stringTemplate( + $this->label ?? Str::ucfirst($this->name()) + ); } /** * Returns the parent model - * - * @return mixed */ - public function model() + public function model(): ModelWithContent { return $this->model; } /** * Returns the field name - * - * @return string */ public function name(): string { @@ -406,8 +285,6 @@ abstract class FieldClass * - The field is required * - The field is currently empty * - The field is not currently inactive because of a `when` rule - * - * @return bool */ protected function needsValue(): bool { @@ -421,20 +298,20 @@ abstract class FieldClass } // check the data of the relevant fields if there is a `when` option - if (empty($this->when) === false && is_array($this->when) === true) { - $formFields = $this->siblings(); + if ( + empty($this->when) === false && + is_array($this->when) === true && + $formFields = $this->siblings() + ) { + foreach ($this->when as $field => $value) { + $field = $formFields->get($field); + $inputValue = $field?->value() ?? ''; - if ($formFields !== null) { - foreach ($this->when as $field => $value) { - $field = $formFields->get($field); - $inputValue = $field?->value() ?? ''; - - // if the input data doesn't match the requested `when` value, - // that means that this field is not required and can be saved - // (*all* `when` conditions must be met for this field to be required) - if ($inputValue !== $value) { - return false; - } + // if the input data doesn't match the requested `when` value, + // that means that this field is not required and can be saved + // (*all* `when` conditions must be met for this field to be required) + if ($inputValue !== $value) { + return false; } } } @@ -445,8 +322,6 @@ abstract class FieldClass /** * Returns all original params for the field - * - * @return array */ public function params(): array { @@ -455,8 +330,6 @@ abstract class FieldClass /** * Optional placeholder value that will be shown when the field is empty - * - * @return string|null */ public function placeholder(): string|null { @@ -466,8 +339,6 @@ abstract class FieldClass /** * Define the props that will be sent to * the Vue component - * - * @return array */ public function props(): array { @@ -478,6 +349,7 @@ abstract class FieldClass 'default' => $this->default(), 'disabled' => $this->isDisabled(), 'help' => $this->help(), + 'hidden' => $this->isHidden(), 'icon' => $this->icon(), 'label' => $this->label(), 'name' => $this->name(), @@ -493,8 +365,6 @@ abstract class FieldClass /** * If `true`, the field has to be filled in correctly to be saved. - * - * @return bool */ public function required(): bool { @@ -503,8 +373,6 @@ abstract class FieldClass /** * Routes for the field API - * - * @return array */ public function routes(): array { @@ -514,176 +382,108 @@ abstract class FieldClass /** * @deprecated 3.5.0 * @todo remove when the general field class setup has been refactored - * @return bool */ - public function save() + public function save(): bool { return $this->isSaveable(); } - /** - * @param array|string|null $after - * @return void - */ - protected function setAfter($after = null) + protected function setAfter(array|string|null $after = null): void { $this->after = $this->i18n($after); } - /** - * @param bool $autofocus - * @return void - */ - protected function setAutofocus(bool $autofocus = false) + protected function setAutofocus(bool $autofocus = false): void { $this->autofocus = $autofocus; } - /** - * @param array|string|null $before - * @return void - */ - protected function setBefore($before = null) + protected function setBefore(array|string|null $before = null): void { $this->before = $this->i18n($before); } - /** - * @param mixed $default - * @return void - */ - protected function setDefault($default = null) + protected function setDefault(mixed $default = null): void { $this->default = $default; } - /** - * @param bool $disabled - * @return void - */ - protected function setDisabled(bool $disabled = false) + protected function setDisabled(bool $disabled = false): void { $this->disabled = $disabled; } - /** - * @param array|string|null $help - * @return void - */ - protected function setHelp($help = null) + protected function setHelp(array|string|null $help = null): void { $this->help = $this->i18n($help); } - /** - * @param string|null $icon - * @return void - */ - protected function setIcon(string|null $icon = null) + protected function setIcon(string|null $icon = null): void { $this->icon = $icon; } - /** - * @param array|string|null $label - * @return void - */ - protected function setLabel($label = null) + protected function setLabel(array|string|null $label = null): void { $this->label = $this->i18n($label); } - /** - * @param \Kirby\Cms\ModelWithContent $model - * @return void - */ - protected function setModel(ModelWithContent $model) + protected function setModel(ModelWithContent $model): void { $this->model = $model; } - /** - * @param string|null $name - * @return void - */ - protected function setName(string $name = null) + protected function setName(string|null $name = null): void { $this->name = $name; } - /** - * @param array|string|null $placeholder - * @return void - */ - protected function setPlaceholder($placeholder = null) + protected function setPlaceholder(array|string|null $placeholder = null): void { $this->placeholder = $this->i18n($placeholder); } - /** - * @param bool $required - * @return void - */ - protected function setRequired(bool $required = false) + protected function setRequired(bool $required = false): void { $this->required = $required; } - /** - * @param \Kirby\Form\Fields|null $siblings - * @return void - */ - protected function setSiblings(?Fields $siblings = null) + protected function setSiblings(Fields|null $siblings = null): void { $this->siblings = $siblings ?? new Fields([$this]); } - /** - * @param bool $translate - * @return void - */ - protected function setTranslate(bool $translate = true) + protected function setTranslate(bool $translate = true): void { $this->translate = $translate; } /** * Setter for the when condition - * - * @param mixed $when - * @return void */ - protected function setWhen($when = null) + protected function setWhen(array|null $when = null): void { $this->when = $when; } /** * Setter for the field width - * - * @param string|null $width - * @return void */ - protected function setWidth(string $width = null) + protected function setWidth(string|null $width = null): void { $this->width = $width; } /** * Returns all sibling fields - * - * @return \Kirby\Form\Fields */ - protected function siblingsCollection() + protected function siblingsCollection(): Fields { return $this->siblings; } /** * Parses a string template in the given value - * - * @param string|null $string - * @return string|null */ protected function stringTemplate(string|null $string = null): string|null { @@ -697,19 +497,14 @@ abstract class FieldClass /** * Converts the given value to a value * that can be stored in the text file - * - * @param mixed $value - * @return mixed */ - public function store($value) + public function store(mixed $value): mixed { return $value; } /** * Should the field be translatable? - * - * @return bool */ public function translate(): bool { @@ -718,8 +513,6 @@ abstract class FieldClass /** * Converts the field to a plain array - * - * @return array */ public function toArray(): array { @@ -733,8 +526,6 @@ abstract class FieldClass /** * Returns the field type - * - * @return string */ public function type(): string { @@ -743,8 +534,6 @@ abstract class FieldClass /** * Runs the validations defined for the field - * - * @return array */ protected function validate(): array { @@ -782,8 +571,7 @@ abstract class FieldClass /** * Defines all validation rules - * - * @return array + * @codeCoverageIgnore */ protected function validations(): array { @@ -793,10 +581,8 @@ abstract class FieldClass /** * Returns the value of the field if saveable * otherwise it returns null - * - * @return mixed */ - public function value(bool $default = false) + public function value(bool $default = false): mixed { if ($this->isSaveable() === false) { return null; @@ -809,11 +595,7 @@ abstract class FieldClass return $this->value; } - /** - * @param mixed $value - * @return array - */ - protected function valueFromJson($value): array + protected function valueFromJson(mixed $value): array { try { return Data::decode($value, 'json'); @@ -822,22 +604,15 @@ abstract class FieldClass } } - /** - * @param mixed $value - * @return array - */ - protected function valueFromYaml($value): array + protected function valueFromYaml(mixed $value): array { return Data::decode($value, 'yaml'); } - /** - * @param array|null $value - * @param bool $pretty - * @return string - */ - protected function valueToJson(array $value = null, bool $pretty = false): string - { + protected function valueToJson( + array|null $value = null, + bool $pretty = false + ): string { $constants = JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE; if ($pretty === true) { @@ -847,19 +622,13 @@ abstract class FieldClass return json_encode($value, $constants); } - /** - * @param array|null $value - * @return string - */ - protected function valueToYaml(array $value = null): string + protected function valueToYaml(array|null $value = null): string { return Data::encode($value, 'yaml'); } /** * Conditions when the field will be shown - * - * @return array|null */ public function when(): array|null { @@ -869,8 +638,6 @@ abstract class FieldClass /** * Returns the width of the field in * the Panel grid - * - * @return string */ public function width(): string { diff --git a/kirby/src/Form/Fields.php b/kirby/src/Form/Fields.php index 9abd199..95c8fec 100644 --- a/kirby/src/Form/Fields.php +++ b/kirby/src/Form/Fields.php @@ -21,9 +21,7 @@ class Fields extends Collection * This takes care of validation and of setting * the collection prop on each object correctly. * - * @param string $name * @param object|array $field - * @return void */ public function __set(string $name, $field): void { @@ -40,11 +38,8 @@ class Fields extends Collection * Converts the fields collection to an * array and also does that for every * included field. - * - * @param \Closure|null $map - * @return array */ - public function toArray(Closure $map = null): array + public function toArray(Closure|null $map = null): array { $array = []; diff --git a/kirby/src/Form/Form.php b/kirby/src/Form/Form.php index 9ee3059..adec4f3 100644 --- a/kirby/src/Form/Form.php +++ b/kirby/src/Form/Form.php @@ -4,9 +4,11 @@ namespace Kirby\Form; use Closure; use Kirby\Cms\App; -use Kirby\Cms\Model; +use Kirby\Cms\File; +use Kirby\Cms\ModelWithContent; use Kirby\Data\Data; use Kirby\Exception\NotFoundException; +use Kirby\Toolkit\A; use Kirby\Toolkit\Str; use Throwable; @@ -26,29 +28,21 @@ class Form { /** * An array of all found errors - * - * @var array|null */ - protected $errors; + protected array|null $errors = null; /** * Fields in the form - * - * @var \Kirby\Form\Fields|null */ - protected $fields; + protected Fields|null $fields; /** * All values of form - * - * @var array */ - protected $values = []; + protected array $values = []; /** * Form constructor - * - * @param array $props */ public function __construct(array $props) { @@ -80,15 +74,12 @@ class Form // inject the name $props['name'] = $name = strtolower($name); - // check if the field is disabled - $disabled = $props['disabled'] ?? false; - + // check if the field is disabled and // overwrite the field value if not set - if ($disabled === true) { - $props['value'] = $values[$name] ?? null; - } else { - $props['value'] = $input[$name] ?? $values[$name] ?? null; - } + $props['value'] = match ($props['disabled'] ?? false) { + true => $values[$name] ?? null, + default => $input[$name] ?? $values[$name] ?? null + }; try { $field = Field::factory($props['type'], $props, $this->fields); @@ -117,8 +108,6 @@ class Form /** * Returns the data required to write to the content file * Doesn't include default and null values - * - * @return array */ public function content(): array { @@ -129,8 +118,6 @@ class Form * Returns data for all fields in the form * * @param false $defaults - * @param bool $includeNulls - * @return array */ public function data($defaults = false, bool $includeNulls = true): array { @@ -153,8 +140,6 @@ class Form /** * An array of all found errors - * - * @return array */ public function errors(): array { @@ -178,17 +163,16 @@ class Form /** * Shows the error with the field - * - * @param \Throwable $exception - * @param array $props - * @return \Kirby\Form\Field */ - public static function exceptionField(Throwable $exception, array $props = []) - { + public static function exceptionField( + Throwable $exception, + array $props = [] + ): Field { $message = $exception->getMessage(); if (App::instance()->option('debug') === true) { - $message .= ' in file: ' . $exception->getFile() . ' line: ' . $exception->getLine(); + $message .= ' in file: ' . $exception->getFile(); + $message .= ' line: ' . $exception->getLine(); } $props = array_merge($props, [ @@ -204,11 +188,9 @@ class Form * Get the field object by name * and handle nested fields correctly * - * @param string $name * @throws \Kirby\Exception\NotFoundException - * @return \Kirby\Form\Field */ - public function field(string $name) + public function field(string $name): Field|FieldClass { $form = $this; $fieldNames = Str::split($name, '+'); @@ -223,9 +205,11 @@ class Form if ($count !== $index) { $form = $field->form(); } - } else { - throw new NotFoundException('The field "' . $fieldName . '" could not be found'); + + continue; } + + throw new NotFoundException('The field "' . $fieldName . '" could not be found'); } // it can get this error only if $name is an empty string as $name = '' @@ -238,21 +222,16 @@ class Form /** * Returns form fields - * - * @return \Kirby\Form\Fields|null */ - public function fields() + public function fields(): Fields|null { return $this->fields; } - /** - * @param \Kirby\Cms\Model $model - * @param array $props - * @return static - */ - public static function for(Model $model, array $props = []) - { + public static function for( + ModelWithContent $model, + array $props = [] + ): static { // get the original model data $original = $model->content($props['language'] ?? null)->toArray(); $values = $props['values'] ?? []; @@ -270,7 +249,10 @@ class Form $props['model'] = $model; // search for the blueprint - if (method_exists($model, 'blueprint') === true && $blueprint = $model->blueprint()) { + if ( + method_exists($model, 'blueprint') === true && + $blueprint = $model->blueprint() + ) { $props['fields'] = $blueprint->fields(); } @@ -289,18 +271,14 @@ class Form /** * Checks if the form is invalid - * - * @return bool */ public function isInvalid(): bool { - return empty($this->errors()) === false; + return $this->isValid() === false; } /** * Checks if the form is valid - * - * @return bool */ public function isValid(): bool { @@ -310,17 +288,15 @@ class Form /** * Disables fields in secondary languages when * they are configured to be untranslatable - * - * @param array $fields - * @param string|null $language - * @return array */ - protected static function prepareFieldsForLanguage(array $fields, string|null $language = null): array - { + protected static function prepareFieldsForLanguage( + array $fields, + string|null $language = null + ): array { $kirby = App::instance(null, true); // only modify the fields if we have a valid Kirby multilang instance - if (!$kirby || $kirby->multilang() === false) { + if ($kirby?->multilang() !== true) { return $fields; } @@ -343,29 +319,20 @@ class Form * Converts the data of fields to strings * * @param false $defaults - * @return array */ public function strings($defaults = false): array { - $strings = []; - - foreach ($this->data($defaults) as $key => $value) { - if ($value === null) { - $strings[$key] = null; - } elseif (is_array($value) === true) { - $strings[$key] = Data::encode($value, 'yaml'); - } else { - $strings[$key] = $value; + return A::map( + $this->data($defaults), + fn ($value) => match (true) { + is_array($value) => Data::encode($value, 'yaml'), + default => $value } - } - - return $strings; + ); } /** * Converts the form to a plain array - * - * @return array */ public function toArray(): array { @@ -380,8 +347,6 @@ class Form /** * Returns form values - * - * @return array */ public function values(): array { diff --git a/kirby/src/Form/Mixin/EmptyState.php b/kirby/src/Form/Mixin/EmptyState.php index 9ea8b0c..6f7d72a 100644 --- a/kirby/src/Form/Mixin/EmptyState.php +++ b/kirby/src/Form/Mixin/EmptyState.php @@ -4,9 +4,9 @@ namespace Kirby\Form\Mixin; trait EmptyState { - protected $empty; + protected string|null $empty; - protected function setEmpty($empty = null) + protected function setEmpty(string|array|null $empty = null): void { $this->empty = $this->i18n($empty); } diff --git a/kirby/src/Form/Mixin/Max.php b/kirby/src/Form/Mixin/Max.php index 501ba2d..1641917 100644 --- a/kirby/src/Form/Mixin/Max.php +++ b/kirby/src/Form/Mixin/Max.php @@ -4,14 +4,14 @@ namespace Kirby\Form\Mixin; trait Max { - protected $max; + protected int|null $max; public function max(): int|null { return $this->max; } - protected function setMax(int $max = null) + protected function setMax(int|null $max = null) { $this->max = $max; } diff --git a/kirby/src/Form/Mixin/Min.php b/kirby/src/Form/Mixin/Min.php index b177a08..aa57af5 100644 --- a/kirby/src/Form/Mixin/Min.php +++ b/kirby/src/Form/Mixin/Min.php @@ -4,14 +4,14 @@ namespace Kirby\Form\Mixin; trait Min { - protected $min; + protected int|null $min; public function min(): int|null { return $this->min; } - protected function setMin(int $min = null) + protected function setMin(int|null $min = null) { $this->min = $min; } diff --git a/kirby/src/Form/Options.php b/kirby/src/Form/Options.php deleted file mode 100644 index 3b5da34..0000000 --- a/kirby/src/Form/Options.php +++ /dev/null @@ -1,205 +0,0 @@ - - * @link https://getkirby.com - * @copyright Bastian Allgeier - * @license https://opensource.org/licenses/MIT - * - * @deprecated 3.8.0 Use `Kirby\Option\Options` instead - */ -class Options -{ - /** - * Returns the classes of predefined Kirby objects - * - * @return array - */ - protected static function aliases(): array - { - return [ - 'Kirby\Cms\File' => 'file', - 'Kirby\Toolkit\Obj' => 'arrayItem', - 'Kirby\Cms\Block' => 'block', - 'Kirby\Cms\Page' => 'page', - 'Kirby\Cms\StructureObject' => 'structureItem', - 'Kirby\Cms\User' => 'user', - ]; - } - - /** - * Brings options through api - * - * @param $api - * @param \Kirby\Cms\Model|null $model - * @return array - */ - public static function api($api, $model = null): array - { - $model ??= App::instance()->site(); - $fetch = null; - $text = null; - $value = null; - - if (is_array($api) === true) { - $fetch = $api['fetch'] ?? null; - $text = $api['text'] ?? null; - $value = $api['value'] ?? null; - $url = $api['url'] ?? null; - } else { - $url = $api; - } - - $optionsApi = new OptionsApi([ - 'data' => static::data($model), - 'fetch' => $fetch, - 'url' => $url, - 'text' => $text, - 'value' => $value - ]); - - return $optionsApi->options(); - } - - /** - * @param \Kirby\Cms\Model $model - * @return array - */ - protected static function data($model): array - { - $kirby = $model->kirby(); - - // default data setup - $data = [ - 'kirby' => $kirby, - 'site' => $kirby->site(), - 'users' => $kirby->users(), - ]; - - // add the model by the proper alias - foreach (static::aliases() as $className => $alias) { - if ($model instanceof $className) { - $data[$alias] = $model; - } - } - - return $data; - } - - /** - * Brings options by supporting both api and query - * - * @param $options - * @param array $props - * @param \Kirby\Cms\Model|null $model - * @return array - */ - public static function factory($options, array $props = [], $model = null): array - { - $options = match ($options) { - 'api' => static::api($props['api'], $model), - 'query' => static::query($props['query'], $model), - 'pages' => static::query('site.index', $model), - 'children', - 'grandChildren', - 'siblings', - 'index', - 'files', - 'images', - 'documents', - 'videos', - 'audio', - 'code', - 'archives' => static::query('page.' . $options, $model), - default => $options - }; - - if (is_array($options) === false) { - return []; - } - - $result = []; - - foreach ($options as $key => $option) { - if (is_array($option) === false || isset($option['value']) === false) { - $option = [ - 'value' => is_int($key) ? $option : $key, - 'text' => $option - ]; - } - - // fallback for the text - $option['text'] ??= $option['value']; - - // translate the option text - if (is_array($option['text']) === true) { - $option['text'] = I18n::translate($option['text'], $option['text']); - } - - // add the option to the list - $result[] = $option; - } - - return $result; - } - - /** - * Brings options with query - * - * @param $query - * @param \Kirby\Cms\Model|null $model - * @return array - */ - public static function query($query, $model = null): array - { - $model ??= App::instance()->site(); - - // default text setup - $text = [ - 'arrayItem' => '{{ arrayItem.value }}', - 'block' => '{{ block.type }}: {{ block.id }}', - 'file' => '{{ file.filename }}', - 'page' => '{{ page.title }}', - 'structureItem' => '{{ structureItem.title }}', - 'user' => '{{ user.username }}', - ]; - - // default value setup - $value = [ - 'arrayItem' => '{{ arrayItem.value }}', - 'block' => '{{ block.id }}', - 'file' => '{{ file.id }}', - 'page' => '{{ page.id }}', - 'structureItem' => '{{ structureItem.id }}', - 'user' => '{{ user.email }}', - ]; - - // resolve array query setup - if (is_array($query) === true) { - $text = $query['text'] ?? $text; - $value = $query['value'] ?? $value; - $query = $query['fetch'] ?? null; - } - - $optionsQuery = new OptionsQuery([ - 'aliases' => static::aliases(), - 'data' => static::data($model), - 'query' => $query, - 'text' => $text, - 'value' => $value - ]); - - return $optionsQuery->options(); - } -} diff --git a/kirby/src/Form/OptionsApi.php b/kirby/src/Form/OptionsApi.php deleted file mode 100644 index a568520..0000000 --- a/kirby/src/Form/OptionsApi.php +++ /dev/null @@ -1,244 +0,0 @@ - - * @link https://getkirby.com - * @copyright Bastian Allgeier - * @license https://opensource.org/licenses/MIT - * - * @deprecated 3.8.0 Use `Kirby\Option\OptionsApi` instead - */ -class OptionsApi -{ - use Properties; - - /** - * @var array - */ - protected $data; - - /** - * @var string|null - */ - protected $fetch; - - /** - * @var array|string|null - */ - protected $options; - - /** - * @var string - */ - protected $text = '{{ item.value }}'; - - /** - * @var string - */ - protected $url; - - /** - * @var string - */ - protected $value = '{{ item.key }}'; - - /** - * OptionsApi constructor - * - * @param array $props - */ - public function __construct(array $props) - { - $this->setProperties($props); - } - - /** - * @return array - */ - public function data(): array - { - return $this->data; - } - - /** - * @return mixed - */ - public function fetch() - { - return $this->fetch; - } - - /** - * @param string $field - * @param array $data - * @return string - */ - protected function field(string $field, array $data): string - { - $value = $this->$field(); - return Str::safeTemplate($value, $data); - } - - /** - * @return array - * @throws \Exception - * @throws \Kirby\Exception\InvalidArgumentException - */ - public function options(): array - { - if (is_array($this->options) === true) { - return $this->options; - } - - if (Url::isAbsolute($this->url()) === true) { - // URL, request via cURL - $data = Remote::get($this->url())->json(); - } else { - // local file, get contents locally - - // ensure the file exists before trying to load it as the - // file_get_contents() warnings need to be suppressed - if (is_file($this->url()) !== true) { - throw new Exception('Local file ' . $this->url() . ' was not found'); - } - - $content = @file_get_contents($this->url()); - - if (is_string($content) !== true) { - throw new Exception('Unexpected read error'); // @codeCoverageIgnore - } - - if (empty($content) === true) { - return []; - } - - $data = json_decode($content, true); - } - - if (is_array($data) === false) { - throw new InvalidArgumentException('Invalid options format'); - } - - $result = (new Query($this->fetch()))->resolve(Nest::create($data)); - $options = []; - - foreach ($result as $item) { - $data = array_merge($this->data(), ['item' => $item]); - - $options[] = [ - 'text' => $this->field('text', $data), - 'value' => $this->field('value', $data), - ]; - } - - return $options; - } - - /** - * @param array $data - * @return $this - */ - protected function setData(array $data) - { - $this->data = $data; - return $this; - } - - /** - * @param string|null $fetch - * @return $this - */ - protected function setFetch(string|null $fetch = null) - { - $this->fetch = $fetch; - return $this; - } - - /** - * @param array|string|null $options - * @return $this - */ - protected function setOptions($options = null) - { - $this->options = $options; - return $this; - } - - /** - * @param string $text - * @return $this - */ - protected function setText(string|null $text = null) - { - $this->text = $text; - return $this; - } - - /** - * @param string $url - * @return $this - */ - protected function setUrl(string $url) - { - $this->url = $url; - return $this; - } - - /** - * @param string|null $value - * @return $this - */ - protected function setValue(string|null $value = null) - { - $this->value = $value; - return $this; - } - - /** - * @return string - */ - public function text(): string - { - return $this->text; - } - - /** - * @return array - * @throws \Kirby\Exception\InvalidArgumentException - */ - public function toArray(): array - { - return $this->options(); - } - - /** - * @return string - */ - public function url(): string - { - return Str::template($this->url, $this->data()); - } - - /** - * @return string - */ - public function value(): string - { - return $this->value; - } -} diff --git a/kirby/src/Form/OptionsQuery.php b/kirby/src/Form/OptionsQuery.php deleted file mode 100644 index 36ff287..0000000 --- a/kirby/src/Form/OptionsQuery.php +++ /dev/null @@ -1,273 +0,0 @@ - - * @link https://getkirby.com - * @copyright Bastian Allgeier - * @license https://opensource.org/licenses/MIT - * - * @deprecated 3.8.0 Use `Kirby\Option\OptionsQuery` instead - */ -class OptionsQuery -{ - use Properties; - - /** - * @var array - */ - protected $aliases = []; - - /** - * @var array - */ - protected $data; - - /** - * @var array|string|null - */ - protected $options; - - /** - * @var string - */ - protected $query; - - /** - * @var mixed - */ - protected $text; - - /** - * @var mixed - */ - protected $value; - - /** - * OptionsQuery constructor - * - * @param array $props - */ - public function __construct(array $props) - { - $this->setProperties($props); - } - - /** - * @return array - */ - public function aliases(): array - { - return $this->aliases; - } - - /** - * @return array - */ - public function data(): array - { - return $this->data; - } - - /** - * @param string $object - * @param string $field - * @param array $data - * @return string - * @throws \Kirby\Exception\NotFoundException - */ - protected function template(string $object, string $field, array $data) - { - $value = $this->$field(); - - if (is_array($value) === true) { - if (isset($value[$object]) === false) { - throw new NotFoundException('Missing "' . $field . '" definition'); - } - - $value = $value[$object]; - } - - return Str::safeTemplate($value, $data); - } - - /** - * @return array - */ - public function options(): array - { - if (is_array($this->options) === true) { - return $this->options; - } - - $data = $this->data(); - $query = new Query($this->query()); - $result = $query->resolve($data); - $result = $this->resultToCollection($result); - $options = []; - - foreach ($result as $item) { - $alias = $this->resolve($item); - $data = array_merge($data, [$alias => $item]); - - $options[] = [ - 'text' => $this->template($alias, 'text', $data), - 'value' => $this->template($alias, 'value', $data) - ]; - } - - return $this->options = $options; - } - - /** - * @return string - */ - public function query(): string - { - return $this->query; - } - - /** - * @param $object - * @return mixed|string|null - */ - public function resolve($object) - { - // fast access - if ($alias = ($this->aliases[get_class($object)] ?? null)) { - return $alias; - } - - // slow but precise resolving - foreach ($this->aliases as $className => $alias) { - if ($object instanceof $className) { - return $alias; - } - } - - return 'item'; - } - - /** - * @param $result - * @throws \Kirby\Exception\InvalidArgumentException - */ - protected function resultToCollection($result) - { - if (is_array($result)) { - foreach ($result as $key => $item) { - if (is_scalar($item) === true) { - $result[$key] = new Obj([ - 'key' => new Field(null, 'key', $key), - 'value' => new Field(null, 'value', $item), - ]); - } - } - - $result = new Collection($result); - } - - if ($result instanceof Collection === false) { - throw new InvalidArgumentException('Invalid query result data'); - } - - return $result; - } - - /** - * @param array|null $aliases - * @return $this - */ - protected function setAliases(array|null $aliases = null) - { - $this->aliases = $aliases; - return $this; - } - - /** - * @param array $data - * @return $this - */ - protected function setData(array $data) - { - $this->data = $data; - return $this; - } - - /** - * @param array|string|null $options - * @return $this - */ - protected function setOptions($options = null) - { - $this->options = $options; - return $this; - } - - /** - * @param string $query - * @return $this - */ - protected function setQuery(string $query) - { - $this->query = $query; - return $this; - } - - /** - * @param mixed $text - * @return $this - */ - protected function setText($text) - { - $this->text = $text; - return $this; - } - - /** - * @param mixed $value - * @return $this - */ - protected function setValue($value) - { - $this->value = $value; - return $this; - } - - /** - * @return mixed - */ - public function text() - { - return $this->text; - } - - public function toArray(): array - { - return $this->options(); - } - - /** - * @return mixed - */ - public function value() - { - return $this->value; - } -} diff --git a/kirby/src/Form/Validations.php b/kirby/src/Form/Validations.php index 5834624..cf1235a 100644 --- a/kirby/src/Form/Validations.php +++ b/kirby/src/Form/Validations.php @@ -20,8 +20,6 @@ class Validations * Validates if the field value is boolean * * @param \Kirby\Form\Field|\Kirby\Form\FieldClass $field - * @param $value - * @return bool * @throws \Kirby\Exception\InvalidArgumentException */ public static function boolean($field, $value): bool @@ -40,12 +38,9 @@ class Validations /** * Validates if the field value is valid date * - * @param \Kirby\Form\Field|\Kirby\Form\FieldClass $field - * @param $value - * @return bool * @throws \Kirby\Exception\InvalidArgumentException */ - public static function date($field, $value): bool + public static function date(Field|FieldClass $field, mixed $value): bool { if ($field->isEmpty($value) === false) { if (V::date($value) !== true) { @@ -61,12 +56,9 @@ class Validations /** * Validates if the field value is valid email * - * @param \Kirby\Form\Field|\Kirby\Form\FieldClass $field - * @param $value - * @return bool * @throws \Kirby\Exception\InvalidArgumentException */ - public static function email($field, $value): bool + public static function email(Field|FieldClass $field, mixed $value): bool { if ($field->isEmpty($value) === false) { if (V::email($value) === false) { @@ -82,14 +74,14 @@ class Validations /** * Validates if the field value is maximum * - * @param \Kirby\Form\Field|\Kirby\Form\FieldClass $field - * @param $value - * @return bool * @throws \Kirby\Exception\InvalidArgumentException */ - public static function max($field, $value): bool + public static function max(Field|FieldClass $field, mixed $value): bool { - if ($field->isEmpty($value) === false && $field->max() !== null) { + if ( + $field->isEmpty($value) === false && + $field->max() !== null + ) { if (V::max($value, $field->max()) === false) { throw new InvalidArgumentException( V::message('max', $value, $field->max()) @@ -103,14 +95,14 @@ class Validations /** * Validates if the field value is max length * - * @param \Kirby\Form\Field|\Kirby\Form\FieldClass $field - * @param $value - * @return bool * @throws \Kirby\Exception\InvalidArgumentException */ - public static function maxlength($field, $value): bool + public static function maxlength(Field|FieldClass $field, mixed $value): bool { - if ($field->isEmpty($value) === false && $field->maxlength() !== null) { + if ( + $field->isEmpty($value) === false && + $field->maxlength() !== null + ) { if (V::maxLength($value, $field->maxlength()) === false) { throw new InvalidArgumentException( V::message('maxlength', $value, $field->maxlength()) @@ -124,14 +116,14 @@ class Validations /** * Validates if the field value is minimum * - * @param \Kirby\Form\Field|\Kirby\Form\FieldClass $field - * @param $value - * @return bool * @throws \Kirby\Exception\InvalidArgumentException */ - public static function min($field, $value): bool + public static function min(Field|FieldClass $field, mixed $value): bool { - if ($field->isEmpty($value) === false && $field->min() !== null) { + if ( + $field->isEmpty($value) === false && + $field->min() !== null + ) { if (V::min($value, $field->min()) === false) { throw new InvalidArgumentException( V::message('min', $value, $field->min()) @@ -145,14 +137,14 @@ class Validations /** * Validates if the field value is min length * - * @param \Kirby\Form\Field|\Kirby\Form\FieldClass $field - * @param $value - * @return bool * @throws \Kirby\Exception\InvalidArgumentException */ - public static function minlength($field, $value): bool + public static function minlength(Field|FieldClass $field, mixed $value): bool { - if ($field->isEmpty($value) === false && $field->minlength() !== null) { + if ( + $field->isEmpty($value) === false && + $field->minlength() !== null + ) { if (V::minLength($value, $field->minlength()) === false) { throw new InvalidArgumentException( V::message('minlength', $value, $field->minlength()) @@ -166,18 +158,22 @@ class Validations /** * Validates if the field value matches defined pattern * - * @param \Kirby\Form\Field|\Kirby\Form\FieldClass $field - * @param $value - * @return bool * @throws \Kirby\Exception\InvalidArgumentException */ - public static function pattern($field, $value): bool + public static function pattern(Field|FieldClass $field, mixed $value): bool { - if ($field->isEmpty($value) === false && $field->pattern() !== null) { - if (V::match($value, '/' . $field->pattern() . '/i') === false) { - throw new InvalidArgumentException( - V::message('match') - ); + if ($field->isEmpty($value) === false) { + if ($pattern = $field->pattern()) { + // ensure that that pattern needs to match the whole + // input value from start to end, not just a partial match + // https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/pattern#overview + $pattern = '^(?:' . $pattern . ')$'; + + if (V::match($value, '/' . $pattern . '/i') === false) { + throw new InvalidArgumentException( + V::message('match') + ); + } } } @@ -187,14 +183,15 @@ class Validations /** * Validates if the field value is required * - * @param \Kirby\Form\Field|\Kirby\Form\FieldClass $field - * @param $value - * @return bool * @throws \Kirby\Exception\InvalidArgumentException */ - public static function required($field, $value): bool + public static function required(Field|FieldClass $field, mixed $value): bool { - if ($field->isRequired() === true && $field->save() === true && $field->isEmpty($value) === true) { + if ( + $field->isRequired() === true && + $field->save() === true && + $field->isEmpty($value) === true + ) { throw new InvalidArgumentException([ 'key' => 'validation.required' ]); @@ -206,12 +203,9 @@ class Validations /** * Validates if the field value is in defined options * - * @param \Kirby\Form\Field|\Kirby\Form\FieldClass $field - * @param $value - * @return bool * @throws \Kirby\Exception\InvalidArgumentException */ - public static function option($field, $value): bool + public static function option(Field|FieldClass $field, mixed $value): bool { if ($field->isEmpty($value) === false) { $values = array_column($field->options(), 'value'); @@ -229,12 +223,9 @@ class Validations /** * Validates if the field values is in defined options * - * @param \Kirby\Form\Field|\Kirby\Form\FieldClass $field - * @param $value - * @return bool * @throws \Kirby\Exception\InvalidArgumentException */ - public static function options($field, $value): bool + public static function options(Field|FieldClass $field, mixed $value): bool { if ($field->isEmpty($value) === false) { $values = array_column($field->options(), 'value'); @@ -253,12 +244,9 @@ class Validations /** * Validates if the field value is valid time * - * @param \Kirby\Form\Field|\Kirby\Form\FieldClass $field - * @param $value - * @return bool * @throws \Kirby\Exception\InvalidArgumentException */ - public static function time($field, $value): bool + public static function time(Field|FieldClass $field, mixed $value): bool { if ($field->isEmpty($value) === false) { if (V::time($value) !== true) { @@ -274,12 +262,9 @@ class Validations /** * Validates if the field value is valid url * - * @param \Kirby\Form\Field|\Kirby\Form\FieldClass $field - * @param $value - * @return bool * @throws \Kirby\Exception\InvalidArgumentException */ - public static function url($field, $value): bool + public static function url(Field|FieldClass $field, mixed $value): bool { if ($field->isEmpty($value) === false) { if (V::url($value) === false) { diff --git a/kirby/src/Http/Cookie.php b/kirby/src/Http/Cookie.php index 1d5c3e6..a44b752 100644 --- a/kirby/src/Http/Cookie.php +++ b/kirby/src/Http/Cookie.php @@ -39,8 +39,11 @@ class Cookie * @return bool true: cookie was created, * false: cookie creation failed */ - public static function set(string $key, string $value, array $options = []): bool - { + public static function set( + string $key, + string $value, + array $options = [] + ): bool { // modify CMS caching behavior static::trackUsage($key); @@ -59,8 +62,11 @@ class Cookie $_COOKIE[$key] = $value; // store the cookie - $options = compact('expires', 'path', 'domain', 'secure', 'httponly', 'samesite'); - return setcookie($key, $value, $options); + return setcookie( + $key, + $value, + compact('expires', 'path', 'domain', 'secure', 'httponly', 'samesite') + ); } /** @@ -70,13 +76,13 @@ class Cookie */ public static function lifetime(int $minutes): int { + // absolute timestamp if ($minutes > 1000000000) { - // absolute timestamp return $minutes; } + // minutes from now if ($minutes > 0) { - // minutes from now return time() + ($minutes * 60); } @@ -100,8 +106,11 @@ class Cookie * @return bool true: cookie was created, * false: cookie creation failed */ - public static function forever(string $key, string $value, array $options = []): bool - { + public static function forever( + string $key, + string $value, + array $options = [] + ): bool { // 9999-12-31 if supported (lower on 32-bit servers) $options['lifetime'] = min(253402214400, PHP_INT_MAX); return static::set($key, $value, $options); @@ -111,10 +120,8 @@ class Cookie * Get a cookie value * * - * * cookie::get('mycookie', 'peter'); * // sample output: 'hello' or if the cookie is not set 'peter' - * * * * @param string|null $key The name of the cookie @@ -122,8 +129,10 @@ class Cookie * if the cookie has not been found * @return string|array|null The found value */ - public static function get(string|null $key = null, string|null $default = null): string|array|null - { + public static function get( + string|null $key = null, + string|null $default = null + ): string|array|null { if ($key === null) { return $_COOKIE; } @@ -131,8 +140,11 @@ class Cookie // modify CMS caching behavior static::trackUsage($key); - $value = $_COOKIE[$key] ?? null; - return empty($value) ? $default : static::parse($value); + if ($value = $_COOKIE[$key] ?? null) { + return static::parse($value); + } + + return $default; } /** diff --git a/kirby/src/Http/Environment.php b/kirby/src/Http/Environment.php index f60b055..c7b1f05 100644 --- a/kirby/src/Http/Environment.php +++ b/kirby/src/Http/Environment.php @@ -103,14 +103,16 @@ class Environment * * @param array|null $info Optional override for `$_SERVER` */ - public function __construct(array|null $options = null, array|null $info = null) - { + public function __construct( + array|null $options = null, + array|null $info = null + ) { $this->detect($options, $info); } /** * Returns the server's IP address - * @see static::ip + * @see ::ip */ public function address(): string|null { @@ -150,13 +152,17 @@ class Environment * * @param array|null $info Optional override for `$_SERVER` */ - public function detect(array $options = null, array $info = null): array - { - $info ??= $_SERVER; - $options = array_merge([ + public function detect( + array|null $options = null, + array|null $info = null + ): array { + $defaults = [ 'cli' => null, 'allowed' => null - ], $options ?? []); + ]; + + $info ??= $_SERVER; + $options = array_merge($defaults, $options ?? []); $this->info = static::sanitize($info); $this->cli = $this->detectCli($options['cli']); @@ -172,11 +178,11 @@ class Environment if ($options['allowed'] === '*' || $options['allowed'] === ['*']) { $this->detectAuto(true); - // fixed environments + // fixed environments } elseif (empty($options['allowed']) === false) { $this->detectAllowed($options['allowed']); - // secure auto-detection + // secure auto-detection } else { $this->detectAuto(); } @@ -235,9 +241,9 @@ class Environment $uri = new Uri($url, ['slash' => false]); + // the current environment is allowed, + // stop before the exception below is thrown if ($uri->toString() === $this->baseUrl) { - // the current environment is allowed, - // stop before the exception below is thrown return; } } @@ -316,9 +322,18 @@ class Environment } // @codeCoverageIgnoreStart + $sapi = php_sapi_name(); + if ($sapi === 'cli') { + return true; + } + $term = getenv('TERM'); - if (substr(PHP_SAPI, 0, 3) === 'cgi' && $term && $term !== 'unknown') { + if ( + substr($sapi, 0, 3) === 'cgi' && + $term && + $term !== 'unknown' + ) { return true; } @@ -340,8 +355,7 @@ class Environment ]; // prefer the standardized `Forwarded` header if defined - $forwarded = $this->get('HTTP_FORWARDED'); - if ($forwarded) { + if ($forwarded = $this->get('HTTP_FORWARDED')) { // only use the first (outermost) proxy by using the first set of values // before the first comma (but only a comma outside of quotes) if (Str::contains($forwarded, ',') === true) { @@ -513,7 +527,9 @@ class Environment return false; } - return in_array(strtolower($protocol), ['https', 'https, http']) === true; + $protocols = ['https', 'https, http']; + + return in_array(strtolower($protocol), $protocols) === true; } /** @@ -586,13 +602,7 @@ class Environment */ protected function detectRequestUri(string|null $requestUri = null): Uri { - // make sure the URL parser works properly when there's a - // colon in the request URI but the URI is relative - if (Url::isAbsolute($requestUri) === false) { - $requestUri = 'https://getkirby.com' . $requestUri; - } - - $uri = new Uri($requestUri); + $uri = new Uri($requestUri ?? ''); // create the URI object as a combination of base uri parts // and the parts from REQUEST_URI @@ -660,11 +670,12 @@ class Environment * @param mixed $default Optional default value, which should be * returned if no element has been found */ - public static function getGlobally(string|false|null $key = null, $default = null) - { + public static function getGlobally( + string|false|null $key = null, + $default = null + ) { // first try the global `Environment` object if the CMS is running - $app = App::instance(null, true); - if ($app) { + if ($app = App::instance(null, true)) { return $app->environment()->get($key, $default); } @@ -741,6 +752,10 @@ class Environment return true; } + if (Str::endsWith($host, '.ddev.site') === true) { + return true; + } + // collect all possible visitor ips $ips = [ $this->get('REMOTE_ADDR'), @@ -851,8 +866,10 @@ class Environment /** * Sanitizes some `$_SERVER` keys */ - public static function sanitize(string|array $key, $value = null) - { + public static function sanitize( + string|array $key, + $value = null + ) { if (is_array($key) === true) { foreach ($key as $k => $v) { $key[$k] = static::sanitize($k, $v); @@ -877,8 +894,9 @@ class Environment /** * Sanitizes the given host name */ - protected static function sanitizeHost(string|null $host = null): string|null - { + protected static function sanitizeHost( + string|null $host = null + ): string|null { if (empty($host) === true) { return null; } @@ -902,8 +920,9 @@ class Environment /** * Sanitizes the given port number */ - protected static function sanitizePort(string|int|false|null $port = null): int|null - { + protected static function sanitizePort( + string|int|false|null $port = null + ): int|null { // already fine if (is_int($port) === true) { return $port; diff --git a/kirby/src/Http/Header.php b/kirby/src/Http/Header.php index 0c36b58..857798c 100644 --- a/kirby/src/Http/Header.php +++ b/kirby/src/Http/Header.php @@ -18,7 +18,6 @@ class Header { // configuration public static array $codes = [ - // successful '_200' => 'OK', '_201' => 'Created', @@ -58,8 +57,11 @@ class Header * * @return string|void */ - public static function contentType(string $mime, string $charset = 'UTF-8', bool $send = true) - { + public static function contentType( + string $mime, + string $charset = 'UTF-8', + bool $send = true + ) { if ($found = F::extensionToMime($mime)) { $mime = $found; } @@ -80,8 +82,10 @@ class Header /** * Creates headers by key and value */ - public static function create(string|array $key, string|null $value = null): string - { + public static function create( + string|array $key, + string|null $value = null + ): string { if (is_array($key) === true) { $headers = []; @@ -92,7 +96,8 @@ class Header return implode("\r\n", $headers); } - // prevent header injection by stripping any newline characters from single headers + // prevent header injection by stripping + // any newline characters from single headers return str_replace(["\r", "\n"], '', $key . ': ' . $value); } @@ -258,8 +263,11 @@ class Header * * @return string|void */ - public static function redirect(string $url, int $code = 302, bool $send = true) - { + public static function redirect( + string $url, + int $code = 302, + bool $send = true + ) { $status = static::status($code, false); $location = 'Location:' . Url::unIdn($url); diff --git a/kirby/src/Http/Params.php b/kirby/src/Http/Params.php index 6ad821f..4067a0f 100644 --- a/kirby/src/Http/Params.php +++ b/kirby/src/Http/Params.php @@ -93,7 +93,7 @@ class Params extends Obj public function isNotEmpty(): bool { - return empty((array)$this) === false; + return $this->isEmpty() === false; } /** diff --git a/kirby/src/Http/Query.php b/kirby/src/Http/Query.php index 8cd3de8..410e2f0 100644 --- a/kirby/src/Http/Query.php +++ b/kirby/src/Http/Query.php @@ -33,7 +33,7 @@ class Query extends Obj public function isNotEmpty(): bool { - return empty((array)$this) === false; + return $this->isEmpty() === false; } public function toString(bool $questionMark = false): string diff --git a/kirby/src/Http/Remote.php b/kirby/src/Http/Remote.php index 2348ec4..2d596ff 100644 --- a/kirby/src/Http/Remote.php +++ b/kirby/src/Http/Remote.php @@ -50,17 +50,6 @@ class Remote public array $options = []; /** - * Magic getter for request info data - */ - public function __call(string $method, array $arguments = []) - { - $method = str_replace('-', '_', Str::kebab($method)); - return $this->info[$method] ?? null; - } - - /** - * Constructor - * * @throws \Exception when the curl request failed */ public function __construct(string $url, array $options = []) @@ -70,14 +59,15 @@ class Remote // use the system CA store by default if // one has been configured in php.ini $cainfo = ini_get('curl.cainfo'); - if (empty($cainfo) === false && is_file($cainfo) === true) { + + // Suppress warnings e.g. if system CA is outside of open_basedir (See: issue #6236) + if (empty($cainfo) === false && @is_file($cainfo) === true) { $defaults['ca'] = self::CA_SYSTEM; } // update the defaults with App config if set; // request the App instance lazily - $app = App::instance(null, true); - if ($app !== null) { + if ($app = App::instance(null, true)) { $defaults = array_merge($defaults, $app->option('remote', [])); } @@ -91,8 +81,19 @@ class Remote $this->fetch(); } - public static function __callStatic(string $method, array $arguments = []): static + /** + * Magic getter for request info data + */ + public function __call(string $method, array $arguments = []) { + $method = str_replace('-', '_', Str::kebab($method)); + return $this->info[$method] ?? null; + } + + public static function __callStatic( + string $method, + array $arguments = [] + ): static { return new static( url: $arguments[0], options: array_merge( @@ -307,6 +308,7 @@ class Remote * Decode the response content * * @param bool $array decode as array or object + * @psalm-return ($array is true ? array|null : stdClass|null) */ public function json(bool $array = true): array|stdClass|null { diff --git a/kirby/src/Http/Request.php b/kirby/src/Http/Request.php index 3ad338c..85178a9 100644 --- a/kirby/src/Http/Request.php +++ b/kirby/src/Http/Request.php @@ -4,6 +4,9 @@ namespace Kirby\Http; use Kirby\Cms\App; use Kirby\Http\Request\Auth; +use Kirby\Http\Request\Auth\BasicAuth; +use Kirby\Http\Request\Auth\BearerAuth; +use Kirby\Http\Request\Auth\SessionAuth; use Kirby\Http\Request\Body; use Kirby\Http\Request\Files; use Kirby\Http\Request\Query; @@ -24,9 +27,9 @@ use Kirby\Toolkit\Str; class Request { public static array $authTypes = [ - 'basic' => 'Kirby\Http\Request\Auth\BasicAuth', - 'bearer' => 'Kirby\Http\Request\Auth\BearerAuth', - 'session' => 'Kirby\Http\Request\Auth\SessionAuth', + 'basic' => BasicAuth::class, + 'bearer' => BearerAuth::class, + 'session' => SessionAuth::class, ]; /** @@ -99,24 +102,37 @@ class Request $this->method = $this->detectRequestMethod($options['method'] ?? null); if (isset($options['body']) === true) { - $this->body = $options['body'] instanceof Body ? $options['body'] : new Body($options['body']); + $this->body = + $options['body'] instanceof Body + ? $options['body'] + : new Body($options['body']); } if (isset($options['files']) === true) { - $this->files = $options['files'] instanceof Files ? $options['files'] : new Files($options['files']); + $this->files = + $options['files'] instanceof Files + ? $options['files'] + : new Files($options['files']); } if (isset($options['query']) === true) { - $this->query = $options['query'] instanceof Query ? $options['query'] : new Query($options['query']); + $this->query = + $options['query'] instanceof Query + ? $options['query'] + : new Query($options['query']); } if (isset($options['url']) === true) { - $this->url = $options['url'] instanceof Uri ? $options['url'] : new Uri($options['url']); + $this->url = + $options['url'] instanceof Uri + ? $options['url'] + : new Uri($options['url']); } } /** * Improved `var_dump` output + * @codeCoverageIgnore */ public function __debugInfo(): array { @@ -195,7 +211,10 @@ class Request */ public function data(): array { - return array_replace($this->body()->toArray(), $this->query()->toArray()); + return array_replace( + $this->body()->toArray(), + $this->query()->toArray() + ); } /** @@ -215,7 +234,7 @@ class Request } // final chain of options to detect the method - $method = $method ?? Environment::getGlobally('REQUEST_METHOD', 'GET'); + $method ??= Environment::getGlobally('REQUEST_METHOD', 'GET'); // uppercase the shit out of it $method = strtoupper($method); diff --git a/kirby/src/Http/Request/Auth.php b/kirby/src/Http/Request/Auth.php index f197379..e73f4da 100644 --- a/kirby/src/Http/Request/Auth.php +++ b/kirby/src/Http/Request/Auth.php @@ -16,19 +16,12 @@ use SensitiveParameter; abstract class Auth { /** - * Raw authentication data after the first space - * in the `Authorization` header - */ - protected string $data; - - /** - * Constructor + * @param string $data Raw authentication data after the first space in the `Authorization` header */ public function __construct( #[SensitiveParameter] - string $data + protected string $data ) { - $this->data = $data; } /** diff --git a/kirby/src/Http/Request/Data.php b/kirby/src/Http/Request/Data.php index 58be233..d2f3d95 100644 --- a/kirby/src/Http/Request/Data.php +++ b/kirby/src/Http/Request/Data.php @@ -20,6 +20,7 @@ trait Data { /** * Improved `var_dump` output + * @codeCoverageIgnore */ public function __debugInfo(): array { diff --git a/kirby/src/Http/Request/Query.php b/kirby/src/Http/Request/Query.php index b6c4ad1..51b004a 100644 --- a/kirby/src/Http/Request/Query.php +++ b/kirby/src/Http/Request/Query.php @@ -20,7 +20,7 @@ class Query /** * The Query data array */ - protected array|null $data; + protected array|null $data = null; /** * Creates a new Query object. diff --git a/kirby/src/Http/Response.php b/kirby/src/Http/Response.php index 0c373bf..6650d4a 100644 --- a/kirby/src/Http/Response.php +++ b/kirby/src/Http/Response.php @@ -6,7 +6,6 @@ use Closure; use Exception; use Kirby\Exception\LogicException; use Kirby\Filesystem\F; -use Throwable; /** * Representation of an Http response, @@ -82,6 +81,7 @@ class Response /** * Improved `var_dump` output + * @codeCoverageIgnore */ public function __debugInfo(): array { @@ -95,11 +95,7 @@ class Response */ public function __toString(): string { - try { - return $this->send(); - } catch (Throwable) { - return ''; - } + return $this->send(); } /** @@ -250,7 +246,7 @@ class Response array $headers = [] ): static { if (is_array($body) === true) { - $body = json_encode($body, $pretty === true ? JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES : 0); + $body = json_encode($body, $pretty === true ? JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES : 0); } return new static([ @@ -291,7 +287,7 @@ class Response } // send the content type header - header('Content-Type:' . $this->type() . '; charset=' . $this->charset()); + header('Content-Type: ' . $this->type() . '; charset=' . $this->charset()); // print the response body return $this->body(); diff --git a/kirby/src/Http/Route.php b/kirby/src/Http/Route.php index 18796c4..8bc8348 100644 --- a/kirby/src/Http/Route.php +++ b/kirby/src/Http/Route.php @@ -63,7 +63,7 @@ class Route /** * Magic getter for route attributes */ - public function __call(string $key, array $arguments = null) + public function __call(string $key, array|null $args = null): mixed { return $this->attributes[$key] ?? null; } diff --git a/kirby/src/Http/Uri.php b/kirby/src/Http/Uri.php index a44526f..9cd951c 100644 --- a/kirby/src/Http/Uri.php +++ b/kirby/src/Http/Uri.php @@ -4,7 +4,6 @@ namespace Kirby\Http; use Kirby\Cms\App; use Kirby\Exception\InvalidArgumentException; -use Kirby\Toolkit\Properties; use SensitiveParameter; use Throwable; @@ -19,8 +18,6 @@ use Throwable; */ class Uri { - use Properties; - /** * Cache for the current Uri object */ @@ -29,32 +26,32 @@ class Uri /** * The fragment after the hash */ - protected string|false|null $fragment = null; + protected string|false|null $fragment; /** * The host address */ - protected string|null $host = null; + protected string|null $host; /** * The optional password for basic authentication */ - protected string|false|null $password = null; + protected string|false|null $password; /** * The optional list of params */ - protected Params|null $params = null; + protected Params $params; /** * The optional path */ - protected Path|null $path = null; + protected Path $path; /** * The optional port number */ - protected int|false|null $port = null; + protected int|false|null $port; /** * All original properties @@ -64,25 +61,67 @@ class Uri /** * The optional query string without leading ? */ - protected Query|null $query = null; + protected Query $query; /** * https or http */ - protected string|null $scheme = 'http'; + protected string|null $scheme; /** * Supported schemes */ protected static array $schemes = ['http', 'https', 'ftp']; - protected bool $slash = false; + protected bool $slash; /** * The optional username for basic authentication */ protected string|false|null $username = null; + /** + * Creates a new URI object + * + * @param array $inject Additional props to inject if a URL string is passed + */ + public function __construct(array|string $props = [], array $inject = []) + { + if (is_string($props) === true) { + // make sure the URL parser works properly when there's a + // colon in the string but the string is a relative URL + if (Url::isAbsolute($props) === false) { + $props = 'https://getkirby.com/' . $props; + $props = parse_url($props); + unset($props['scheme'], $props['host']); + } else { + $props = parse_url($props); + } + + $props['username'] = $props['user'] ?? null; + $props['password'] = $props['pass'] ?? null; + + $props = array_merge($props, $inject); + } + + // parse the path and extract params + if (empty($props['path']) === false) { + $props = static::parsePath($props); + } + + $this->props = $props; + $this->setFragment($props['fragment'] ?? null); + $this->setHost($props['host'] ?? null); + $this->setParams($props['params'] ?? null); + $this->setPassword($props['password'] ?? null); + $this->setPath($props['path'] ?? null); + $this->setPort($props['port'] ?? null); + $this->setQuery($props['query'] ?? null); + $this->setScheme($props['scheme'] ?? 'http'); + $this->setSlash($props['slash'] ?? false); + $this->setUsername($props['username'] ?? null); + } + /** * Magic caller to access all properties */ @@ -102,29 +141,6 @@ class Uri $this->params = clone $this->params; } - /** - * Creates a new URI object - * - * @param array $inject Additional props to inject if a URL string is passed - */ - public function __construct(array|string $props = [], array $inject = []) - { - if (is_string($props) === true) { - $props = parse_url($props); - $props['username'] = $props['user'] ?? null; - $props['password'] = $props['pass'] ?? null; - - $props = array_merge($props, $inject); - } - - // parse the path and extract params - if (empty($props['path']) === false) { - $props = static::parsePath($props); - } - - $this->setProperties($this->props = $props); - } - /** * Magic getter */ @@ -416,7 +432,7 @@ class Uri { $array = []; - foreach ($this->propertyData as $key => $value) { + foreach ($this->props as $key => $value) { $value = $this->$key; if (is_object($value) === true) { diff --git a/kirby/src/Http/Url.php b/kirby/src/Http/Url.php index 7a52907..8139b94 100644 --- a/kirby/src/Http/Url.php +++ b/kirby/src/Http/Url.php @@ -38,9 +38,13 @@ class Url * Url Builder * Actually just a factory for `new Uri($parts)` */ - public static function build(array $parts = [], string|null $url = null): string - { - return (string)(new Uri($url ?? static::current()))->clone($parts); + public static function build( + array $parts = [], + string|null $url = null + ): string { + $url ??= static::current(); + $uri = new Uri($url); + return $uri->clone($parts)->toString(); } /** @@ -139,7 +143,9 @@ class Url bool $leadingSlash = false, bool $trailingSlash = false ): string { - return Url::toObject($url)->path()->toString($leadingSlash, $trailingSlash); + return Url::toObject($url) + ->path() + ->toString($leadingSlash, $trailingSlash); } /** @@ -180,7 +186,7 @@ class Url $uri->slash = false; $url = $base ? $uri->base() : $uri->toString(); - $url = str_replace('www.', '', $url); + $url = str_replace('www.', '', $url ?? ''); return Str::short($url, $length, $rep); } @@ -212,8 +218,10 @@ class Url /** * Smart resolver for internal and external urls */ - public static function to(string|null $path = null, array $options = null): string - { + public static function to( + string|null $path = null, + array|null $options = null + ): string { // make sure $path is string $path ??= ''; diff --git a/kirby/src/Http/Visitor.php b/kirby/src/Http/Visitor.php index bf1d56b..ea2ac2e 100644 --- a/kirby/src/Http/Visitor.php +++ b/kirby/src/Http/Visitor.php @@ -34,10 +34,19 @@ class Visitor */ public function __construct(array $arguments = []) { - $this->ip($arguments['ip'] ?? Environment::getGlobally('REMOTE_ADDR', '')); - $this->userAgent($arguments['userAgent'] ?? Environment::getGlobally('HTTP_USER_AGENT', '')); - $this->acceptedLanguage($arguments['acceptedLanguage'] ?? Environment::getGlobally('HTTP_ACCEPT_LANGUAGE', '')); - $this->acceptedMimeType($arguments['acceptedMimeType'] ?? Environment::getGlobally('HTTP_ACCEPT', '')); + $ip = $arguments['ip'] ?? null; + $ip ??= Environment::getGlobally('REMOTE_ADDR', ''); + $agent = $arguments['userAgent'] ?? null; + $agent ??= Environment::getGlobally('HTTP_USER_AGENT', ''); + $language = $arguments['acceptedLanguage'] ?? null; + $language ??= Environment::getGlobally('HTTP_ACCEPT_LANGUAGE', ''); + $mime = $arguments['acceptedMimeType'] ?? null; + $mime ??= Environment::getGlobally('HTTP_ACCEPT', ''); + + $this->ip($ip); + $this->userAgent($agent); + $this->acceptedLanguage($language); + $this->acceptedMimeType($mime); } /** @@ -47,8 +56,9 @@ class Visitor * * @return $this|\Kirby\Toolkit\Obj|null */ - public function acceptedLanguage(string|null $acceptedLanguage = null): static|Obj|null - { + public function acceptedLanguage( + string|null $acceptedLanguage = null + ): static|Obj|null { if ($acceptedLanguage === null) { return $this->acceptedLanguages()->first(); } @@ -108,8 +118,9 @@ class Visitor * * @return $this|\Kirby\Toolkit\Obj|null */ - public function acceptedMimeType(string|null $acceptedMimeType = null): static|Obj|null - { + public function acceptedMimeType( + string|null $acceptedMimeType = null + ): static|Obj|null { if ($acceptedMimeType === null) { return $this->acceptedMimeTypes()->first(); } @@ -178,7 +189,8 @@ class Visitor */ public function prefersJson(): bool { - return $this->preferredMimeType('application/json', 'text/html') === 'application/json'; + $preferred = $this->preferredMimeType('application/json', 'text/html'); + return $preferred === 'application/json'; } /** @@ -193,6 +205,7 @@ class Visitor if ($ip === null) { return $this->ip; } + $this->ip = $ip; return $this; } @@ -209,6 +222,7 @@ class Visitor if ($userAgent === null) { return $this->userAgent; } + $this->userAgent = $userAgent; return $this; } diff --git a/kirby/src/Image/Camera.php b/kirby/src/Image/Camera.php index 8655330..c149c2e 100644 --- a/kirby/src/Image/Camera.php +++ b/kirby/src/Image/Camera.php @@ -13,16 +13,7 @@ namespace Kirby\Image; */ class Camera { - /** - * Make exif data - */ protected string|null $make; - - /** - * Model exif data - * - * @var - */ protected string|null $model; public function __construct(array $exif) @@ -68,6 +59,7 @@ class Camera /** * Improved `var_dump` output + * @codeCoverageIgnore */ public function __debugInfo(): array { diff --git a/kirby/src/Image/Darkroom.php b/kirby/src/Image/Darkroom.php index e8277ab..7d727e7 100644 --- a/kirby/src/Image/Darkroom.php +++ b/kirby/src/Image/Darkroom.php @@ -3,6 +3,8 @@ namespace Kirby\Image; use Exception; +use Kirby\Image\Darkroom\GdLib; +use Kirby\Image\Darkroom\ImageMagick; /** * A wrapper around resizing and cropping @@ -17,17 +19,13 @@ use Exception; class Darkroom { public static array $types = [ - 'gd' => 'Kirby\Image\Darkroom\GdLib', - 'im' => 'Kirby\Image\Darkroom\ImageMagick' + 'gd' => GdLib::class, + 'im' => ImageMagick::class ]; - protected array $settings = []; - - /** - * Darkroom constructor - */ - public function __construct(array $settings = []) - { + public function __construct( + protected array $settings = [] + ) { $this->settings = array_merge($this->defaults(), $settings); } @@ -62,6 +60,7 @@ class Darkroom 'quality' => 90, 'scaleHeight' => null, 'scaleWidth' => null, + 'sharpen' => null, 'width' => null, ]; } @@ -95,6 +94,11 @@ class Darkroom unset($options['bw']); } + // normalize the sharpen option + if ($options['sharpen'] === true) { + $options['sharpen'] = 50; + } + $options['quality'] ??= $this->settings['quality']; return $options; @@ -110,18 +114,24 @@ class Darkroom $options = $this->options($options); $image = new Image($file); - $dimensions = $image->dimensions(); - $thumbDimensions = $dimensions->thumb($options); + $options['sourceWidth'] = $image->width(); + $options['sourceHeight'] = $image->height(); - $sourceWidth = $image->width(); - $sourceHeight = $image->height(); + $dimensions = $image->dimensions(); + $thumbDimensions = $dimensions->thumb($options); $options['width'] = $thumbDimensions->width(); $options['height'] = $thumbDimensions->height(); // scale ratio compared to the source dimensions - $options['scaleWidth'] = $sourceWidth ? $options['width'] / $sourceWidth : null; - $options['scaleHeight'] = $sourceHeight ? $options['height'] / $sourceHeight : null; + $options['scaleWidth'] = Focus::ratio( + $options['width'], + $options['sourceWidth'] + ); + $options['scaleHeight'] = Focus::ratio( + $options['height'], + $options['sourceHeight'] + ); return $options; } diff --git a/kirby/src/Image/Darkroom/GdLib.php b/kirby/src/Image/Darkroom/GdLib.php index 62f31cf..0e837d6 100644 --- a/kirby/src/Image/Darkroom/GdLib.php +++ b/kirby/src/Image/Darkroom/GdLib.php @@ -5,6 +5,7 @@ namespace Kirby\Image\Darkroom; use claviska\SimpleImage; use Kirby\Filesystem\Mime; use Kirby\Image\Darkroom; +use Kirby\Image\Focus; /** * GdLib @@ -32,6 +33,7 @@ class GdLib extends Darkroom $image = $this->autoOrient($image, $options); $image = $this->blur($image, $options); $image = $this->grayscale($image, $options); + $image = $this->sharpen($image, $options); $image->toFile($file, $mime, $options); @@ -56,10 +58,34 @@ class GdLib extends Darkroom */ protected function resize(SimpleImage $image, array $options): SimpleImage { + // just resize, no crop if ($options['crop'] === false) { return $image->resize($options['width'], $options['height']); } + // crop based on focus point + if (Focus::isFocalPoint($options['crop']) === true) { + // get crop coords for focal point: + // if image needs to be cropped, crop before resizing + if ($focus = Focus::coords( + $options['crop'], + $options['sourceWidth'], + $options['sourceHeight'], + $options['width'], + $options['height'] + )) { + $image->crop( + $focus['x1'], + $focus['y1'], + $focus['x2'], + $focus['y2'] + ); + } + + return $image->thumbnail($options['width'], $options['height']); + } + + // normal crop with crop anchor return $image->thumbnail( $options['width'], $options['height'] ?? $options['width'], @@ -91,6 +117,18 @@ class GdLib extends Darkroom return $image->desaturate(); } + /** + * Applies sharpening if activated in the options. + */ + protected function sharpen(SimpleImage $image, array $options): SimpleImage + { + if (is_int($options['sharpen']) === false) { + return $image; + } + + return $image->sharpen($options['sharpen']); + } + /** * Returns mime type based on `format` option */ diff --git a/kirby/src/Image/Darkroom/ImageMagick.php b/kirby/src/Image/Darkroom/ImageMagick.php index cf10cf2..1fc767a 100644 --- a/kirby/src/Image/Darkroom/ImageMagick.php +++ b/kirby/src/Image/Darkroom/ImageMagick.php @@ -5,6 +5,7 @@ namespace Kirby\Image\Darkroom; use Exception; use Kirby\Filesystem\F; use Kirby\Image\Darkroom; +use Kirby\Image\Focus; /** * ImageMagick @@ -61,16 +62,8 @@ class ImageMagick extends Darkroom { $command = escapeshellarg($options['bin']); - // limit to single-threading to keep CPU usage sane - $command .= ' -limit thread 1'; - - // add JPEG size hint to optimize CPU and memory usage - if (F::mime($file) === 'image/jpeg') { - // add hint only when downscaling - if ($options['scaleWidth'] < 1 && $options['scaleHeight'] < 1) { - $command .= ' -define ' . escapeshellarg(sprintf('jpeg:size=%dx%d', $options['width'], $options['height'])); - } - } + // default is limiting to single-threading to keep CPU usage sane + $command .= ' -limit thread ' . escapeshellarg($options['threads']); // append input file return $command . ' ' . escapeshellarg($file); @@ -84,6 +77,7 @@ class ImageMagick extends Darkroom return parent::defaults() + [ 'bin' => 'convert', 'interlace' => false, + 'threads' => 1, ]; } @@ -99,6 +93,19 @@ class ImageMagick extends Darkroom return null; } + /** + * Applies sharpening if activated in the options. + */ + protected function sharpen(string $file, array $options): string|null + { + if (is_int($options['sharpen']) === false) { + return null; + } + + $amount = max(1, min(100, $options['sharpen'])) / 100; + return '-sharpen ' . escapeshellarg('0x' . $amount); + } + /** * Applies the correct settings for interlaced JPEGs if * activated via options @@ -132,6 +139,7 @@ class ImageMagick extends Darkroom $command[] = $this->resize($file, $options); $command[] = $this->quality($file, $options); $command[] = $this->blur($file, $options); + $command[] = $this->sharpen($file, $options); $command[] = $this->save($file, $options); // remove all null values and join the parts @@ -167,20 +175,39 @@ class ImageMagick extends Darkroom return '-thumbnail ' . escapeshellarg(sprintf('%sx%s!', $options['width'], $options['height'])); } - $gravities = [ + // crop based on focus point + if (Focus::isFocalPoint($options['crop']) === true) { + if ($focus = Focus::coords( + $options['crop'], + $options['sourceWidth'], + $options['sourceHeight'], + $options['width'], + $options['height'] + )) { + return sprintf( + '-crop %sx%s+%s+%s -resize %sx%s^', + $focus['width'], + $focus['height'], + $focus['x1'], + $focus['y1'], + $options['width'], + $options['height'] + ); + } + } + + // translate the gravity option into something imagemagick understands + $gravity = match ($options['crop'] ?? null) { 'top left' => 'NorthWest', 'top' => 'North', 'top right' => 'NorthEast', 'left' => 'West', - 'center' => 'Center', 'right' => 'East', 'bottom left' => 'SouthWest', 'bottom' => 'South', - 'bottom right' => 'SouthEast' - ]; - - // translate the gravity option into something imagemagick understands - $gravity = $gravities[$options['crop']] ?? 'Center'; + 'bottom right' => 'SouthEast', + default => 'Center' + }; $command = '-thumbnail ' . escapeshellarg(sprintf('%sx%s^', $options['width'], $options['height'])); $command .= ' -gravity ' . escapeshellarg($gravity); diff --git a/kirby/src/Image/Dimensions.php b/kirby/src/Image/Dimensions.php index 54bb636..cf6d334 100644 --- a/kirby/src/Image/Dimensions.php +++ b/kirby/src/Image/Dimensions.php @@ -26,6 +26,7 @@ class Dimensions /** * Improved `var_dump` output + * @codeCoverageIgnore */ public function __debugInfo(): array { @@ -138,8 +139,10 @@ class Dimensions * upscaled to fit the box if smaller * @return $this object with recalculated dimensions */ - public function fitHeight(int|null $fit = null, bool $force = false): static - { + public function fitHeight( + int|null $fit = null, + bool $force = false + ): static { return $this->fitSize('height', $fit, $force); } @@ -152,8 +155,11 @@ class Dimensions * upscaled to fit the box if smaller * @return $this object with recalculated dimensions */ - protected function fitSize(string $ref, int|null $fit = null, bool $force = false): static - { + protected function fitSize( + string $ref, + int|null $fit = null, + bool $force = false + ): static { if ($fit === 0 || $fit === null) { return $this; } @@ -191,8 +197,10 @@ class Dimensions * upscaled to fit the box if smaller * @return $this object with recalculated dimensions */ - public function fitWidth(int|null $fit = null, bool $force = false): static - { + public function fitWidth( + int|null $fit = null, + bool $force = false + ): static { return $this->fitSize('width', $fit, $force); } @@ -254,8 +262,7 @@ class Dimensions $xml = simplexml_load_string($content); if ($xml !== false) { - $attr = $xml->attributes(); - + $attr = $xml->attributes(); $rawWidth = $attr->width; $width = (int)$rawWidth; $rawHeight = $attr->height; @@ -300,11 +307,11 @@ class Dimensions return false; } - if ($this->portrait()) { + if ($this->portrait() === true) { return 'portrait'; } - if ($this->landscape()) { + if ($this->landscape() === true) { return 'landscape'; } @@ -336,7 +343,7 @@ class Dimensions return $this->width / $this->height; } - return 0; + return 0.0; } /** diff --git a/kirby/src/Image/Exif.php b/kirby/src/Image/Exif.php index 1bbb8a5..f32afd0 100644 --- a/kirby/src/Image/Exif.php +++ b/kirby/src/Image/Exif.php @@ -15,61 +15,30 @@ use Kirby\Toolkit\V; */ class Exif { - /** - * The parent image object - */ - protected Image $image; - /** * The raw exif array */ protected array $data = []; - /** - * The camera object with model and make - */ protected Camera|null $camera = null; - - /** - * The location object - */ protected Location|null $location = null; - - /** - * The timestamp - */ protected string|null $timestamp = null; - - /** - * The exposure value - */ protected string|null $exposure = null; - - /** - * The aperture value - */ protected string|null $aperture = null; - - /** - * ISO value - */ protected string|null $iso = null; - - /** - * Focal length - */ protected string|null $focalLength = null; - - /** - * Color or black/white - */ protected bool|null $isColor = null; - public function __construct(Image $image) - { - $this->image = $image; - $this->data = $this->read(); - $this->parse(); + public function __construct( + protected Image $image + ) { + $this->data = $this->read(); + $this->timestamp = $this->parseTimestamp(); + $this->exposure = $this->data['ExposureTime'] ?? null; + $this->iso = $this->data['ISOSpeedRatings'] ?? null; + $this->focalLength = $this->parseFocalLength(); + $this->aperture = $this->computed()['ApertureFNumber'] ?? null; + $this->isColor = V::accepted($this->computed()['IsColor'] ?? null); } /** @@ -85,11 +54,7 @@ class Exif */ public function camera(): Camera { - if ($this->camera !== null) { - return $this->camera; - } - - return $this->camera = new Camera($this->data); + return $this->camera ??= new Camera($this->data); } /** @@ -97,11 +62,7 @@ class Exif */ public function location(): Location { - if ($this->location !== null) { - return $this->location; - } - - return $this->location = new Location($this->data); + return $this->location ??= new Location($this->data); } /** @@ -183,19 +144,6 @@ class Exif return $this->data['COMPUTED'] ?? []; } - /** - * Parses and stores all relevant exif data - */ - protected function parse(): void - { - $this->timestamp = $this->parseTimestamp(); - $this->exposure = $this->data['ExposureTime'] ?? null; - $this->iso = $this->data['ISOSpeedRatings'] ?? null; - $this->focalLength = $this->parseFocalLength(); - $this->aperture = $this->computed()['ApertureFNumber'] ?? null; - $this->isColor = V::accepted($this->computed()['IsColor'] ?? null); - } - /** * Return the timestamp when the picture has been taken */ @@ -240,6 +188,7 @@ class Exif /** * Improved `var_dump` output + * @codeCoverageIgnore */ public function __debugInfo(): array { diff --git a/kirby/src/Image/Focus.php b/kirby/src/Image/Focus.php new file mode 100644 index 0000000..da1dc73 --- /dev/null +++ b/kirby/src/Image/Focus.php @@ -0,0 +1,110 @@ + + * @link https://getkirby.com + * @copyright Bastian Allgeier + * @license https://opensource.org/licenses/MIT + */ +class Focus +{ + /** + * Generates crop coordinates based on focal point + */ + public static function coords( + string $crop, + int $sourceWidth, + int $sourceHeight, + int $width, + int $height + ): array|null { + [$x, $y] = static::parse($crop); + + // determine aspect ratios + $ratioSource = static::ratio($sourceWidth, $sourceHeight); + $ratioThumb = static::ratio($width, $height); + + // no cropping necessary + if ($ratioSource == $ratioThumb) { + return null; + } + + // defaults + $width = $sourceWidth; + $height = $sourceHeight; + + if ($ratioThumb > $ratioSource) { + $height = $sourceWidth / $ratioThumb; + } else { + $width = $sourceHeight * $ratioThumb; + } + + // calculate focus for original image + $x = $sourceWidth * $x; + $y = $sourceHeight * $y; + + $x1 = max(0, $x - $width / 2); + $y1 = max(0, $y - $height / 2); + + // off canvas? + if ($x1 + $width > $sourceWidth) { + $x1 = $sourceWidth - $width; + } + + if ($y1 + $height > $sourceHeight) { + $y1 = $sourceHeight - $height; + } + + return [ + 'x1' => (int)floor($x1), + 'y1' => (int)floor($y1), + 'x2' => (int)floor($x1 + $width), + 'y2' => (int)floor($y1 + $height), + 'width' => (int)floor($width), + 'height' => (int)floor($height), + ]; + } + + public static function isFocalPoint(string $value): bool + { + return Str::contains($value, '%') === true; + } + + /** + * Transforms the focal point's string value (from content field) + * to a [x, y] array (values 0.0-1.0) + */ + public static function parse(string $value): array + { + // support for former Focus plugin + if (Str::startsWith($value, '{') === true) { + $focus = json_decode($value); + return [$focus->x, $focus->y]; + } + + preg_match_all("/(\d{1,3}\.?\d*)[%|,|\s]*/", $value, $points); + + return A::map( + $points[1], + function ($point) { + $point = (float)$point; + $point = $point > 1 ? $point / 100 : $point; + return round($point, 3); + } + ); + } + + /** + * Calculates the image ratio + */ + public static function ratio(int $width, int $height): float + { + return $height !== 0 ? $width / $height : 0; + } +} diff --git a/kirby/src/Image/Image.php b/kirby/src/Image/Image.php index 83c0a7b..f012a1e 100644 --- a/kirby/src/Image/Image.php +++ b/kirby/src/Image/Image.php @@ -2,7 +2,8 @@ namespace Kirby\Image; -use Kirby\Cms\Content; +use Kirby\Cms\FileVersion; +use Kirby\Content\Content; use Kirby\Exception\LogicException; use Kirby\Filesystem\File; use Kirby\Toolkit\Html; @@ -28,6 +29,7 @@ class Image extends File protected Dimensions|null $dimensions = null; public static array $resizableTypes = [ + 'avif', 'jpg', 'jpeg', 'gif', @@ -76,10 +78,11 @@ class Image extends File } if (in_array($this->mime(), [ + 'image/avif', + 'image/gif', 'image/jpeg', 'image/jp2', 'image/png', - 'image/gif', 'image/webp' ])) { return $this->dimensions = Dimensions::forImage($this->root); @@ -113,15 +116,20 @@ class Image extends File */ public function html(array $attr = []): string { + $model = match (true) { + $this->model instanceof FileVersion => $this->model->original(), + default => $this->model + }; + // if no alt text explicitly provided, // try to infer from model content file if ( - $this->model !== null && - method_exists($this->model, 'content') === true && - $this->model->content() instanceof Content && - $this->model->content()->get('alt')->isNotEmpty() === true + $model !== null && + method_exists($model, 'content') === true && + $model->content() instanceof Content && + $model->content()->get('alt')->isNotEmpty() === true ) { - $attr['alt'] ??= $this->model->content()->get('alt')->value(); + $attr['alt'] ??= $model->content()->get('alt')->value(); } if ($url = $this->url()) { diff --git a/kirby/src/Image/Location.php b/kirby/src/Image/Location.php index 4b60c6a..0eddb68 100644 --- a/kirby/src/Image/Location.php +++ b/kirby/src/Image/Location.php @@ -24,13 +24,20 @@ class Location */ public function __construct(array $exif) { - if (isset($exif['GPSLatitude']) === true && + if ( + isset($exif['GPSLatitude']) === true && isset($exif['GPSLatitudeRef']) === true && isset($exif['GPSLongitude']) === true && isset($exif['GPSLongitudeRef']) === true ) { - $this->lat = $this->gps($exif['GPSLatitude'], $exif['GPSLatitudeRef']); - $this->lng = $this->gps($exif['GPSLongitude'], $exif['GPSLongitudeRef']); + $this->lat = $this->gps( + $exif['GPSLatitude'], + $exif['GPSLatitudeRef'] + ); + $this->lng = $this->gps( + $exif['GPSLongitude'], + $exif['GPSLongitudeRef'] + ); } } @@ -95,11 +102,12 @@ class Location */ public function __toString(): string { - return trim(trim($this->lat() . ', ' . $this->lng(), ',')); + return trim($this->lat() . ', ' . $this->lng(), ','); } /** * Improved `var_dump` output + * @codeCoverageIgnore */ public function __debugInfo(): array { diff --git a/kirby/src/Image/QrCode.php b/kirby/src/Image/QrCode.php new file mode 100644 index 0000000..d675f41 --- /dev/null +++ b/kirby/src/Image/QrCode.php @@ -0,0 +1,1611 @@ +, + * Lukas Bestle + * @link https://getkirby.com + * @copyright Bastian Allgeier + * @license https://opensource.org/licenses/MIT + * + * QR Code® is a registered trademark of DENSO WAVE INCORPORATED. + * + * The code of this class is based on: + * https://github.com/psyon/php-qrcode + * + * qrcode.php - Generate QR Codes. MIT license. + * + * Copyright for portions of this project are held by Kreative Software, 2016-2018. + * All other copyright for the project are held by Donald Becker, 2019 + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +class QrCode +{ + public function __construct(public string $data) + { + } + + /** + * Returns the QR code as a PNG data URI + * + * @param int|null $size Image width/height in pixels, defaults to a size per module of 4x4 + * @param string $color Foreground color in hex format + * @param string $back Background color in hex format + * @param int $border Border size in number of modules + */ + public function toDataUri( + int|null $size = null, + string $color = '#000000', + string $back = '#ffffff', + int $border = 4 + ): string { + $image = $this->toImage($size, $color, $back, $border); + + ob_start(); + imagepng($image); + imagedestroy($image); + $data = ob_get_contents(); + ob_end_clean(); + + return 'data:image/png;base64,' . base64_encode($data); + } + + /** + * Returns the QR code as a GdImage object + * + * @param int|null $size Image width/height in pixels, defaults to a size per module of 4x4 + * @param string $color Foreground color in hex format + * @param string $back Background color in hex format + * @param int $border Border size in number of modules + */ + public function toImage( + int|null $size = null, + string $color = '#000000', + string $back = '#ffffff', + int $border = 4 + ): GdImage { + // get code and size measurements + $code = $this->encode($border); + [$width, $height] = $this->measure($code); + $size ??= ceil($width * 4); + $ws = $size / $width; + $hs = $size / $height; + + // create image baseplate + $image = imagecreatetruecolor($size, $size); + + $allocateColor = function (string $hex) use ($image) { + $hex = preg_replace('/[^0-9A-Fa-f]/', '', $hex); + $r = hexdec(substr($hex, 0, 2)); + $g = hexdec(substr($hex, 2, 2)); + $b = hexdec(substr($hex, 4, 2)); + return imagecolorallocate($image, $r, $g, $b); + }; + + $back = $allocateColor($back); + $color = $allocateColor($color); + imagefill($image, 0, 0, $back); + + // paint square for each module + $this->eachModuleGroup( + $code, + fn ($x, $y, $width, $height) => imagefilledrectangle( + $image, + floor($x * $ws), + floor($y * $hs), + floor($x * $ws + $ws * $width) - 1, + floor($y * $hs + $hs * $height) - 1, + $color + ) + ); + + return $image; + } + + /** + * Returns the QR code as `` element + * + * @param int|string|null $size Optional CSS width of the `` element + * @param string $color Foreground color in hex format + * @param string $back Background color in hex format + * @param int $border Border size in number of modules + */ + public function toSvg( + int|string|null $size = null, + string $color = '#000000', + string $back = '#ffffff', + int $border = 4 + ): string { + $code = $this->encode($border); + [$vbw, $vbh] = $this->measure($code); + + $modules = $this->eachModuleGroup( + $code, + fn ($x, $y, $width, $height) => 'M' . $x . ',' . $y . 'h' . $width . 'v' . $height . 'h-' . $width . 'z' + ); + + $size = $size ? ' style="width: ' . $size . '"' : ''; + + return '' . + '' . + '' . + ''; + } + + public function __toString(): string + { + return $this->toSvg(); + } + + /** + * Saves the QR code to a file. + * Supported formats: gif, jpg, jpeg, png, svg, webp + * + * @param string $file Path to the output file with one of the supported file extensions + * @param int|string|null $size Optional image width/height in pixels (defaults to a size per module of 4x4) or CSS width of the `` element + * @param string $color Foreground color in hex format + * @param string $back Background color in hex format + * @param int $border Border size in number of modules + */ + public function write( + string $file, + int|string|null $size = null, + string $color = '#000000', + string $back = '#ffffff', + int $border = 4 + ): void { + $format = F::extension($file); + $args = [$size, $color, $back, $border]; + + match ($format) { + 'gif' => imagegif($this->toImage(...$args), $file), + 'jpg', + 'jpeg' => imagejpeg($this->toImage(...$args), $file), + 'png' => imagepng($this->toImage(...$args), $file), + 'svg' => F::write($file, $this->toSvg(...$args)), + 'webp' => imagewebp($this->toImage(...$args), $file), + default => throw new InvalidArgumentException('Cannot write QR code as ' . $format) + }; + } + + protected function applyMask(array $matrix, int $size, int $mask): array + { + for ($i = 0; $i < $size; $i++) { + for ($j = 0; $j < $size; $j++) { + if ($matrix[$i][$j] >= 4 && $this->mask($mask, $i, $j)) { + $matrix[$i][$j] ^= 1; + } + } + } + + return $matrix; + } + + protected function applyBestMask(array $matrix, int $size): array + { + $mask = 0; + $mmatrix = $this->applyMask($matrix, $size, $mask); + $penalty = $this->penalty($mmatrix, $size); + + for ($tmask = 1; $tmask < 8; $tmask++) { + $tmatrix = $this->applyMask($matrix, $size, $tmask); + $tpenalty = $this->penalty($tmatrix, $size); + + if ($tpenalty < $penalty) { + $mask = $tmask; + $mmatrix = $tmatrix; + $penalty = $tpenalty; + } + } + + return [$mask, $mmatrix]; + } + + protected function createMatrix(int $version, array $data): array + { + $size = $version * 4 + 17; + $matrix = []; + $row = array_fill(0, $size, 0); + + for ($i = 0; $i < $size; $i++) { + $matrix[] = $row; + } + + // finder patterns + for ($i = 0; $i < 8; $i++) { + for ($j = 0; $j < 8; $j++) { + $m = (($i == 7 || $j == 7) ? 2 : + (($i == 0 || $j == 0 || $i == 6 || $j == 6) ? 3 : + (($i == 1 || $j == 1 || $i == 5 || $j == 5) ? 2 : 3))); + $matrix[$i][$j] = $m; + $matrix[$size - $i - 1][$j] = $m; + $matrix[$i][$size - $j - 1] = $m; + } + } + + // alignment patterns + if ($version >= 2) { + $alignment = static::ALIGNMENT_PATTERNS[$version - 2]; + + foreach ($alignment as $i) { + foreach ($alignment as $j) { + if (!$matrix[$i][$j]) { + for ($ii = -2; $ii <= 2; $ii++) { + for ($jj = -2; $jj <= 2; $jj++) { + $m = (max(abs($ii), abs($jj)) & 1) ^ 3; + $matrix[$i + $ii][$j + $jj] = $m; + } + } + } + } + } + } + + // timing patterns + for ($i = $size - 9; $i >= 8; $i--) { + $matrix[$i][6] = ($i & 1) ^ 3; + $matrix[6][$i] = ($i & 1) ^ 3; + } + + // dark module – such an ominous name for such an innocuous thing + $matrix[$size - 8][8] = 3; + + // format information area + for ($i = 0; $i <= 8; $i++) { + if (!$matrix[$i][8]) { + $matrix[$i][8] = 1; + } + if (!$matrix[8][$i]) { + $matrix[8][$i] = 1; + } + if ($i && !$matrix[$size - $i][8]) { + $matrix[$size - $i][8] = 1; + } + if ($i && !$matrix[8][$size - $i]) { + $matrix[8][$size - $i] = 1; + } + } + + // version information area + if ($version >= 7) { + for ($i = 9; $i < 12; $i++) { + for ($j = 0; $j < 6; $j++) { + $matrix[$size - $i][$j] = 1; + $matrix[$j][$size - $i] = 1; + } + } + } + + // data + $col = $size - 1; + $row = $size - 1; + $dir = -1; + $offset = 0; + $length = count($data); + + while ($col > 0 && $offset < $length) { + if (!$matrix[$row][$col]) { + $matrix[$row][$col] = $data[$offset] ? 5 : 4; + $offset++; + } + if (!$matrix[$row][$col - 1]) { + $matrix[$row][$col - 1] = $data[$offset] ? 5 : 4; + $offset++; + } + $row += $dir; + if ($row < 0 || $row >= $size) { + $dir = -$dir; + $row += $dir; + $col -= 2; + + if ($col == 6) { + $col--; + } + } + } + + return [$size, $matrix]; + } + + /** + * Loops over every row and column, finds all modules that can + * be grouped as rectangle (starting at the top left corner) + * and applies the given action to each active module group + */ + protected function eachModuleGroup(array $code, Closure $action): array + { + $result = []; + $xStart = $code['q'][3]; + $yStart = $code['q'][0]; + + // generate empty matrix to track what modules have been covered + $covered = array_fill(0, count($code['bits']), array_fill(0, count($code['bits'][0]), 0)); + + foreach ($code['bits'] as $by => $row) { + foreach ($row as $bx => $module) { + // skip if module is inactive or already covered + if ($module === 0 || $covered[$by][$bx] === 1) { + continue; + } + + $width = 0; + $height = 0; + + $rowLength = count($row); + $colLength = count($code['bits']); + + // extend to the right as long as the modules are active + // and use this to determine the width of the group + for ($x = $bx; $x < $rowLength; $x++) { + if ($row[$x] === 0) { + break; + } + $width++; + $covered[$by][$x] = 1; + } + + // extend downwards as long as all the modules + // at the same width range are active; + // use this to determine the height of the group + for ($y = $by; $y < $colLength; $y++) { + $below = array_slice($code['bits'][$y], $bx, $width); + + // if the sum is less than the width, + // there is at least one inactive module + if (array_sum($below) < $width) { + break; + } + + $height++; + + for ($x = $bx; $x < $bx + $width; $x++) { + $covered[$y][$x] = 1; + } + } + + $result[] = $action( + $xStart + $bx, + $yStart + $by, + $width, + $height + ); + } + } + + return $result; + } + + protected function encode(int $q = 4): array + { + [$data, $version, $ecl, $ec] = $this->encodeData(); + $data = $this->encodeErrorCorrection($data, $ec, $version); + [$size, $mtx] = $this->createMatrix($version, $data); + [$mask, $mtx] = $this->applyBestMask($mtx, $size); + $mtx = $this->finalizeMatrix($mtx, $size, $ecl, $mask, $version); + + return [ + 'q' => [$q, $q, $q, $q], + 'size' => [$size, $size], + 'bits' => $mtx + ]; + } + + protected function encodeData(): array + { + $mode = $this->mode(); + [$version, $ecl] = $this->version($mode); + + $group = match (true) { + $version >= 27 => 2, + $version >= 10 => 1, + default => 0 + }; + + $ec = static::EC_PARAMS[($version - 1) * 4 + $ecl]; + + // don't cut off mid-character if exceeding capacity + $max_chars = static::CAPACITY[$version - 1][$ecl][$mode]; + + if ($mode == 3) { + $max_chars <<= 1; + } + + $data = substr($this->data, 0, $max_chars); + + // convert from character level to bit level + $code = match ($mode) { + 0 => $this->encodeNumeric($data, $group), + 1 => $this->encodeAlphanum($data, $group), + 2 => $this->encodeBinary($data, $group), + default => throw new LogicException('Invalid QR mode') // @codeCoverageIgnore + }; + + $code = array_merge($code, array_fill(0, 4, 0)); + + if ($remainder = count($code) % 8) { + $code = array_merge($code, array_fill(0, 8 - $remainder, 0)); + } + + // convert from bit level to byte level + $data = []; + + for ($i = 0, $n = count($code); $i < $n; $i += 8) { + $byte = 0; + + if ($code[$i + 0]) { + $byte |= 0x80; + } + if ($code[$i + 1]) { + $byte |= 0x40; + } + if ($code[$i + 2]) { + $byte |= 0x20; + } + if ($code[$i + 3]) { + $byte |= 0x10; + } + if ($code[$i + 4]) { + $byte |= 0x08; + } + if ($code[$i + 5]) { + $byte |= 0x04; + } + if ($code[$i + 6]) { + $byte |= 0x02; + } + if ($code[$i + 7]) { + $byte |= 0x01; + } + + $data[] = $byte; + } + + for ( + $i = count($data), + $a = 1, + $n = $ec[0]; + $i < $n; + $i++, + $a ^= 1 + ) { + $data[] = $a ? 236 : 17; + } + + return [ + $data, + $version, + $ecl, + $ec + ]; + } + + protected function encodeNumeric($data, $version_group): array + { + $code = [0, 0, 0, 1]; + $length = strlen($data); + + switch ($version_group) { + case 2: // 27 - 40 + $code[] = $length & 0x2000; + $code[] = $length & 0x1000; + // no break + case 1: // 10 - 26 + $code[] = $length & 0x0800; + $code[] = $length & 0x0400; + // no break + case 0: // 1 - 9 + $code[] = $length & 0x0200; + $code[] = $length & 0x0100; + $code[] = $length & 0x0080; + $code[] = $length & 0x0040; + $code[] = $length & 0x0020; + $code[] = $length & 0x0010; + $code[] = $length & 0x0008; + $code[] = $length & 0x0004; + $code[] = $length & 0x0002; + $code[] = $length & 0x0001; + } + for ($i = 0; $i < $length; $i += 3) { + $group = substr($data, $i, 3); + switch (strlen($group)) { + case 3: + $code[] = $group & 0x200; + $code[] = $group & 0x100; + $code[] = $group & 0x080; + // no break + case 2: + $code[] = $group & 0x040; + $code[] = $group & 0x020; + $code[] = $group & 0x010; + // no break + case 1: + $code[] = $group & 0x008; + $code[] = $group & 0x004; + $code[] = $group & 0x002; + $code[] = $group & 0x001; + } + } + return $code; + } + + protected function encodeAlphanum($data, $version_group): array + { + $alphabet = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:'; + $code = [0, 0, 1, 0]; + $length = strlen($data); + switch ($version_group) { + case 2: // 27 - 40 + $code[] = $length & 0x1000; + $code[] = $length & 0x0800; + // no break + case 1: // 10 - 26 + $code[] = $length & 0x0400; + $code[] = $length & 0x0200; + // no break + case 0: // 1 - 9 + $code[] = $length & 0x0100; + $code[] = $length & 0x0080; + $code[] = $length & 0x0040; + $code[] = $length & 0x0020; + $code[] = $length & 0x0010; + $code[] = $length & 0x0008; + $code[] = $length & 0x0004; + $code[] = $length & 0x0002; + $code[] = $length & 0x0001; + } + for ($i = 0; $i < $length; $i += 2) { + $group = substr($data, $i, 2); + if (strlen($group) > 1) { + $c1 = strpos($alphabet, substr($group, 0, 1)); + $c2 = strpos($alphabet, substr($group, 1, 1)); + $ch = $c1 * 45 + $c2; + $code[] = $ch & 0x400; + $code[] = $ch & 0x200; + $code[] = $ch & 0x100; + $code[] = $ch & 0x080; + $code[] = $ch & 0x040; + $code[] = $ch & 0x020; + $code[] = $ch & 0x010; + $code[] = $ch & 0x008; + $code[] = $ch & 0x004; + $code[] = $ch & 0x002; + $code[] = $ch & 0x001; + } else { + $ch = strpos($alphabet, $group); + $code[] = $ch & 0x020; + $code[] = $ch & 0x010; + $code[] = $ch & 0x008; + $code[] = $ch & 0x004; + $code[] = $ch & 0x002; + $code[] = $ch & 0x001; + } + } + return $code; + } + + protected function encodeBinary(string $data, int $version_group): array + { + $code = [0, 1, 0, 0]; + $length = strlen($data); + + switch ($version_group) { + case 2: // 27 - 40 + case 1: // 10 - 26 + $code[] = $length & 0x8000; + $code[] = $length & 0x4000; + $code[] = $length & 0x2000; + $code[] = $length & 0x1000; + $code[] = $length & 0x0800; + $code[] = $length & 0x0400; + $code[] = $length & 0x0200; + $code[] = $length & 0x0100; + // no break + case 0: // 1 - 9 + $code[] = $length & 0x0080; + $code[] = $length & 0x0040; + $code[] = $length & 0x0020; + $code[] = $length & 0x0010; + $code[] = $length & 0x0008; + $code[] = $length & 0x0004; + $code[] = $length & 0x0002; + $code[] = $length & 0x0001; + } + + for ($i = 0; $i < $length; $i++) { + $ch = ord(substr($data, $i, 1)); + $code[] = $ch & 0x80; + $code[] = $ch & 0x40; + $code[] = $ch & 0x20; + $code[] = $ch & 0x10; + $code[] = $ch & 0x08; + $code[] = $ch & 0x04; + $code[] = $ch & 0x02; + $code[] = $ch & 0x01; + } + + return $code; + } + + protected function encodeErrorCorrection( + array $data, + array $ec_params, + int $version + ): array { + $blocks = $this->errorCorrectionSplit($data, $ec_params); + $ec_blocks = []; + + for ($i = 0, $n = count($blocks); $i < $n; $i++) { + $ec_blocks[] = $this->errorCorrectionDivide($blocks[$i], $ec_params); + } + + $data = $this->errorCorrectionInterleave($blocks); + $ec_data = $this->errorCorrectionInterleave($ec_blocks); + $code = []; + + foreach ($data as $ch) { + $code[] = $ch & 0x80; + $code[] = $ch & 0x40; + $code[] = $ch & 0x20; + $code[] = $ch & 0x10; + $code[] = $ch & 0x08; + $code[] = $ch & 0x04; + $code[] = $ch & 0x02; + $code[] = $ch & 0x01; + } + foreach ($ec_data as $ch) { + $code[] = $ch & 0x80; + $code[] = $ch & 0x40; + $code[] = $ch & 0x20; + $code[] = $ch & 0x10; + $code[] = $ch & 0x08; + $code[] = $ch & 0x04; + $code[] = $ch & 0x02; + $code[] = $ch & 0x01; + } + for ($n = static::REMAINER_BITS[$version - 1]; $n > 0; $n--) { + $code[] = 0; + } + + return $code; + } + + protected function errorCorrectionSplit(array $data, array $ec): array + { + $blocks = []; + $offset = 0; + + for ($i = $ec[2], $length = $ec[3]; $i > 0; $i--) { + $blocks[] = array_slice($data, $offset, $length); + $offset += $length; + } + for ($i = $ec[4], $length = $ec[5]; $i > 0; $i--) { + $blocks[] = array_slice($data, $offset, $length); + $offset += $length; + } + + return $blocks; + } + + protected function errorCorrectionDivide(array $data, array $ec): array + { + $num_data = count($data); + $num_error = $ec[1]; + $generator = static::EC_POLYNOMIALS[$num_error]; + $message = $data; + + for ($i = 0; $i < $num_error; $i++) { + $message[] = 0; + } + + for ($i = 0; $i < $num_data; $i++) { + if ($message[$i]) { + $leadterm = static::LOG[$message[$i]]; + + for ($j = 0; $j <= $num_error; $j++) { + $term = ($generator[$j] + $leadterm) % 255; + $message[$i + $j] ^= static::EXP[$term]; + } + } + } + + return array_slice($message, $num_data, $num_error); + } + + protected function errorCorrectionInterleave(array $blocks): array + { + $data = []; + $num_blocks = count($blocks); + + for ($offset = 0; true; $offset++) { + $break = true; + + for ($i = 0; $i < $num_blocks; $i++) { + if (isset($blocks[$i][$offset]) === true) { + $data[] = $blocks[$i][$offset]; + $break = false; + } + } + + if ($break) { + break; + } + } + + return $data; + } + + protected function finalizeMatrix( + array $matrix, + int $size, + int $ecl, + int $mask, + int $version + ): array { + // Format info + $format = static::FORMAT_INFO[$ecl * 8 + $mask]; + $matrix[8][0] = $format[0]; + $matrix[8][1] = $format[1]; + $matrix[8][2] = $format[2]; + $matrix[8][3] = $format[3]; + $matrix[8][4] = $format[4]; + $matrix[8][5] = $format[5]; + $matrix[8][7] = $format[6]; + $matrix[8][8] = $format[7]; + $matrix[7][8] = $format[8]; + $matrix[5][8] = $format[9]; + $matrix[4][8] = $format[10]; + $matrix[3][8] = $format[11]; + $matrix[2][8] = $format[12]; + $matrix[1][8] = $format[13]; + $matrix[0][8] = $format[14]; + $matrix[$size - 1][8] = $format[0]; + $matrix[$size - 2][8] = $format[1]; + $matrix[$size - 3][8] = $format[2]; + $matrix[$size - 4][8] = $format[3]; + $matrix[$size - 5][8] = $format[4]; + $matrix[$size - 6][8] = $format[5]; + $matrix[$size - 7][8] = $format[6]; + $matrix[8][$size - 8] = $format[7]; + $matrix[8][$size - 7] = $format[8]; + $matrix[8][$size - 6] = $format[9]; + $matrix[8][$size - 5] = $format[10]; + $matrix[8][$size - 4] = $format[11]; + $matrix[8][$size - 3] = $format[12]; + $matrix[8][$size - 2] = $format[13]; + $matrix[8][$size - 1] = $format[14]; + + // version info + if ($version >= 7) { + $version = static::VERSION_INFO[$version - 7]; + + for ($i = 0; $i < 18; $i++) { + $r = $size - 9 - ($i % 3); + $c = 5 - floor($i / 3); + $matrix[$r][$c] = $version[$i]; + $matrix[$c][$r] = $version[$i]; + } + } + + // patterns and data + for ($i = 0; $i < $size; $i++) { + for ($j = 0; $j < $size; $j++) { + $matrix[$i][$j] &= 1; + } + } + + return $matrix; + } + + protected function mask(int $mask, int $row, int $column): int + { + return match ($mask) { + 0 => !(($row + $column) % 2), + 1 => !($row % 2), + 2 => !($column % 3), + 3 => !(($row + $column) % 3), + 4 => !((floor($row / 2) + floor($column / 3)) % 2), + 5 => !(((($row * $column) % 2) + (($row * $column) % 3))), + 6 => !(((($row * $column) % 2) + (($row * $column) % 3)) % 2), + 7 => !(((($row + $column) % 2) + (($row * $column) % 3)) % 2), + default => throw new LogicException('Invalid QR mask') // @codeCoverageIgnore + }; + } + + /** + * Returns width and height based on the + * generated modules and quiet zone + */ + protected function measure($code): array + { + return [ + $code['q'][3] + $code['size'][0] + $code['q'][1], + $code['q'][0] + $code['size'][1] + $code['q'][2] + ]; + } + + /** + * Detect what encoding mode (numeric, alphanumeric, binary) + * can be used + */ + protected function mode(): int + { + // numeric + if (preg_match('/^[0-9]*$/', $this->data)) { + return 0; + } + + // alphanumeric + if (preg_match('/^[0-9A-Z .\/:$%*+-]*$/', $this->data)) { + return 1; + } + + return 2; + } + + protected function penalty(array &$matrix, int $size): int + { + $score = $this->penalty1($matrix, $size); + $score += $this->penalty2($matrix, $size); + $score += $this->penalty3($matrix, $size); + $score += $this->penalty4($matrix, $size); + return $score; + } + + protected function penalty1(array &$matrix, int $size): int + { + $score = 0; + + for ($i = 0; $i < $size; $i++) { + $rowvalue = 0; + $rowcount = 0; + $colvalue = 0; + $colcount = 0; + + for ($j = 0; $j < $size; $j++) { + $rv = ($matrix[$i][$j] == 5 || $matrix[$i][$j] == 3) ? 1 : 0; + $cv = ($matrix[$j][$i] == 5 || $matrix[$j][$i] == 3) ? 1 : 0; + + if ($rv == $rowvalue) { + $rowcount++; + } else { + if ($rowcount >= 5) { + $score += $rowcount - 2; + } + $rowvalue = $rv; + $rowcount = 1; + } + + if ($cv == $colvalue) { + $colcount++; + } else { + if ($colcount >= 5) { + $score += $colcount - 2; + } + $colvalue = $cv; + $colcount = 1; + } + } + + if ($rowcount >= 5) { + $score += $rowcount - 2; + } + if ($colcount >= 5) { + $score += $colcount - 2; + } + } + + return $score; + } + + protected function penalty2(array &$matrix, int $size): int + { + $score = 0; + + for ($i = 1; $i < $size; $i++) { + for ($j = 1; $j < $size; $j++) { + $v1 = $matrix[$i - 1][$j - 1]; + $v2 = $matrix[$i - 1][$j ]; + $v3 = $matrix[$i ][$j - 1]; + $v4 = $matrix[$i ][$j ]; + $v1 = ($v1 == 5 || $v1 == 3) ? 1 : 0; + $v2 = ($v2 == 5 || $v2 == 3) ? 1 : 0; + $v3 = ($v3 == 5 || $v3 == 3) ? 1 : 0; + $v4 = ($v4 == 5 || $v4 == 3) ? 1 : 0; + + if ($v1 == $v2 && $v2 == $v3 && $v3 == $v4) { + $score += 3; + } + } + } + + return $score; + } + + protected function penalty3(array &$matrix, int $size): int + { + $score = 0; + + for ($i = 0; $i < $size; $i++) { + $rowvalue = 0; + $colvalue = 0; + + for ($j = 0; $j < 11; $j++) { + $rv = ($matrix[$i][$j] == 5 || $matrix[$i][$j] == 3) ? 1 : 0; + $cv = ($matrix[$j][$i] == 5 || $matrix[$j][$i] == 3) ? 1 : 0; + $rowvalue = (($rowvalue << 1) & 0x7FF) | $rv; + $colvalue = (($colvalue << 1) & 0x7FF) | $cv; + } + + if ($rowvalue == 0x5D0 || $rowvalue == 0x5D) { + $score += 40; + } + if ($colvalue == 0x5D0 || $colvalue == 0x5D) { + $score += 40; + } + + for ($j = 11; $j < $size; $j++) { + $rv = ($matrix[$i][$j] == 5 || $matrix[$i][$j] == 3) ? 1 : 0; + $cv = ($matrix[$j][$i] == 5 || $matrix[$j][$i] == 3) ? 1 : 0; + $rowvalue = (($rowvalue << 1) & 0x7FF) | $rv; + $colvalue = (($colvalue << 1) & 0x7FF) | $cv; + + if ($rowvalue == 0x5D0 || $rowvalue == 0x5D) { + $score += 40; + } + + if ($colvalue == 0x5D0 || $colvalue == 0x5D) { + $score += 40; + } + } + } + + return $score; + } + + protected function penalty4(array &$matrix, int $size): int + { + $dark = 0; + + for ($i = 0; $i < $size; $i++) { + for ($j = 0; $j < $size; $j++) { + if ($matrix[$i][$j] == 5 || $matrix[$i][$j] == 3) { + $dark++; + } + } + } + + $dark *= 20; + $dark /= $size * $size; + $a = abs(floor($dark) - 10); + $b = abs(ceil($dark) - 10); + return min($a, $b) * 10; + } + + /** + * Detect what version needs to be used by + * trying to maximize the error correction level + */ + protected function version(int $mode): array + { + $length = strlen($this->data); + + if ($mode == 3) { + $length >>= 1; + } + + $ecl = 0; + + // first try to find the minimum version + // that can contain the data + for ($version = 1; $version <= 40; $version++) { + if ($length <= static::CAPACITY[$version - 1][$ecl][$mode]) { + break; + } + } + + // with the version in place, try to raise + // the error correction level as long as + // the data still fits + for ($newEcl = 1; $newEcl <= 3; $newEcl++) { + if ($length <= static::CAPACITY[$version - 1][$newEcl][$mode]) { + $ecl = $newEcl; + } + } + + return [$version, $ecl]; + } + + /** + * maximum encodable characters = $qr_capacity [ (version - 1) ] + * [ (0 for L, 1 for M, 2 for Q, 3 for H) ] + * [ (0 for numeric, 1 for alpha, 2 for binary) ] + */ + protected const CAPACITY = [ + [ + [ 41, 25, 17], + [ 34, 20, 14], + [ 27, 16, 11], + [ 17, 10, 7] + ], + [ + [ 77, 47, 32], + [ 63, 38, 26], + [ 48, 29, 20], + [ 34, 20, 14] + ], + [ + [ 127, 77, 53], + [ 101, 61, 42], + [ 77, 47, 32], + [ 58, 35, 24] + ], + [ + [ 187, 114, 78], + [ 149, 90, 62], + [ 111, 67, 46], + [ 82, 50, 34] + ], + [ + [ 255, 154, 106], + [ 202, 122, 84], + [ 144, 87, 60], + [ 106, 64, 44] + ], + [ + [ 322, 195, 134], + [ 255, 154, 106], + [ 178, 108, 74], + [ 139, 84, 58] + ], + [ + [ 370, 224, 154], + [ 293, 178, 122], + [ 207, 125, 86], + [ 154, 93, 64] + ], + [ + [ 461, 279, 192], + [ 365, 221, 152], + [ 259, 157, 108], + [ 202, 122, 84] + ], + [ + [ 552, 335, 230], + [ 432, 262, 180], + [ 312, 189, 130], + [ 235, 143, 98]], + [ + [ 652, 395, 271], + [ 513, 311, 213], + [ 364, 221, 151], + [ 288, 174, 119] + ], + [ + [ 772, 468, 321], + [ 604, 366, 251], + [ 427, 259, 177], + [ 331, 200, 137] + ], + [ + [ 883, 535, 367], + [ 691, 419, 287], + [ 489, 296, 203], + [ 374, 227, 155] + ], + [ + [1022, 619, 425], + [ 796, 483, 331], + [ 580, 352, 241], + [ 427, 259, 177] + ], + [ + [1101, 667, 458], + [ 871, 528, 362], + [ 621, 376, 258], + [ 468, 283, 194] + ], + [ + [1250, 758, 520], + [ 991, 600, 412], + [ 703, 426, 292], + [ 530, 321, 220] + ], + [ + [1408, 854, 586], + [1082, 656, 450], + [ 775, 470, 322], + [ 602, 365, 250] + ], + [ + [1548, 938, 644], + [1212, 734, 504], + [ 876, 531, 364], + [ 674, 408, 280] + ], + [ + [1725, 1046, 718], + [1346, 816, 560], + [ 948, 574, 394], + [ 746, 452, 310] + ], + [ + [1903, 1153, 792], + [1500, 909, 624], + [1063, 644, 442], + [ 813, 493, 338] + ], + [ + [2061, 1249, 858], + [1600, 970, 666], + [1159, 702, 482], + [ 919, 557, 382] + ], + [ + [2232, 1352, 929], + [1708, 1035, 711], + [1224, 742, 509], + [ 969, 587, 403] + ], + [ + [2409, 1460, 1003], + [1872, 1134, 779], + [1358, 823, 565], + [1056, 640, 439] + ], + [ + [2620, 1588, 1091], + [2059, 1248, 857], + [1468, 890, 611], + [1108, 672, 461] + ], + [ + [2812, 1704, 1171], + [2188, 1326, 911], + [1588, 963, 661], + [1228, 744, 511] + ], + [ + [3057, 1853, 1273], + [2395, 1451, 997], + [1718, 1041, 715], + [1286, 779, 535] + ], + [ + [3283, 1990, 1367], + [2544, 1542, 1059], + [1804, 1094, 751], + [1425, 864, 593] + ], + [ + [3517, 2132, 1465], + [2701, 1637, 1125], + [1933, 1172, 805], + [1501, 910, 625] + ], + [ + [3669, 2223, 1528], + [2857, 1732, 1190], + [2085, 1263, 868], + [1581, 958, 658] + ], + [ + [3909, 2369, 1628], + [3035, 1839, 1264], + [2181, 1322, 908], + [1677, 1016, 698] + ], + [ + [4158, 2520, 1732], + [3289, 1994, 1370], + [2358, 1429, 982], + [1782, 1080, 742] + ], + [ + [4417, 2677, 1840], + [3486, 2113, 1452], + [2473, 1499, 1030], + [1897, 1150, 790] + ], + [ + [4686, 2840, 1952], + [3693, 2238, 1538], + [2670, 1618, 1112], + [2022, 1226, 842] + ], + [ + [4965, 3009, 2068], + [3909, 2369, 1628], + [2805, 1700, 1168], + [2157, 1307, 898] + ], + [ + [5253, 3183, 2188], + [4134, 2506, 1722], + [2949, 1787, 1228], + [2301, 1394, 958] + ], + [ + [5529, 3351, 2303], + [4343, 2632, 1809], + [3081, 1867, 1283], + [2361, 1431, 983] + ], + [ + [5836, 3537, 2431], + [4588, 2780, 1911], + [3244, 1966, 1351], + [2524, 1530, 1051] + ], + [ + [6153, 3729, 2563], + [4775, 2894, 1989], + [3417, 2071, 1423], + [2625, 1591, 1093] + ], + [ + [6479, 3927, 2699], + [5039, 3054, 2099], + [3599, 2181, 1499], + [2735, 1658, 1139] + ], + [ + [6743, 4087, 2809], + [5313, 3220, 2213], + [3791, 2298, 1579], + [2927, 1774, 1219] + ], + [ + [7089, 4296, 2953], + [5596, 3391, 2331], + [3993, 2420, 1663], + [3057, 1852, 1273] + ], + ]; + + /** + * $qr_ec_params[ + * 4 * (version - 1) + (0 for L, 1 for M, 2 for Q, 3 for H) + * ] = [ + * total number of data codewords, + * number of error correction codewords per block, + * number of blocks in first group, + * number of data codewords per block in first group, + * number of blocks in second group, + * number of data codewords per block in second group + * ); + */ + protected const EC_PARAMS = [ + [ 19, 7, 1, 19, 0, 0], + [ 16, 10, 1, 16, 0, 0], + [ 13, 13, 1, 13, 0, 0], + [ 9, 17, 1, 9, 0, 0], + [ 34, 10, 1, 34, 0, 0], + [ 28, 16, 1, 28, 0, 0], + [ 22, 22, 1, 22, 0, 0], + [ 16, 28, 1, 16, 0, 0], + [ 55, 15, 1, 55, 0, 0], + [ 44, 26, 1, 44, 0, 0], + [ 34, 18, 2, 17, 0, 0], + [ 26, 22, 2, 13, 0, 0], + [ 80, 20, 1, 80, 0, 0], + [ 64, 18, 2, 32, 0, 0], + [ 48, 26, 2, 24, 0, 0], + [ 36, 16, 4, 9, 0, 0], + [ 108, 26, 1, 108, 0, 0], + [ 86, 24, 2, 43, 0, 0], + [ 62, 18, 2, 15, 2, 16], + [ 46, 22, 2, 11, 2, 12], + [ 136, 18, 2, 68, 0, 0], + [ 108, 16, 4, 27, 0, 0], + [ 76, 24, 4, 19, 0, 0], + [ 60, 28, 4, 15, 0, 0], + [ 156, 20, 2, 78, 0, 0], + [ 124, 18, 4, 31, 0, 0], + [ 88, 18, 2, 14, 4, 15], + [ 66, 26, 4, 13, 1, 14], + [ 194, 24, 2, 97, 0, 0], + [ 154, 22, 2, 38, 2, 39], + [ 110, 22, 4, 18, 2, 19], + [ 86, 26, 4, 14, 2, 15], + [ 232, 30, 2, 116, 0, 0], + [ 182, 22, 3, 36, 2, 37], + [ 132, 20, 4, 16, 4, 17], + [ 100, 24, 4, 12, 4, 13], + [ 274, 18, 2, 68, 2, 69], + [ 216, 26, 4, 43, 1, 44], + [ 154, 24, 6, 19, 2, 20], + [ 122, 28, 6, 15, 2, 16], + [ 324, 20, 4, 81, 0, 0], + [ 254, 30, 1, 50, 4, 51], + [ 180, 28, 4, 22, 4, 23], + [ 140, 24, 3, 12, 8, 13], + [ 370, 24, 2, 92, 2, 93], + [ 290, 22, 6, 36, 2, 37], + [ 206, 26, 4, 20, 6, 21], + [ 158, 28, 7, 14, 4, 15], + [ 428, 26, 4, 107, 0, 0], + [ 334, 22, 8, 37, 1, 38], + [ 244, 24, 8, 20, 4, 21], + [ 180, 22, 12, 11, 4, 12], + [ 461, 30, 3, 115, 1, 116], + [ 365, 24, 4, 40, 5, 41], + [ 261, 20, 11, 16, 5, 17], + [ 197, 24, 11, 12, 5, 13], + [ 523, 22, 5, 87, 1, 88], + [ 415, 24, 5, 41, 5, 42], + [ 295, 30, 5, 24, 7, 25], + [ 223, 24, 11, 12, 7, 13], + [ 589, 24, 5, 98, 1, 99], + [ 453, 28, 7, 45, 3, 46], + [ 325, 24, 15, 19, 2, 20], + [ 253, 30, 3, 15, 13, 16], + [ 647, 28, 1, 107, 5, 108], + [ 507, 28, 10, 46, 1, 47], + [ 367, 28, 1, 22, 15, 23], + [ 283, 28, 2, 14, 17, 15], + [ 721, 30, 5, 120, 1, 121], + [ 563, 26, 9, 43, 4, 44], + [ 397, 28, 17, 22, 1, 23], + [ 313, 28, 2, 14, 19, 15], + [ 795, 28, 3, 113, 4, 114], + [ 627, 26, 3, 44, 11, 45], + [ 445, 26, 17, 21, 4, 22], + [ 341, 26, 9, 13, 16, 14], + [ 861, 28, 3, 107, 5, 108], + [ 669, 26, 3, 41, 13, 42], + [ 485, 30, 15, 24, 5, 25], + [ 385, 28, 15, 15, 10, 16], + [ 932, 28, 4, 116, 4, 117], + [ 714, 26, 17, 42, 0, 0], + [ 512, 28, 17, 22, 6, 23], + [ 406, 30, 19, 16, 6, 17], + [1006, 28, 2, 111, 7, 112], + [ 782, 28, 17, 46, 0, 0], + [ 568, 30, 7, 24, 16, 25], + [ 442, 24, 34, 13, 0, 0], + [1094, 30, 4, 121, 5, 122], + [ 860, 28, 4, 47, 14, 48], + [ 614, 30, 11, 24, 14, 25], + [ 464, 30, 16, 15, 14, 16], + [1174, 30, 6, 117, 4, 118], + [ 914, 28, 6, 45, 14, 46], + [ 664, 30, 11, 24, 16, 25], + [ 514, 30, 30, 16, 2, 17], + [1276, 26, 8, 106, 4, 107], + [1000, 28, 8, 47, 13, 48], + [ 718, 30, 7, 24, 22, 25], + [ 538, 30, 22, 15, 13, 16], + [1370, 28, 10, 114, 2, 115], + [1062, 28, 19, 46, 4, 47], + [ 754, 28, 28, 22, 6, 23], + [ 596, 30, 33, 16, 4, 17], + [1468, 30, 8, 122, 4, 123], + [1128, 28, 22, 45, 3, 46], + [ 808, 30, 8, 23, 26, 24], + [ 628, 30, 12, 15, 28, 16], + [1531, 30, 3, 117, 10, 118], + [1193, 28, 3, 45, 23, 46], + [ 871, 30, 4, 24, 31, 25], + [ 661, 30, 11, 15, 31, 16], + [1631, 30, 7, 116, 7, 117], + [1267, 28, 21, 45, 7, 46], + [ 911, 30, 1, 23, 37, 24], + [ 701, 30, 19, 15, 26, 16], + [1735, 30, 5, 115, 10, 116], + [1373, 28, 19, 47, 10, 48], + [ 985, 30, 15, 24, 25, 25], + [ 745, 30, 23, 15, 25, 16], + [1843, 30, 13, 115, 3, 116], + [1455, 28, 2, 46, 29, 47], + [1033, 30, 42, 24, 1, 25], + [ 793, 30, 23, 15, 28, 16], + [1955, 30, 17, 115, 0, 0], + [1541, 28, 10, 46, 23, 47], + [1115, 30, 10, 24, 35, 25], + [ 845, 30, 19, 15, 35, 16], + [2071, 30, 17, 115, 1, 116], + [1631, 28, 14, 46, 21, 47], + [1171, 30, 29, 24, 19, 25], + [ 901, 30, 11, 15, 46, 16], + [2191, 30, 13, 115, 6, 116], + [1725, 28, 14, 46, 23, 47], + [1231, 30, 44, 24, 7, 25], + [ 961, 30, 59, 16, 1, 17], + [2306, 30, 12, 121, 7, 122], + [1812, 28, 12, 47, 26, 48], + [1286, 30, 39, 24, 14, 25], + [ 986, 30, 22, 15, 41, 16], + [2434, 30, 6, 121, 14, 122], + [1914, 28, 6, 47, 34, 48], + [1354, 30, 46, 24, 10, 25], + [1054, 30, 2, 15, 64, 16], + [2566, 30, 17, 122, 4, 123], + [1992, 28, 29, 46, 14, 47], + [1426, 30, 49, 24, 10, 25], + [1096, 30, 24, 15, 46, 16], + [2702, 30, 4, 122, 18, 123], + [2102, 28, 13, 46, 32, 47], + [1502, 30, 48, 24, 14, 25], + [1142, 30, 42, 15, 32, 16], + [2812, 30, 20, 117, 4, 118], + [2216, 28, 40, 47, 7, 48], + [1582, 30, 43, 24, 22, 25], + [1222, 30, 10, 15, 67, 16], + [2956, 30, 19, 118, 6, 119], + [2334, 28, 18, 47, 31, 48], + [1666, 30, 34, 24, 34, 25], + [1276, 30, 20, 15, 61, 16], + ]; + + protected const EC_POLYNOMIALS = [ + 7 => [0, 87, 229, 146, 149, 238, 102, 21], + 10 => [0, 251, 67, 46, 61, 118, 70, 64, 94, 32, 45], + 13 => [0, 74, 152, 176, 100, 86, 100, 106, 104, 130, 218, 206, 140, 78], + 15 => [0, 8, 183, 61, 91, 202, 37, 51, 58, 58, 237, 140, 124, 5, 99, 105], + 16 => [0, 120, 104, 107, 109, 102, 161, 76, 3, 91, 191, 147, 169, 182, 194, 225, 120], + 17 => [0, 43, 139, 206, 78, 43, 239, 123, 206, 214, 147, 24, 99, 150, 39, 243, 163, 136], + 18 => [0, 215, 234, 158, 94, 184, 97, 118, 170, 79, 187, 152, 148, 252, 179, 5, 98, 96, 153], + 20 => [0, 17, 60, 79, 50, 61, 163, 26, 187, 202, 180, 221, 225, 83, 239, 156, 164, 212, 212, 188, 190], + 22 => [0, 210, 171, 247, 242, 93, 230, 14, 109, 221, 53, 200, 74, 8, 172, 98, 80, 219, 134, 160, 105, 165, 231], + 24 => [0, 229, 121, 135, 48, 211, 117, 251, 126, 159, 180, 169, 152, 192, 226, 228, 218, 111, 0, 117, 232, 87, 96, 227, 21], + 26 => [0, 173, 125, 158, 2, 103, 182, 118, 17, 145, 201, 111, 28, 165, 53, 161, 21, 245, 142, 13, 102, 48, 227, 153, 145, 218, 70], + 28 => [0, 168, 223, 200, 104, 224, 234, 108, 180, 110, 190, 195, 147, 205, 27, 232, 201, 21, 43, 245, 87, 42, 195, 212, 119, 242, 37, 9, 123], + 30 => [0, 41, 173, 145, 152, 216, 31, 179, 182, 50, 48, 110, 86, 239, 96, 222, 125, 42, 173, 226, 193, 224, 130, 156, 37, 251, 216, 238, 40, 192, 180], + ]; + + protected const LOG = [0, 0, 1, 25, 2, 50, 26, 198, 3, 223, 51, 238, 27, 104, 199, 75, 4, 100, 224, 14, 52, 141, 239, 129, 28, 193, 105, 248, 200, 8, 76, 113, 5, 138, 101, 47, 225, 36, 15, 33, 53, 147, 142, 218, 240, 18, 130, 69, 29, 181, 194, 125, 106, 39, 249, 185, 201, 154, 9, 120, 77, 228, 114, 166, 6, 191, 139, 98, 102, 221, 48, 253, 226, 152, 37, 179, 16, 145, 34, 136, 54, 208, 148, 206, 143, 150, 219, 189, 241, 210, 19, 92, 131, 56, 70, 64, 30, 66, 182, 163, 195, 72, 126, 110, 107, 58, 40, 84, 250, 133, 186, 61, 202, 94, 155, 159, 10, 21, 121, 43, 78, 212, 229, 172, 115, 243, 167, 87, 7, 112, 192, 247, 140, 128, 99, 13, 103, 74, 222, 237, 49, 197, 254, 24, 227, 165, 153, 119, 38, 184, 180, 124, 17, 68, 146, 217, 35, 32, 137, 46, 55, 63, 209, 91, 149, 188, 207, 205, 144, 135, 151, 178, 220, 252, 190, 97, 242, 86, 211, 171, 20, 42, 93, 158, 132, 60, 57, 83, 71, 109, 65, 162, 31, 45, 67, 216, 183, 123, 164, 118, 196, 23, 73, 236, 127, 12, 111, 246, 108, 161, 59, 82, 41, 157, 85, 170, 251, 96, 134, 177, 187, 204, 62, 90, 203, 89, 95, 176, 156, 169, 160, 81, 11, 245, 22, 235, 122, 117, 44, 215, 79, 174, 213, 233, 230, 231, 173, 232, 116, 214, 244, 234, 168, 80, 88, 175]; + + protected const EXP = [1, 2, 4, 8, 16, 32, 64, 128, 29, 58, 116, 232, 205, 135, 19, 38, 76, 152, 45, 90, 180, 117, 234, 201, 143, 3, 6, 12, 24, 48, 96, 192, 157, 39, 78, 156, 37, 74, 148, 53, 106, 212, 181, 119, 238, 193, 159, 35, 70, 140, 5, 10, 20, 40, 80, 160, 93, 186, 105, 210, 185, 111, 222, 161, 95, 190, 97, 194, 153, 47, 94, 188, 101, 202, 137, 15, 30, 60, 120, 240, 253, 231, 211, 187, 107, 214, 177, 127, 254, 225, 223, 163, 91, 182, 113, 226, 217, 175, 67, 134, 17, 34, 68, 136, 13, 26, 52, 104, 208, 189, 103, 206, 129, 31, 62, 124, 248, 237, 199, 147, 59, 118, 236, 197, 151, 51, 102, 204, 133, 23, 46, 92, 184, 109, 218, 169, 79, 158, 33, 66, 132, 21, 42, 84, 168, 77, 154, 41, 82, 164, 85, 170, 73, 146, 57, 114, 228, 213, 183, 115, 230, 209, 191, 99, 198, 145, 63, 126, 252, 229, 215, 179, 123, 246, 241, 255, 227, 219, 171, 75, 150, 49, 98, 196, 149, 55, 110, 220, 165, 87, 174, 65, 130, 25, 50, 100, 200, 141, 7, 14, 28, 56, 112, 224, 221, 167, 83, 166, 81, 162, 89, 178, 121, 242, 249, 239, 195, 155, 43, 86, 172, 69, 138, 9, 18, 36, 72, 144, 61, 122, 244, 245, 247, 243, 251, 235, 203, 139, 11, 22, 44, 88, 176, 125, 250, 233, 207, 131, 27, 54, 108, 216, 173, 71, 142, 1]; + + protected const REMAINER_BITS = [0, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0]; + + protected const ALIGNMENT_PATTERNS = [ + [6, 18], + [6, 22], + [6, 26], + [6, 30], + [6, 34], + [6, 22, 38], + [6, 24, 42], + [6, 26, 46], + [6, 28, 50], + [6, 30, 54], + [6, 32, 58], + [6, 34, 62], + [6, 26, 46, 66], + [6, 26, 48, 70], + [6, 26, 50, 74], + [6, 30, 54, 78], + [6, 30, 56, 82], + [6, 30, 58, 86], + [6, 34, 62, 90], + [6, 28, 50, 72, 94], + [6, 26, 50, 74, 98], + [6, 30, 54, 78, 102], + [6, 28, 54, 80, 106], + [6, 32, 58, 84, 110], + [6, 30, 58, 86, 114], + [6, 34, 62, 90, 118], + [6, 26, 50, 74, 98, 122], + [6, 30, 54, 78, 102, 126], + [6, 26, 52, 78, 104, 130], + [6, 30, 56, 82, 108, 134], + [6, 34, 60, 86, 112, 138], + [6, 30, 58, 86, 114, 142], + [6, 34, 62, 90, 118, 146], + [6, 30, 54, 78, 102, 126, 150], + [6, 24, 50, 76, 102, 128, 154], + [6, 28, 54, 80, 106, 132, 158], + [6, 32, 58, 84, 110, 136, 162], + [6, 26, 54, 82, 110, 138, 166], + [6, 30, 58, 86, 114, 142, 170], + ]; + + /** + * format info string = $qr_format_info[ + * (0 for L, 8 for M, 16 for Q, 24 for H) + mask + *]; + */ + protected const FORMAT_INFO = [ + [1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0], + [1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1], + [1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0], + [1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1], + [1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1], + [1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0], + [1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1], + [1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0], + [1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0], + [1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1], + [1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0], + [1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1], + [1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1], + [1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0], + [1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1], + [1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0], + [0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1], + [0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0], + [0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1], + [0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0], + [0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0], + [0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1], + [0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0], + [0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1], + [0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1], + [0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0], + [0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1], + [0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0], + [0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0], + [0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1], + [0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0], + [0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1] + ]; + + /** + * version info string = $qr_version_info[ (version - 7) ] + */ + protected const VERSION_INFO = [ + [0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0], + [0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0], + [0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1], + [0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1], + [0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0], + [0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0], + [0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1], + [0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1], + [0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0], + [0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0], + [0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1], + [0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1], + [0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0], + [0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0], + [0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1], + [0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1], + [0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0], + [0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0], + [0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1], + [0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1], + [0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0], + [0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0], + [0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1], + [0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1], + [0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0], + [1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1], + [1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0], + [1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0], + [1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1], + [1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1], + [1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0], + [1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0], + [1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1], + [1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1] + ]; +} diff --git a/kirby/src/Option/Option.php b/kirby/src/Option/Option.php index 3e58741..7de4b42 100644 --- a/kirby/src/Option/Option.php +++ b/kirby/src/Option/Option.php @@ -28,7 +28,7 @@ class Option $this->text ??= new NodeText(['en' => $this->value]); } - public static function factory(string|int|float|null|array $props): static + public static function factory(string|int|float|array|null $props): static { if (is_array($props) === false) { $props = ['value' => $props]; diff --git a/kirby/src/Option/OptionsApi.php b/kirby/src/Option/OptionsApi.php index 011d19e..3c83538 100644 --- a/kirby/src/Option/OptionsApi.php +++ b/kirby/src/Option/OptionsApi.php @@ -2,9 +2,9 @@ namespace Kirby\Option; -use Kirby\Cms\Field; use Kirby\Cms\ModelWithContent; use Kirby\Cms\Nest; +use Kirby\Content\Field; use Kirby\Data\Json; use Kirby\Exception\NotFoundException; use Kirby\Http\Remote; @@ -28,7 +28,9 @@ class OptionsApi extends OptionsProvider public string $url, public string|null $query = null, public string|null $text = null, - public string|null $value = null + public string|null $value = null, + public string|null $icon = null, + public string|null $info = null ) { } @@ -46,10 +48,12 @@ class OptionsApi extends OptionsProvider } return new static( - url: $props['url'], + url : $props['url'], query: $props['query'] ?? $props['fetch'] ?? null, - text: $props['text'] ?? null, - value: $props['value'] ?? null + text : $props['text'] ?? null, + value: $props['value'] ?? null, + icon : $props['icon'] ?? null, + info : $props['info'] ?? null ); } @@ -138,7 +142,10 @@ class OptionsApi extends OptionsProvider 'value' => $model->toString($this->value, ['item' => $item]), // text is only a raw string when using {< >} // or when the safe mode is explicitly disabled (select field) - 'text' => $model->$safeMethod($this->text, ['item' => $item]) + 'text' => $model->$safeMethod($this->text, ['item' => $item]), + // additional data + 'icon' => $this->icon !== null ? $model->toString($this->icon, ['item' => $item]) : null, + 'info' => $this->info !== null ? $model->$safeMethod($this->info, ['item' => $item]) : null ]; } diff --git a/kirby/src/Option/OptionsQuery.php b/kirby/src/Option/OptionsQuery.php index 62800ca..9ffdbaa 100644 --- a/kirby/src/Option/OptionsQuery.php +++ b/kirby/src/Option/OptionsQuery.php @@ -3,12 +3,12 @@ namespace Kirby\Option; use Kirby\Cms\Block; -use Kirby\Cms\Field; use Kirby\Cms\File; use Kirby\Cms\ModelWithContent; use Kirby\Cms\Page; use Kirby\Cms\StructureObject; use Kirby\Cms\User; +use Kirby\Content\Field; use Kirby\Exception\InvalidArgumentException; use Kirby\Toolkit\Collection; use Kirby\Toolkit\Obj; @@ -30,7 +30,9 @@ class OptionsQuery extends OptionsProvider public function __construct( public string $query, public string|null $text = null, - public string|null $value = null + public string|null $value = null, + public string|null $icon = null, + public string|null $info = null ) { } @@ -56,8 +58,10 @@ class OptionsQuery extends OptionsProvider return new static( query: $props['query'] ?? $props['fetch'], - text: $props['text'] ?? null, - value: $props['value'] ?? null + text : $props['text'] ?? null, + value: $props['value'] ?? null, + icon : $props['icon'] ?? null, + info : $props['info'] ?? null ); } @@ -159,7 +163,9 @@ class OptionsQuery extends OptionsProvider } if ($result instanceof Collection === false) { - throw new InvalidArgumentException('Invalid query result data: ' . get_class($result)); + $type = is_object($result) === true ? get_class($result) : gettype($result); + + throw new InvalidArgumentException('Invalid query result data: ' . $type); } // create options array @@ -176,7 +182,11 @@ class OptionsQuery extends OptionsProvider $safeMethod = $safeMode === true ? 'toSafeString' : 'toString'; $text = $model->$safeMethod($this->text ?? $text, $data); - return compact('text', 'value'); + // additional data + $icon = $this->icon !== null ? $model->toString($this->icon, $data) : null; + $info = $this->info !== null ? $model->$safeMethod($this->info, $data) : null; + + return compact('text', 'value', 'icon', 'info'); }); return $this->options = Options::factory($options); diff --git a/kirby/src/Panel/Assets.php b/kirby/src/Panel/Assets.php new file mode 100644 index 0000000..49fd44f --- /dev/null +++ b/kirby/src/Panel/Assets.php @@ -0,0 +1,339 @@ + + * @link https://getkirby.com + * @copyright Bastian Allgeier + * @license https://getkirby.com/license + * @since 4.0.0 + */ +class Assets +{ + protected bool $dev; + protected App $kirby; + protected string $nonce; + protected Plugins $plugins; + protected string $url; + protected bool $vite; + + public function __construct() + { + $this->kirby = App::instance(); + $this->nonce = $this->kirby->nonce(); + $this->plugins = new Plugins(); + + $vite = $this->kirby->roots()->panel() . '/.vite-running'; + $this->vite = is_file($vite) === true; + + // get the assets from the Vite dev server in dev mode; + // dev mode = explicitly enabled in the config AND Vite is running + $dev = $this->kirby->option('panel.dev', false); + $this->dev = $dev !== false && $this->vite === true; + + // get the base URL + $this->url = $this->url(); + } + + /** + * Get all CSS files + */ + public function css(): array + { + $css = [ + 'index' => $this->url . '/css/style.min.css', + 'plugins' => $this->plugins->url('css'), + ...$this->custom('panel.css') + ]; + + // during dev mode we do not need to load + // the general stylesheet (as styling will be inlined) + if ($this->dev === true) { + $css['index'] = null; + } + + return array_filter($css); + } + + /** + * Check for a custom asset file from the + * config (e.g. panel.css or panel.js) + */ + public function custom(string $option): array + { + $customs = []; + + if ($assets = $this->kirby->option($option)) { + $assets = A::wrap($assets); + + foreach ($assets as $index => $path) { + if (Url::isAbsolute($path) === true) { + $customs['custom-' . $index] = $path; + continue; + } + + $asset = new Asset($path); + + if ($asset->exists() === true) { + $customs['custom-' . $index] = $asset->url() . '?' . $asset->modified(); + } + } + } + + return $customs; + } + + /** + * Generates an array with all assets + * that need to be loaded for the panel (js, css, icons) + */ + public function external(): array + { + return [ + 'css' => $this->css(), + 'icons' => $this->favicons(), + // loader for plugins' index.dev.mjs files – inlined, + // so we provide the code instead of the asset URL + 'plugin-imports' => $this->plugins->read('mjs'), + 'js' => $this->js() + ]; + } + + /** + * Returns array of favicon icons + * based on config option + * + * @todo Deprecate `url` option in v5, use `href` option instead + * @todo Deprecate `rel` usage as array key in v5, use `rel` option instead + * + * @throws \Kirby\Exception\InvalidArgumentException + */ + public function favicons(): array + { + $icons = $this->kirby->option('panel.favicon', [ + [ + 'rel' => 'apple-touch-icon', + 'type' => 'image/png', + 'href' => $this->url . '/apple-touch-icon.png' + ], + [ + 'rel' => 'alternate icon', + 'type' => 'image/png', + 'href' => $this->url . '/favicon.png' + ], + [ + 'rel' => 'shortcut icon', + 'type' => 'image/svg+xml', + 'href' => $this->url . '/favicon.svg' + ], + [ + 'rel' => 'apple-touch-icon', + 'type' => 'image/png', + 'href' => $this->url . '/apple-touch-icon-dark.png', + 'media' => '(prefers-color-scheme: dark)' + ], + [ + 'rel' => 'alternate icon', + 'type' => 'image/png', + 'href' => $this->url . '/favicon-dark.png', + 'media' => '(prefers-color-scheme: dark)' + ] + ]); + + if (is_array($icons) === true) { + // normalize options + foreach ($icons as $rel => &$icon) { + // TODO: remove this backward compatibility check in v6 + if (isset($icon['url']) === true) { + $icon['href'] = $icon['url']; + unset($icon['url']); + } + + // TODO: remove this backward compatibility check in v6 + if (is_string($rel) === true && isset($icon['rel']) === false) { + $icon['rel'] = $rel; + } + + $icon['href'] = Url::to($icon['href']); + $icon['nonce'] = $this->nonce; + } + + return array_values($icons); + } + + // make sure to convert favicon string to array + if (is_string($icons) === true) { + return [ + [ + 'rel' => 'shortcut icon', + 'type' => F::mime($icons), + 'href' => Url::to($icons), + 'nonce' => $this->nonce + ] + ]; + } + + throw new InvalidArgumentException('Invalid panel.favicon option'); + } + + /** + * Load the SVG icon sprite + * This will be injected in the + * initial HTML document for the Panel + */ + public function icons(): string + { + $dir = $this->kirby->root('panel') . '/'; + $dir .= $this->dev ? 'public' : 'dist'; + $icons = F::read($dir . '/img/icons.svg'); + $icons = preg_replace('//', '', $icons); + return $icons; + } + + /** + * Get all js files + */ + public function js(): array + { + $js = [ + 'vue' => [ + 'nonce' => $this->nonce, + 'src' => $this->vue(), + ], + 'vendor' => [ + 'nonce' => $this->nonce, + 'src' => $this->url . '/js/vendor.min.js', + 'type' => 'module' + ], + 'pluginloader' => [ + 'nonce' => $this->nonce, + 'src' => $this->url . '/js/plugins.js', + 'type' => 'module' + ], + 'plugins' => [ + 'nonce' => $this->nonce, + 'src' => $this->plugins->url('js'), + 'defer' => true + ], + ...A::map($this->custom('panel.js'), fn ($src) => [ + 'nonce' => $this->nonce, + 'src' => $src, + 'type' => 'module' + ]), + 'index' => [ + 'nonce' => $this->nonce, + 'src' => $this->url . '/js/index.min.js', + 'type' => 'module' + ], + ]; + + + // during dev mode, add vite client and adapt + // path to `index.js` - vendor does not need + // to be loaded in dev mode + if ($this->dev === true) { + // load the non-minified index.js, remove vendor script and + // development version of Vue + $js['vendor']['src'] = null; + $js['index']['src'] = $this->url . '/src/index.js'; + $js['vue']['src'] = $this->vue(production: false); + + // add vite dev client + $js['vite'] = [ + 'nonce' => $this->nonce, + 'src' => $this->url . '/@vite/client', + 'type' => 'module' + ]; + } + + return array_filter($js, fn ($js) => empty($js['src']) === false); + } + + /** + * Links all dist files in the media folder + * and returns the link to the requested asset + * + * @throws \Kirby\Exception\Exception If Panel assets could not be moved to the public directory + */ + public function link(): bool + { + $mediaRoot = $this->kirby->root('media') . '/panel'; + $panelRoot = $this->kirby->root('panel') . '/dist'; + $versionHash = $this->kirby->versionHash(); + $versionRoot = $mediaRoot . '/' . $versionHash; + + // check if the version already exists + if (is_dir($versionRoot) === true) { + return false; + } + + // delete the panel folder and all previous versions + Dir::remove($mediaRoot); + + // recreate the panel folder + Dir::make($mediaRoot, true); + + // copy assets to the dist folder + if (Dir::copy($panelRoot, $versionRoot) !== true) { + throw new Exception('Panel assets could not be linked'); + } + + return true; + } + + /** + * Get the base URL for all assets depending on dev mode + */ + public function url(): string + { + // vite is not running, use production assets + if ($this->dev === false) { + return $this->kirby->url('media') . '/panel/' . $this->kirby->versionHash(); + } + + // explicitly configured base URL + $dev = $this->kirby->option('panel.dev'); + if (is_string($dev) === true) { + return $dev; + } + + // port 3000 of the current Kirby request + return rtrim($this->kirby->request()->url([ + 'port' => 3000, + 'path' => null, + 'params' => null, + 'query' => null + ])->toString(), '/'); + } + + /** + * Get the correct Vue script URL depending on dev mode + * and the enabled/disabled template compiler + */ + public function vue(bool $production = true): string + { + $script = $this->kirby->option('panel.vue.compiler', true) === true ? 'vue' : 'vue.runtime'; + + if ($production === false) { + return $this->url . '/node_modules/vue/dist/' . $script . '.js'; + } + + return $this->url . '/js/' . $script . '.min.js'; + } +} diff --git a/kirby/src/Panel/ChangesDialog.php b/kirby/src/Panel/ChangesDialog.php new file mode 100644 index 0000000..7053626 --- /dev/null +++ b/kirby/src/Panel/ChangesDialog.php @@ -0,0 +1,71 @@ +multilang(); + $changes = []; + + foreach ($ids as $id) { + try { + // parse the given ID to extract + // the path and an optional query + $uri = new Uri($id); + $path = $uri->path()->toString(); + $query = $uri->query(); + $model = Find::parent($path); + $item = $model->panel()->dropdownOption(); + + // add the language to each option, if it is included in the query + // of the given ID and the language actually exists + if ( + $multilang && + $query->language && + $language = $kirby->language($query->language) + ) { + $item['text'] .= ' (' . $language->code() . ')'; + $item['link'] .= '?language=' . $language->code(); + } + + $item['text'] = Escape::html($item['text']); + + $changes[] = $item; + } catch (Throwable) { + continue; + } + } + + return $changes; + } + + public function load(): array + { + return $this->state(); + } + + public function state(bool $loading = true, array $changes = []) + { + return [ + 'component' => 'k-changes-dialog', + 'props' => [ + 'changes' => $changes, + 'loading' => $loading + ] + ]; + } + + public function submit(array $ids): array + { + return $this->state(false, $this->changes($ids)); + } +} diff --git a/kirby/src/Panel/Dialog.php b/kirby/src/Panel/Dialog.php index 3f0fc1c..df43c4a 100644 --- a/kirby/src/Panel/Dialog.php +++ b/kirby/src/Panel/Dialog.php @@ -7,7 +7,7 @@ use Kirby\Http\Response; /** * The Dialog response class handles Fiber * requests to render the JSON object for - * Panel dialogs + * Panel dialogs and creates the routes * @since 3.6.0 * * @package Kirby Panel @@ -34,4 +34,39 @@ class Dialog extends Json return parent::response($data, $options); } + + /** + * Builds the routes for a dialog + */ + public static function routes( + string $id, + string $areaId, + string $prefix = '', + array $options = [] + ) { + $routes = []; + + // create the full pattern with dialogs prefix + $pattern = trim($prefix . '/' . ($options['pattern'] ?? $id), '/'); + $type = str_replace('$', '', static::$key); + + // load event + $routes[] = [ + 'pattern' => $pattern, + 'type' => $type, + 'area' => $areaId, + 'action' => $options['load'] ?? fn () => 'The load handler is missing' + ]; + + // submit event + $routes[] = [ + 'pattern' => $pattern, + 'type' => $type, + 'area' => $areaId, + 'method' => 'POST', + 'action' => $options['submit'] ?? fn () => 'The submit handler is missing' + ]; + + return $routes; + } } diff --git a/kirby/src/Panel/Document.php b/kirby/src/Panel/Document.php index ee2a62d..1c145f0 100644 --- a/kirby/src/Panel/Document.php +++ b/kirby/src/Panel/Document.php @@ -3,11 +3,6 @@ namespace Kirby\Panel; use Kirby\Cms\App; -use Kirby\Exception\Exception; -use Kirby\Exception\InvalidArgumentException; -use Kirby\Filesystem\Asset; -use Kirby\Filesystem\Dir; -use Kirby\Filesystem\F; use Kirby\Http\Response; use Kirby\Http\Uri; use Kirby\Toolkit\Tpl; @@ -27,234 +22,18 @@ use Throwable; */ class Document { - /** - * Generates an array with all assets - * that need to be loaded for the panel (js, css, icons) - */ - public static function assets(): array - { - $kirby = App::instance(); - $nonce = $kirby->nonce(); - - // get the assets from the Vite dev server in dev mode; - // dev mode = explicitly enabled in the config AND Vite is running - $dev = $kirby->option('panel.dev', false); - $isDev = $dev !== false && is_file($kirby->roots()->panel() . '/.vite-running') === true; - - if ($isDev === true) { - // vite on explicitly configured base URL or port 3000 - // of the current Kirby request - if (is_string($dev) === true) { - $url = $dev; - } else { - $url = rtrim($kirby->request()->url([ - 'port' => 3000, - 'path' => null, - 'params' => null, - 'query' => null - ])->toString(), '/'); - } - } else { - // vite is not running, use production assets - $url = $kirby->url('media') . '/panel/' . $kirby->versionHash(); - } - - // fetch all plugins - $plugins = new Plugins(); - - $assets = [ - 'css' => [ - 'index' => $url . '/css/style.css', - 'plugins' => $plugins->url('css'), - 'custom' => static::customAsset('panel.css'), - ], - 'icons' => static::favicon($url), - // loader for plugins' index.dev.mjs files – - // inlined, so we provide the code instead of the asset URL - 'plugin-imports' => $plugins->read('mjs'), - 'js' => [ - 'vue' => [ - 'nonce' => $nonce, - 'src' => $url . '/js/vue.js' - ], - 'vendor' => [ - 'nonce' => $nonce, - 'src' => $url . '/js/vendor.js', - 'type' => 'module' - ], - 'pluginloader' => [ - 'nonce' => $nonce, - 'src' => $url . '/js/plugins.js', - 'type' => 'module' - ], - 'plugins' => [ - 'nonce' => $nonce, - 'src' => $plugins->url('js'), - 'defer' => true - ], - 'custom' => [ - 'nonce' => $nonce, - 'src' => static::customAsset('panel.js'), - 'type' => 'module' - ], - 'index' => [ - 'nonce' => $nonce, - 'src' => $url . '/js/index.js', - 'type' => 'module' - ], - ] - ]; - - // during dev mode, add vite client and adapt - // path to `index.js` - vendor and stylesheet - // don't need to be loaded in dev mode - if ($isDev === true) { - $assets['js']['vite'] = [ - 'nonce' => $nonce, - 'src' => $url . '/@vite/client', - 'type' => 'module' - ]; - $assets['js']['index'] = [ - 'nonce' => $nonce, - 'src' => $url . '/src/index.js', - 'type' => 'module' - ]; - - // load the development version of Vue - $assets['js']['vue']['src'] = $url . '/node_modules/vue/dist/vue.js'; - - unset($assets['css']['index'], $assets['js']['vendor']); - } - - // remove missing files - $assets['css'] = array_filter($assets['css']); - $assets['js'] = array_filter( - $assets['js'], - fn ($js) => empty($js['src']) === false - ); - - return $assets; - } - - /** - * Check for a custom asset file from the - * config (e.g. panel.css or panel.js) - * @since 3.7.0 - * - * @param string $option asset option name - */ - public static function customAsset(string $option): string|null - { - if ($path = App::instance()->option($option)) { - $asset = new Asset($path); - - if ($asset->exists() === true) { - return $asset->url() . '?' . $asset->modified(); - } - } - - return null; - } - - /** - * Returns array of favicon icons - * based on config option - * @since 3.7.0 - * - * @param string $url URL prefix for default icons - * @throws \Kirby\Exception\InvalidArgumentException - */ - public static function favicon(string $url = ''): array - { - $kirby = App::instance(); - $icons = $kirby->option('panel.favicon', [ - 'apple-touch-icon' => [ - 'type' => 'image/png', - 'url' => $url . '/apple-touch-icon.png', - ], - 'alternate icon' => [ - 'type' => 'image/png', - 'url' => $url . '/favicon.png', - ], - 'shortcut icon' => [ - 'type' => 'image/svg+xml', - 'url' => $url . '/favicon.svg', - ] - ]); - - if (is_array($icons) === true) { - return $icons; - } - - // make sure to convert favicon string to array - if (is_string($icons) === true) { - return [ - 'shortcut icon' => [ - 'type' => F::mime($icons), - 'url' => $icons, - ] - ]; - } - - throw new InvalidArgumentException('Invalid panel.favicon option'); - } - - /** - * Load the SVG icon sprite - * This will be injected in the - * initial HTML document for the Panel - */ - public static function icons(): string - { - $dev = App::instance()->option('panel.dev', false); - $dir = $dev ? 'public' : 'dist'; - return F::read(App::instance()->root('kirby') . '/panel/' . $dir . '/img/icons.svg'); - } - - /** - * Links all dist files in the media folder - * and returns the link to the requested asset - * - * @throws \Kirby\Exception\Exception If Panel assets could not be moved to the public directory - */ - public static function link(): bool - { - $kirby = App::instance(); - $mediaRoot = $kirby->root('media') . '/panel'; - $panelRoot = $kirby->root('panel') . '/dist'; - $versionHash = $kirby->versionHash(); - $versionRoot = $mediaRoot . '/' . $versionHash; - - // check if the version already exists - if (is_dir($versionRoot) === true) { - return false; - } - - // delete the panel folder and all previous versions - Dir::remove($mediaRoot); - - // recreate the panel folder - Dir::make($mediaRoot, true); - - // copy assets to the dist folder - if (Dir::copy($panelRoot, $versionRoot) !== true) { - throw new Exception('Panel assets could not be linked'); - } - - return true; - } - /** * Renders the panel document */ public static function response(array $fiber): Response { - $kirby = App::instance(); + $kirby = App::instance(); + $assets = new Assets(); // Full HTML response // @codeCoverageIgnoreStart try { - if (static::link() === true) { + if ($assets->link() === true) { usleep(1); Response::go($kirby->url('base') . '/' . $kirby->path()); } @@ -264,15 +43,15 @@ class Document // @codeCoverageIgnoreEnd // get the uri object for the panel url - $uri = new Uri($url = $kirby->url('panel')); + $uri = new Uri($kirby->url('panel')); // proper response code $code = $fiber['$view']['code'] ?? 200; // load the main Panel view template $body = Tpl::load($kirby->root('kirby') . '/views/panel.php', [ - 'assets' => static::assets(), - 'icons' => static::icons(), + 'assets' => $assets->external(), + 'icons' => $assets->icons(), 'nonce' => $kirby->nonce(), 'fiber' => $fiber, 'panelUrl' => $uri->path()->toString(true) . '/', diff --git a/kirby/src/Panel/Drawer.php b/kirby/src/Panel/Drawer.php new file mode 100644 index 0000000..0952088 --- /dev/null +++ b/kirby/src/Panel/Drawer.php @@ -0,0 +1,21 @@ + + * @link https://getkirby.com + * @copyright Bastian Allgeier + * @license https://getkirby.com/license + */ +class Drawer extends Dialog +{ + protected static string $key = '$drawer'; +} diff --git a/kirby/src/Panel/Dropdown.php b/kirby/src/Panel/Dropdown.php index b0e5fda..de01bf6 100644 --- a/kirby/src/Panel/Dropdown.php +++ b/kirby/src/Panel/Dropdown.php @@ -2,13 +2,8 @@ namespace Kirby\Panel; -use Kirby\Cms\App; -use Kirby\Cms\Find; -use Kirby\Exception\LogicException; +use Closure; use Kirby\Http\Response; -use Kirby\Http\Uri; -use Kirby\Toolkit\Str; -use Throwable; /** * The Dropdown response class handles Fiber @@ -26,49 +21,6 @@ class Dropdown extends Json { protected static string $key = '$dropdown'; - /** - * Returns the options for the changes dropdown - */ - public static function changes(): array - { - $kirby = App::instance(); - $multilang = $kirby->multilang(); - $ids = Str::split($kirby->request()->get('ids')); - $options = []; - - foreach ($ids as $id) { - try { - // parse the given ID to extract - // the path and an optional query - $uri = new Uri($id); - $path = $uri->path()->toString(); - $query = $uri->query(); - $option = Find::parent($path)->panel()->dropdownOption(); - - // add the language to each option, if it is included in the query - // of the given ID and the language actually exists - if ($multilang && $query->language && $language = $kirby->language($query->language)) { - $option['text'] .= ' (' . $language->code() . ')'; - $option['link'] .= '?language=' . $language->code(); - } - - $options[] = $option; - } catch (Throwable) { - continue; - } - } - - // the given set of ids does not match any - // real models. This means that the stored ids - // in local storage are not correct and the changes - // store needs to be cleared - if (empty($options) === true) { - throw new LogicException('No changes for given models'); - } - - return $options; - } - /** * Renders dropdowns */ @@ -82,4 +34,38 @@ class Dropdown extends Json return parent::response($data, $options); } + + /** + * Routes for the dropdown + */ + public static function routes( + string $id, + string $areaId, + string $prefix = '', + Closure|array $options = [] + ): array { + // Handle shortcuts for dropdowns. The name is the pattern + // and options are defined in a Closure + if ($options instanceof Closure) { + $options = [ + 'pattern' => $id, + 'action' => $options + ]; + } + + // create the full pattern with dialogs prefix + $pattern = trim($prefix . '/' . ($options['pattern'] ?? $id), '/'); + $type = str_replace('$', '', static::$key); + + return [ + // load event + [ + 'pattern' => $pattern, + 'type' => $type, + 'area' => $areaId, + 'method' => 'GET|POST', + 'action' => $options['options'] ?? $options['action'] + ] + ]; + } } diff --git a/kirby/src/Panel/Field.php b/kirby/src/Panel/Field.php index ab53aa1..d792fc0 100644 --- a/kirby/src/Panel/Field.php +++ b/kirby/src/Panel/Field.php @@ -4,8 +4,13 @@ namespace Kirby\Panel; use Kirby\Cms\App; use Kirby\Cms\File; +use Kirby\Cms\ModelWithContent; use Kirby\Cms\Page; +use Kirby\Cms\Roles; +use Kirby\Form\Form; +use Kirby\Http\Router; use Kirby\Toolkit\I18n; +use Kirby\Toolkit\Str; /** * Provides common field prop definitions @@ -20,6 +25,58 @@ use Kirby\Toolkit\I18n; */ class Field { + /** + * Creates the routes for a field dialog + * This is most definitely not a good place for this + * method, but as long as the other classes are + * not fully refactored, it still feels appropriate + */ + public static function dialog( + ModelWithContent $model, + string $fieldName, + string|null $path = null, + string $method = 'GET', + ) { + $field = Form::for($model)->field($fieldName); + $routes = []; + + foreach ($field->dialogs() as $dialogId => $dialog) { + $routes = array_merge($routes, Dialog::routes( + id: $dialogId, + areaId: 'site', + options: $dialog + )); + } + + return Router::execute($path, $method, $routes); + } + + /** + * Creates the routes for a field drawer + * This is most definitely not a good place for this + * method, but as long as the other classes are + * not fully refactored, it still feels appropriate + */ + public static function drawer( + ModelWithContent $model, + string $fieldName, + string|null $path = null, + string $method = 'GET', + ) { + $field = Form::for($model)->field($fieldName); + $routes = []; + + foreach ($field->drawers() as $drawerId => $drawer) { + $routes = array_merge($routes, Drawer::routes( + id: $drawerId, + areaId: 'site', + options: $drawer + )); + } + + return Router::execute($path, $method, $routes); + } + /** * A standard email field */ @@ -73,7 +130,7 @@ class Field public static function hidden(): array { - return ['type' => 'hidden']; + return ['hidden' => true]; } /** @@ -135,30 +192,33 @@ class Field /** * User role radio buttons */ - public static function role(array $props = []): array - { - $kirby = App::instance(); - $user = $kirby->user(); - $isAdmin = $user && $user->isAdmin(); - $roles = []; + public static function role( + array $props = [], + Roles|null $roles = null + ): array { + $kirby = App::instance(); - foreach ($kirby->roles() as $role) { - // exclude the admin role, if the user - // is not allowed to change role to admin - if ($role->name() === 'admin' && $isAdmin === false) { - continue; - } + // if no $roles where provided, fall back to all roles + $roles ??= $kirby->roles(); - $roles[] = [ - 'text' => $role->title(), - 'info' => $role->description() ?? I18n::translate('role.description.placeholder'), - 'value' => $role->name() - ]; - } + // exclude the admin role, if the user + // is not allowed to change role to admin + $roles = $roles->filter( + fn ($role) => + $role->name() !== 'admin' || + $kirby->user()?->isAdmin() === true + ); + + // turn roles into radio field options + $roles = $roles->values(fn ($role) => [ + 'text' => $role->title(), + 'info' => $role->description() ?? I18n::translate('role.description.placeholder'), + 'value' => $role->name() + ]); return array_merge([ 'label' => I18n::translate('role'), - 'type' => count($roles) <= 1 ? 'hidden' : 'radio', + 'type' => count($roles) < 1 ? 'hidden' : 'radio', 'options' => $roles ], $props); } @@ -168,11 +228,14 @@ class Field return array_merge([ 'label' => I18n::translate('slug'), 'type' => 'slug', + 'allow' => Str::$defaults['slug']['allowed'] ], $props); } - public static function template(array|null $blueprints = [], array|null $props = []): array - { + public static function template( + array|null $blueprints = [], + array|null $props = [] + ): array { $options = []; foreach ($blueprints as $blueprint) { @@ -217,7 +280,7 @@ class Field return array_merge([ 'label' => I18n::translate('language'), 'type' => 'select', - 'icon' => 'globe', + 'icon' => 'translate', 'options' => $translations, 'empty' => false ], $props); diff --git a/kirby/src/Panel/File.php b/kirby/src/Panel/File.php index 531c679..de31234 100644 --- a/kirby/src/Panel/File.php +++ b/kirby/src/Panel/File.php @@ -3,6 +3,7 @@ namespace Kirby\Panel; use Kirby\Cms\File as CmsFile; +use Kirby\Cms\ModelWithContent; use Kirby\Filesystem\Asset; use Kirby\Toolkit\I18n; use Throwable; @@ -19,6 +20,11 @@ use Throwable; */ class File extends Model { + /** + * @var \Kirby\Cms\File + */ + protected ModelWithContent $model; + /** * Breadcrumb array */ @@ -41,10 +47,12 @@ class File extends Model break; case 'page': /** @var \Kirby\Cms\Page $parent */ - $breadcrumb = $this->model->parents()->flip()->values(fn ($parent) => [ - 'label' => $parent->title()->toString(), - 'link' => $parent->panel()->url(true), - ]); + $breadcrumb = $this->model->parents()->flip()->values( + fn ($parent) => [ + 'label' => $parent->title()->toString(), + 'link' => $parent->panel()->url(true), + ] + ); } // add the file @@ -65,8 +73,10 @@ class File extends Model * @internal * @param string|null $type (`auto`|`kirbytext`|`markdown`) */ - public function dragText(string|null $type = null, bool $absolute = false): string - { + public function dragText( + string|null $type = null, + bool $absolute = false + ): string { $type = $this->dragTextType($type); $url = $this->model->filename(); $file = $this->model->type(); @@ -76,14 +86,17 @@ class File extends Model // for markdown notation or the UUID for Kirbytext (since // Kirbytags support can resolve UUIDs directly) if ($absolute === true) { - $url = $type === 'markdown' ? $this->model->permalink() : $this->model->uuid(); + $url = match ($type) { + 'markdown' => $this->model->permalink(), + default => $this->model->uuid() + }; + // if UUIDs are disabled, fall back to URL $url ??= $this->model->url(); } - - if ($dragTextFromCallback = $this->dragTextFromCallback($type, $url)) { - return $dragTextFromCallback; + if ($callback = $this->dragTextFromCallback($type, $url)) { + return $callback; } if ($type === 'markdown') { @@ -104,9 +117,9 @@ class File extends Model */ public function dropdown(array $options = []): array { - $file = $this->model; - - $defaults = $file->kirby()->request()->get(['view', 'update', 'delete']); + $file = $this->model; + $request = $file->kirby()->request(); + $defaults = $request->get(['view', 'update', 'delete']); $options = array_merge($defaults, $options); $permissions = $this->options(['preview']); @@ -131,15 +144,7 @@ class File extends Model 'disabled' => $this->isDisabledDropdownOption('changeName', $options, $permissions) ]; - $result[] = [ - 'click' => 'replace', - 'icon' => 'upload', - 'text' => I18n::translate('replace'), - 'disabled' => $this->isDisabledDropdownOption('replace', $options, $permissions) - ]; - if ($view === 'list') { - $result[] = '-'; $result[] = [ 'dialog' => $url . '/changeSort', 'icon' => 'sort', @@ -148,6 +153,22 @@ class File extends Model ]; } + $result[] = [ + 'dialog' => $url . '/changeTemplate', + 'icon' => 'template', + 'text' => I18n::translate('file.changeTemplate'), + 'disabled' => $this->isDisabledDropdownOption('changeTemplate', $options, $permissions) + ]; + + $result[] = '-'; + + $result[] = [ + 'click' => 'replace', + 'icon' => 'upload', + 'text' => I18n::translate('replace'), + 'disabled' => $this->isDisabledDropdownOption('replace', $options, $permissions) + ]; + $result[] = '-'; $result[] = [ 'dialog' => $url . '/delete', @@ -178,22 +199,22 @@ class File extends Model protected function imageColor(): string { $types = [ - 'image' => 'orange-400', - 'video' => 'yellow-400', - 'document' => 'red-400', - 'audio' => 'aqua-400', - 'code' => 'blue-400', - 'archive' => 'gray-500' + 'archive' => 'gray-500', + 'audio' => 'aqua-500', + 'code' => 'pink-500', + 'document' => 'red-500', + 'image' => 'orange-500', + 'video' => 'yellow-500', ]; $extensions = [ - 'indd' => 'purple-400', - 'xls' => 'green-400', - 'xlsx' => 'green-400', - 'csv' => 'green-400', - 'docx' => 'blue-400', - 'doc' => 'blue-400', - 'rtf' => 'blue-400' + 'csv' => 'green-500', + 'doc' => 'blue-500', + 'docx' => 'blue-500', + 'indd' => 'purple-500', + 'rtf' => 'blue-500', + 'xls' => 'green-500', + 'xlsx' => 'green-500', ]; return @@ -219,23 +240,23 @@ class File extends Model protected function imageIcon(): string { $types = [ - 'image' => 'image', - 'video' => 'video', - 'document' => 'document', + 'archive' => 'archive', 'audio' => 'audio', 'code' => 'code', - 'archive' => 'archive' + 'document' => 'document', + 'image' => 'image', + 'video' => 'video', ]; $extensions = [ + 'csv' => 'table', + 'doc' => 'pen', + 'docx' => 'pen', + 'md' => 'markdown', + 'mdown' => 'markdown', + 'rtf' => 'pen', 'xls' => 'table', 'xlsx' => 'table', - 'csv' => 'table', - 'docx' => 'pen', - 'doc' => 'pen', - 'rtf' => 'pen', - 'mdown' => 'markdown', - 'md' => 'markdown' ]; return @@ -258,6 +279,40 @@ class File extends Model return parent::imageSource($query); } + /** + * Whether focus can be added in Panel view + */ + public function isFocusable(): bool + { + // blueprint option + $option = $this->model->blueprint()->focus(); + // fallback to whether the file is viewable + // (images should be focusable by default, others not) + $option ??= $this->model->isViewable(); + + if ($option === false) { + return false; + } + + // ensure that user can update content file + if ($this->options()['update'] === false) { + return false; + } + + $kirby = $this->model->kirby(); + + // ensure focus is only added when editing primary/only language + if ( + $kirby->multilang() === false || + $kirby->languages()->count() === 0 || + $kirby->language()->isDefault() === true + ) { + return true; + } + + return false; + } + /** * Returns an array of all actions * that can be performed in the Panel @@ -298,10 +353,14 @@ class File extends Model if (empty($params['model']) === false) { $parent = $this->model->parent(); + $absolute = $parent !== $params['model']; + // if the file belongs to the current parent model, // store only name as ID to keep its path relative to the model - $id = $parent === $params['model'] ? $name : $id; - $absolute = $parent !== $params['model']; + $id = match ($absolute) { + true => $id, + false => $name + }; } $params['text'] ??= '{{ file.filename }}'; @@ -324,13 +383,6 @@ class File extends Model { $file = $this->model; $dimensions = $file->dimensions(); - $siblings = $file->templateSiblings()->sortBy( - 'sort', - 'asc', - 'filename', - 'asc' - ); - return array_merge( parent::props(), @@ -350,14 +402,16 @@ class File extends Model 'template' => $file->template(), 'type' => $file->type(), 'url' => $file->url(), + 'uuid' => fn () => $file->uuid()?->toString(), ], 'preview' => [ - 'image' => $this->image([ + 'focusable' => $this->isFocusable(), + 'image' => $this->image([ 'back' => 'transparent', 'ratio' => '1/1' ], 'cards'), - 'url' => $url = $file->previewUrl(), - 'details' => [ + 'url' => $url = $file->previewUrl(), + 'details' => [ [ 'title' => I18n::translate('template'), 'text' => $file->template() ?? '—' diff --git a/kirby/src/Panel/Home.php b/kirby/src/Panel/Home.php index 95ba708..3abb1c4 100644 --- a/kirby/src/Panel/Home.php +++ b/kirby/src/Panel/Home.php @@ -50,7 +50,8 @@ class Home // needed to create a proper menu $areas = Panel::areas(); - $menu = View::menu($areas, $permissions->toArray()); + $menu = new Menu($areas, $permissions->toArray()); + $menu = $menu->entries(); // go through the menu and search for the first // available view we can go to @@ -65,11 +66,16 @@ class Home continue; } - // skip the logout button - if ($menuItem['id'] === 'logout') { + // skip buttons that don't open a link + // (but e.g. a dialog) + if (isset($menuItem['link']) === false) { continue; } + // skip the logout button + if ($menuItem['link'] === 'logout') { + continue; + } return Panel::url($menuItem['link']); } @@ -100,9 +106,10 @@ class Home // create a dummy router to check if we can access this route at all try { return Router::execute($path, 'GET', $routes, function ($route) use ($user) { - $auth = $route->attributes()['auth'] ?? true; - $areaId = $route->attributes()['area'] ?? null; - $type = $route->attributes()['type'] ?? 'view'; + $attrs = $route->attributes(); + $auth = $attrs['auth'] ?? true; + $areaId = $attrs['area'] ?? null; + $type = $attrs['type'] ?? 'view'; // only allow redirects to views if ($type !== 'view') { @@ -131,7 +138,8 @@ class Home public static function hasValidDomain(Uri $uri): bool { $rootUrl = App::instance()->site()->url(); - return $uri->domain() === (new Uri($rootUrl))->domain(); + $rootUri = new Uri($rootUrl); + return $uri->domain() === $rootUri->domain(); } /** @@ -139,7 +147,8 @@ class Home */ public static function isPanelUrl(string $url): bool { - return Str::startsWith($url, App::instance()->url('panel')); + $panel = App::instance()->url('panel'); + return Str::startsWith($url, $panel); } /** @@ -161,10 +170,12 @@ class Home public static function remembered(): string|null { // check for a stored path after login - $remembered = App::instance()->session()->pull('panel.path'); + if ($remembered = App::instance()->session()->pull('panel.path')) { + // convert the result to an absolute URL if available + return Panel::url($remembered); + } - // convert the result to an absolute URL if available - return $remembered ? Panel::url($remembered) : null; + return null; } /** diff --git a/kirby/src/Panel/Json.php b/kirby/src/Panel/Json.php index 1e67e31..2cfa895 100644 --- a/kirby/src/Panel/Json.php +++ b/kirby/src/Panel/Json.php @@ -2,6 +2,7 @@ namespace Kirby\Panel; +use Kirby\Cms\App; use Kirby\Exception\Exception; use Kirby\Http\Response; use Throwable; @@ -39,35 +40,45 @@ abstract class Json */ public static function response($data, array $options = []): Response { - // handle redirects - if ($data instanceof Redirect) { - $data = [ - 'redirect' => $data->location(), - 'code' => $data->code() - ]; - - // handle Kirby exceptions - } elseif ($data instanceof Exception) { - $data = static::error($data->getMessage(), $data->getHttpCode()); - - // handle exceptions - } elseif ($data instanceof Throwable) { - $data = static::error($data->getMessage(), 500); - - // only expect arrays from here on - } elseif (is_array($data) === false) { - $data = static::error('Invalid response', 500); - } - - if (empty($data) === true) { - $data = static::error('The response is empty', 404); - } + $data = static::responseData($data); // always inject the response code $data['code'] ??= 200; $data['path'] = $options['path'] ?? null; + $data['query'] = App::instance()->request()->query()->toArray(); $data['referrer'] = Panel::referrer(); return Panel::json([static::$key => $data], $data['code']); } + + public static function responseData(mixed $data): array + { + // handle redirects + if ($data instanceof Redirect) { + return [ + 'redirect' => $data->location(), + ]; + } + + // handle Kirby exceptions + if ($data instanceof Exception) { + return static::error($data->getMessage(), $data->getHttpCode()); + } + + // handle exceptions + if ($data instanceof Throwable) { + return static::error($data->getMessage(), 500); + } + + // only expect arrays from here on + if (is_array($data) === false) { + return static::error('Invalid response', 500); + } + + if (empty($data) === true) { + return static::error('The response is empty', 404); + } + + return $data; + } } diff --git a/kirby/src/Panel/Lab/Category.php b/kirby/src/Panel/Lab/Category.php new file mode 100644 index 0000000..4926737 --- /dev/null +++ b/kirby/src/Panel/Lab/Category.php @@ -0,0 +1,134 @@ + + * @link https://getkirby.com + * @copyright Bastian Allgeier + * @license https://getkirby.com/license + */ +class Category +{ + protected string $root; + + public function __construct( + protected string $id, + string|null $root = null, + protected array $props = [] + ) { + $this->root = $root ?? static::base() . '/' . $this->id; + + if (file_exists($this->root . '/index.php') === true) { + $this->props = array_merge( + require $this->root . '/index.php', + $this->props + ); + } + } + + public static function all(): array + { + // all core lab examples from `kirby/panel/lab` + $examples = A::map( + Dir::inventory(static::base())['children'], + fn ($props) => (new static($props['dirname']))->toArray() + ); + + // all custom lab examples from `site/lab` + $custom = static::factory('site')->toArray(); + + array_push($examples, $custom); + + return $examples; + } + + public static function base(): string + { + return App::instance()->root('panel') . '/lab'; + } + + public function example(string $id, string|null $tab = null): Example + { + return new Example(parent: $this, id: $id, tab: $tab); + } + + public function examples(): array + { + return A::map( + Dir::inventory($this->root)['children'], + fn ($props) => $this->example($props['dirname'])->toArray() + ); + } + + public static function factory(string $id) + { + return match ($id) { + 'site' => static::site(), + default => new static($id) + }; + } + + public function icon(): string + { + return $this->props['icon'] ?? 'palette'; + } + + public function id(): string + { + return $this->id; + } + + public static function installed(): bool + { + return Dir::exists(static::base()) === true; + } + + public function name(): string + { + return $this->props['name'] ?? ucfirst($this->id); + } + + public function root(): string + { + return $this->root; + } + + public static function site(): static + { + return new static( + 'site', + App::instance()->root('site') . '/lab', + [ + 'name' => 'Your examples', + 'icon' => 'live' + ] + ); + } + + public function toArray(): array + { + return [ + 'name' => $this->name(), + 'examples' => $this->examples(), + 'icon' => $this->icon(), + 'path' => Str::after( + $this->root(), + App::instance()->root('index') + ), + ]; + } +} diff --git a/kirby/src/Panel/Lab/Docs.php b/kirby/src/Panel/Lab/Docs.php new file mode 100644 index 0000000..44a6af0 --- /dev/null +++ b/kirby/src/Panel/Lab/Docs.php @@ -0,0 +1,340 @@ + + * @link https://getkirby.com + * @copyright Bastian Allgeier + * @license https://getkirby.com/license + */ +class Docs +{ + protected array $json; + protected App $kirby; + + public function __construct( + protected string $name + ) { + $this->kirby = App::instance(); + $this->json = $this->read(); + } + + public static function all(): array + { + $dist = static::root(); + $tmp = static::root(true); + $files = Dir::inventory($dist)['files']; + + if (Dir::exists($tmp) === true) { + $files = [...Dir::inventory($tmp)['files'], ...$files]; + } + + $docs = A::map( + $files, + function ($file) { + $component = 'k-' . Str::camelToKebab(F::name($file['filename'])); + + return [ + 'image' => [ + 'icon' => 'book', + 'back' => 'white', + ], + 'text' => $component, + 'link' => '/lab/docs/' . $component, + ]; + } + ); + + usort($docs, fn ($a, $b) => $a['text'] <=> $b['text']); + + return array_values($docs); + } + + public function deprecated(): string|null + { + return $this->kt($this->json['tags']['deprecated'][0]['description'] ?? ''); + } + + public function description(): string + { + return $this->kt($this->json['description'] ?? ''); + } + + public function docBlock(): string + { + return $this->kt($this->json['docsBlocks'][0] ?? ''); + } + + public function events(): array + { + $events = A::map( + $this->json['events'] ?? [], + fn ($event) => [ + 'name' => $event['name'], + 'description' => $this->kt($event['description'] ?? ''), + 'deprecated' => $this->kt($event['tags']['deprecated'][0]['description'] ?? ''), + 'since' => $event['tags']['since'][0]['description'] ?? null, + 'properties' => A::map( + $event['properties'] ?? [], + fn ($property) => [ + 'name' => $property['name'], + 'type' => $property['type']['names'][0] ?? '', + 'description' => $this->kt($property['description'] ?? '', true), + ] + ), + ] + ); + + usort($events, fn ($a, $b) => $a['name'] <=> $b['name']); + + return $events; + } + + public function examples(): array + { + if (empty($this->json['tags']['examples']) === false) { + return $this->json['tags']['examples']; + } + + return []; + } + + public function file(string $context): string + { + $root = match ($context) { + 'dev' => $this->kirby->root('panel') . '/tmp', + 'dist' => $this->kirby->root('panel') . '/dist/ui', + }; + + $name = Str::after($this->name, 'k-'); + $name = Str::kebabToCamel($name); + return $root . '/' . $name . '.json'; + } + + public function github(): string + { + return 'https://github.com/getkirby/kirby/tree/main/panel/' . $this->json['sourceFile']; + } + + public static function installed(): bool + { + return Dir::exists(static::root()) === true; + } + + protected function kt(string $text, bool $inline = false): string + { + return $this->kirby->kirbytext($text, [ + 'markdown' => [ + 'breaks' => false, + 'inline' => $inline, + ] + ]); + } + + public function lab(): string|null + { + $root = $this->kirby->root('panel') . '/lab'; + + foreach (glob($root . '/{,*/,*/*/,*/*/*/}index.php', GLOB_BRACE) as $example) { + $props = require $example; + + if (($props['docs'] ?? null) === $this->name) { + return Str::before(Str::after($example, $root), 'index.php'); + } + } + + return null; + } + + public function methods(): array + { + $methods = A::map( + $this->json['methods'] ?? [], + fn ($method) => [ + 'name' => $method['name'], + 'description' => $this->kt($method['description'] ?? ''), + 'deprecated' => $this->kt($method['tags']['deprecated'][0]['description'] ?? ''), + 'since' => $method['tags']['since'][0]['description'] ?? null, + 'params' => A::map( + $method['params'] ?? [], + fn ($param) => [ + 'name' => $param['name'], + 'type' => $param['type']['name'] ?? '', + 'description' => $this->kt($param['description'] ?? '', true), + ] + ), + 'returns' => $method['returns']['type']['name'] ?? null, + ] + ); + + usort($methods, fn ($a, $b) => $a['name'] <=> $b['name']); + + return $methods; + } + + public function name(): string + { + return $this->name; + } + + public function prop(string|int $key): array|null + { + $prop = $this->json['props'][$key]; + + // filter private props + if (($prop['tags']['access'][0]['description'] ?? null) === 'private') { + return null; + } + + // filter unset props + if (($type = $prop['type']['name'] ?? null) === 'null') { + return null; + } + + $default = $prop['defaultValue']['value'] ?? null; + $deprecated = $this->kt($prop['tags']['deprecated'][0]['description'] ?? ''); + + return [ + 'name' => Str::camelToKebab($prop['name']), + 'type' => $type, + 'description' => $this->kt($prop['description'] ?? ''), + 'default' => $this->propDefault($default, $type), + 'deprecated' => $deprecated, + 'example' => $prop['tags']['example'][0]['description'] ?? null, + 'required' => $prop['required'] ?? false, + 'since' => $prop['tags']['since'][0]['description'] ?? null, + 'value' => $prop['tags']['value'][0]['description'] ?? null, + 'values' => $prop['values'] ?? null, + ]; + } + + protected function propDefault( + string|null $default, + string|null $type + ): string|null { + if ($default !== null) { + // normalize longform function + if (preg_match('/function\(\) {.*return (.*);.*}/si', $default, $matches) === 1) { + return $matches[1]; + } + + // normalize object shorthand function + if (preg_match('/\(\) => \((.*)\)/si', $default, $matches) === 1) { + return $matches[1]; + } + + // normalize all other defaults from shorthand function + if (preg_match('/\(\) => (.*)/si', $default, $matches) === 1) { + return $matches[1]; + } + + return $default; + } + + // if type is boolean primarily and no default + // value has been set, add `false` as default + // for clarity + if (Str::startsWith($type, 'boolean')) { + return 'false'; + } + + return null; + } + + public function props(): array + { + $props = A::map( + array_keys($this->json['props'] ?? []), + fn ($key) => $this->prop($key) + ); + + // remove empty props + $props = array_filter($props); + + usort($props, fn ($a, $b) => $a['name'] <=> $b['name']); + + // always return an array + return array_values($props); + } + + protected function read(): array + { + $file = $this->file('dev'); + + if (file_exists($file) === false) { + $file = $this->file('dist'); + } + + return Data::read($file); + } + + public static function root(bool $tmp = false): string + { + return App::instance()->root('panel') . '/' . match ($tmp) { + true => 'tmp', + default => 'dist/ui', + }; + } + + public function since(): string|null + { + return $this->json['tags']['since'][0]['description'] ?? null; + } + + public function slots(): array + { + $slots = A::map( + $this->json['slots'] ?? [], + fn ($slot) => [ + 'name' => $slot['name'], + 'description' => $this->kt($slot['description'] ?? ''), + 'deprecated' => $this->kt($slot['tags']['deprecated'][0]['description'] ?? ''), + 'since' => $slot['tags']['since'][0]['description'] ?? null, + 'bindings' => A::map( + $slot['bindings'] ?? [], + fn ($binding) => [ + 'name' => $binding['name'], + 'type' => $binding['type']['name'] ?? '', + 'description' => $this->kt($binding['description'] ?? '', true), + ] + ), + ] + ); + + usort($slots, fn ($a, $b) => $a['name'] <=> $b['name']); + + return $slots; + } + + public function toArray(): array + { + return [ + 'component' => $this->name(), + 'deprecated' => $this->deprecated(), + 'description' => $this->description(), + 'docBlock' => $this->docBlock(), + 'events' => $this->events(), + 'examples' => $this->examples(), + 'github' => $this->github(), + 'methods' => $this->methods(), + 'props' => $this->props(), + 'since' => $this->since(), + 'slots' => $this->slots(), + ]; + } +} diff --git a/kirby/src/Panel/Lab/Example.php b/kirby/src/Panel/Lab/Example.php new file mode 100644 index 0000000..153bb12 --- /dev/null +++ b/kirby/src/Panel/Lab/Example.php @@ -0,0 +1,296 @@ + + * @link https://getkirby.com + * @copyright Bastian Allgeier + * @license https://getkirby.com/license + */ +class Example +{ + protected string $root; + protected string|null $tab = null; + protected array $tabs; + + public function __construct( + protected Category $parent, + protected string $id, + string|null $tab = null, + ) { + $this->root = $this->parent->root() . '/' . $this->id; + + if ($this->exists() === false) { + throw new NotFoundException('The example could not be found'); + } + + $this->tabs = $this->collectTabs(); + $this->tab = $this->collectTab($tab); + } + + public function collectTab(string|null $tab): string|null + { + if (empty($this->tabs) === true) { + return null; + } + + if (array_key_exists($tab, $this->tabs) === true) { + return $tab; + } + + return array_key_first($this->tabs); + } + + public function collectTabs(): array + { + $tabs = []; + + foreach (Dir::inventory($this->root)['children'] as $child) { + $tabs[$child['dirname']] = [ + 'name' => $child['dirname'], + 'label' => $child['slug'], + 'link' => '/lab/' . $this->parent->id() . '/' . $this->id . '/' . $child['dirname'] + ]; + } + + return $tabs; + } + + public function exists(): bool + { + return is_dir($this->root) === true; + } + + public function file(string $filename): string + { + return $this->parent->root() . '/' . $this->path() . '/' . $filename; + } + + public function id(): string + { + return $this->id; + } + + public function load(string $filename): array|null + { + if ($file = $this->file($filename)) { + return F::load($file); + } + + return null; + } + + public function module(): string + { + return $this->url() . '/index.vue'; + } + + public function path(): string + { + return match ($this->tab) { + null => $this->id, + default => $this->id . '/' . $this->tab + }; + } + + public function props(): array + { + if ($this->tab !== null) { + $props = $this->load('../index.php'); + } + + return array_replace_recursive( + $props ?? [], + $this->load('index.php') ?? [] + ); + } + + public function read(string $filename): string|null + { + $file = $this->file($filename); + + if (is_file($file) === false) { + return null; + } + + return F::read($file); + } + + public function root(): string + { + return $this->root; + } + + public function serve(): Response + { + return new Response($this->vue()['script'], 'application/javascript'); + } + + public function tab(): string|null + { + return $this->tab; + } + + public function tabs(): array + { + return $this->tabs; + } + + public function template(string $filename): string|null + { + $file = $this->file($filename); + + if (is_file($file) === false) { + return null; + } + + $data = $this->props(); + return (new Template($file))->render($data); + } + + public function title(): string + { + return basename($this->id); + } + + public function toArray(): array + { + return [ + 'image' => [ + 'icon' => $this->parent->icon(), + 'back' => 'white', + ], + 'text' => $this->title(), + 'link' => $this->url() + ]; + } + + public function url(): string + { + return '/lab/' . $this->parent->id() . '/' . $this->path(); + } + + public function vue(): array + { + // read the index.vue file (or programmabel Vue PHP file) + $file = $this->read('index.vue'); + $file ??= $this->template('index.vue.php'); + $file ??= ''; + + // extract parts + $parts['script'] = $this->vueScript($file); + $parts['template'] = $this->vueTemplate($file); + $parts['examples'] = $this->vueExamples($parts['template'], $parts['script']); + $parts['style'] = $this->vueStyle($file); + + return $parts; + } + + public function vueExamples(string|null $template, string|null $script): array + { + $template ??= ''; + $examples = []; + $scripts = []; + + if (preg_match_all('!\/\*\* \@script: (.*?)\*\/(.*?)\/\*\* \@script-end \*\/!s', $script, $matches)) { + foreach ($matches[1] as $key => $name) { + $code = $matches[2][$key]; + $code = preg_replace('!const (.*?) \=!', 'default', $code); + + $scripts[trim($name)] = $code; + } + } + + if (preg_match_all('!(.*?)<\/k-lab-example>!s', $template, $matches)) { + foreach ($matches[1] as $key => $name) { + $tail = $matches[2][$key]; + $code = $matches[3][$key]; + + $scriptId = trim(preg_replace_callback( + '!script="(.*?)"!', + fn ($match) => trim($match[1]), + $tail + )); + + $scriptBlock = $scripts[$scriptId] ?? null; + + if (empty($scriptBlock) === false) { + $js = PHP_EOL . PHP_EOL; + $js .= ''; + } else { + $js = ''; + } + + // only use the code between the @code and @code-end comments + if (preg_match('$(.*?)$s', $code, $match)) { + $code = $match[1]; + } + + if (preg_match_all('/^(\t*)\S/m', $code, $indents)) { + // get minimum indent + $indents = array_map(fn ($i) => strlen($i), $indents[1]); + $indents = min($indents); + + if (empty($js) === false) { + $indents--; + } + + // strip minimum indent from each line + $code = preg_replace('/^\t{' . $indents . '}/m', '', $code); + } + + $code = trim($code); + + if (empty($js) === false) { + $code = ''; + } + + $examples[$name] = $code . $js; + } + } + + return $examples; + } + + public function vueScript(string $file): string + { + if (preg_match('!!s', $file, $match)) { + return trim($match[1]); + } + + return 'export default {}'; + } + + public function vueStyle(string $file): string|null + { + if (preg_match('!!s', $file, $match)) { + return trim($match[1]); + } + + return null; + } + + public function vueTemplate(string $file): string|null + { + if (preg_match('!!s', $file, $match)) { + return preg_replace('!^\n!', '', $match[1]); + } + + return null; + } +} diff --git a/kirby/src/Panel/Lab/Snippet.php b/kirby/src/Panel/Lab/Snippet.php new file mode 100644 index 0000000..0739eb7 --- /dev/null +++ b/kirby/src/Panel/Lab/Snippet.php @@ -0,0 +1,26 @@ + + * @link https://getkirby.com + * @copyright Bastian Allgeier + * @license https://getkirby.com/license + */ +class Snippet extends BaseSnippet +{ + public static function root(): string + { + return __DIR__ . '/snippets'; + } +} diff --git a/kirby/src/Panel/Lab/Template.php b/kirby/src/Panel/Lab/Template.php new file mode 100644 index 0000000..71cf484 --- /dev/null +++ b/kirby/src/Panel/Lab/Template.php @@ -0,0 +1,34 @@ + + * @link https://getkirby.com + * @copyright Bastian Allgeier + * @license https://getkirby.com/license + */ +class Template extends BaseTemplate +{ + public function __construct( + public string $file + ) { + parent::__construct( + name: basename($this->file) + ); + } + + public function file(): string|null + { + return $this->file; + } +} diff --git a/kirby/src/Panel/Menu.php b/kirby/src/Panel/Menu.php new file mode 100644 index 0000000..c1c4498 --- /dev/null +++ b/kirby/src/Panel/Menu.php @@ -0,0 +1,221 @@ + + * @link https://getkirby.com + * @copyright Bastian Allgeier + * @license https://getkirby.com/license + */ +class Menu +{ + public function __construct( + protected array $areas = [], + protected array $permissions = [], + protected string|null $current = null + ) { + } + + /** + * Returns all areas that are configured for the menu + * @internal + */ + public function areas(): array + { + // get from config option which areas should be listed in the menu + $kirby = App::instance(); + $areas = $kirby->option('panel.menu'); + + if ($areas instanceof Closure) { + $areas = $areas($kirby); + } + + // if no config is defined… + if ($areas === null) { + // ensure that some defaults are on top in the right order + $defaults = ['site', 'languages', 'users', 'system']; + // add all other areas after that + $additionals = array_diff(array_keys($this->areas), $defaults); + $areas = array_merge($defaults, $additionals); + } + + $result = []; + + foreach ($areas as $id => $area) { + // separator, keep as is in array + if ($area === '-') { + $result[] = '-'; + continue; + } + + // for a simple id, get global area definition + if (is_numeric($id) === true) { + $id = $area; + $area = $this->areas[$id] ?? null; + } + + // did not receive custom entry definition in config, + // but also is not a global area + if ($area === null) { + continue; + } + + // merge area definition (e.g. from config) + // with global area definition + if (is_array($area) === true) { + $area = array_merge( + $this->areas[$id] ?? [], + ['menu' => true], + $area + ); + $area = Panel::area($id, $area); + } + + $result[] = $area; + } + + return $result; + } + + /** + * Transforms an area definition into a menu entry + * @internal + */ + public function entry(array $area): array|false + { + // areas without access permissions get skipped entirely + if ($this->hasPermission($area['id']) === false) { + return false; + } + + // check menu setting from the area definition + $menu = $area['menu'] ?? false; + + // menu setting can be a callback + // that returns true, false or 'disabled' + if ($menu instanceof Closure) { + $menu = $menu($this->areas, $this->permissions, $this->current); + } + + // false will remove the area/entry entirely + //just like with disabled permissions + if ($menu === false) { + return false; + } + + $menu = match ($menu) { + 'disabled' => ['disabled' => true], + true => [], + default => $menu + }; + + $entry = array_merge([ + 'current' => $this->isCurrent( + $area['id'], + $area['current'] ?? null + ), + 'icon' => $area['icon'] ?? null, + 'link' => $area['link'] ?? null, + 'dialog' => $area['dialog'] ?? null, + 'drawer' => $area['drawer'] ?? null, + 'text' => I18n::translate($area['label'], $area['label']) + ], $menu); + + // unset the link (which is always added by default to an area) + // if a dialog or drawer should be opened instead + if (isset($entry['dialog']) || isset($entry['drawer'])) { + unset($entry['link']); + } + + return array_filter($entry); + } + + /** + * Returns all menu entries + */ + public function entries(): array + { + $entries = []; + $areas = $this->areas(); + + foreach ($areas as $area) { + if ($area === '-') { + $entries[] = '-'; + } elseif ($entry = $this->entry($area)) { + $entries[] = $entry; + } + } + + $entries[] = '-'; + + return array_merge($entries, $this->options()); + } + + /** + * Checks if the access permission to a specific area is granted. + * Defaults to allow access. + * @internal + */ + public function hasPermission(string $id): bool + { + return $this->permissions['access'][$id] ?? true; + } + + /** + * Whether the menu entry should receive aria-current + * @internal + */ + public function isCurrent( + string $id, + bool|Closure|null $callback = null + ): bool { + if ($callback !== null) { + if ($callback instanceof Closure) { + $callback = $callback($this->current); + } + + return $callback; + } + + return $this->current === $id; + } + + /** + * Default options entries for bottom of menu + * @internal + */ + public function options(): array + { + $options = [ + [ + 'icon' => 'edit-line', + 'dialog' => 'changes', + 'text' => I18n::translate('changes'), + ], + [ + 'current' => $this->isCurrent('account'), + 'icon' => 'account', + 'link' => 'account', + 'disabled' => $this->hasPermission('account') === false, + 'text' => I18n::translate('view.account'), + ], + [ + 'icon' => 'logout', + 'link' => 'logout', + 'text' => I18n::translate('logout') + ] + ]; + + return $options; + } +} diff --git a/kirby/src/Panel/Model.php b/kirby/src/Panel/Model.php index 83a1621..b00873f 100644 --- a/kirby/src/Panel/Model.php +++ b/kirby/src/Panel/Model.php @@ -22,11 +22,9 @@ use Kirby\Toolkit\A; */ abstract class Model { - protected ModelWithContent $model; - - public function __construct(ModelWithContent $model) - { - $this->model = $model; + public function __construct( + protected ModelWithContent $model + ) { } /** @@ -85,9 +83,10 @@ abstract class Model public function dropdownOption(): array { return [ - 'icon' => 'page', - 'link' => $this->url(), - 'text' => $this->model->id(), + 'icon' => 'page', + 'image' => $this->image(['back' => 'black']), + 'link' => $this->url(true), + 'text' => $this->model->id(), ]; } @@ -104,59 +103,49 @@ abstract class Model return null; } + // switched off from blueprint, + // only if not overwritten by $settings + $blueprint = $this->model->blueprint()->image(); + + if ($blueprint === false) { + if (empty($settings) === true) { + return null; + } + + $blueprint = null; + } + + // convert string blueprint settings to proper array + if (is_string($blueprint) === true) { + $blueprint = ['query' => $blueprint]; + } + // skip image thumbnail if option // is explicitly set to show the icon if ($settings === 'icon') { - $settings = [ - 'query' => false - ]; - } elseif (is_string($settings) === true) { - // convert string settings to proper array - $settings = [ - 'query' => $settings - ]; + $settings = ['query' => false]; + } + + // convert string settings to proper array + if (is_string($settings) === true) { + $settings = ['query' => $settings]; } // merge with defaults and blueprint option $settings = array_merge( $this->imageDefaults(), $settings ?? [], - $this->model->blueprint()->image() ?? [], + $blueprint ?? [], ); if ($image = $this->imageSource($settings['query'] ?? null)) { // main url $settings['url'] = $image->url(); - // only create srcsets for resizable files if ($image->isResizable() === true) { - $settings['src'] = static::imagePlaceholder(); - - $sizes = match ($layout) { - 'cards' => [352, 864, 1408], - 'cardlets' => [96, 192], - default => [38, 76] - }; - - if ( - ($settings['cover'] ?? false) === false || - $layout === 'cards' - ) { - $settings['srcset'] = $image->srcset($sizes); - } else { - $settings['srcset'] = $image->srcset([ - '1x' => [ - 'width' => $sizes[0], - 'height' => $sizes[0], - 'crop' => 'center' - ], - '2x' => [ - 'width' => $sizes[1], - 'height' => $sizes[1], - 'crop' => 'center' - ] - ]); - } + // only create srcsets for resizable files + $settings['src'] = static::imagePlaceholder(); + $settings['srcset'] = $this->imageSrcset($image, $layout, $settings); } elseif ($image->isViewable() === true) { $settings['src'] = $image->url(); } @@ -183,8 +172,7 @@ abstract class Model 'back' => 'pattern', 'color' => 'gray-500', 'cover' => false, - 'icon' => 'page', - 'ratio' => '3/2', + 'icon' => 'page' ]; } @@ -217,14 +205,85 @@ abstract class Model return null; } + /** + * Provides the correct srcset string based on + * the layout and settings + * @internal + */ + protected function imageSrcset( + CmsFile|Asset $image, + string $layout, + array $settings + ): string|null { + // depending on layout type, set different sizes + // to have multiple options for the srcset attribute + $sizes = match ($layout) { + 'cards' => [352, 864, 1408], + 'cardlets' => [96, 192], + default => [38, 76] + }; + + // no additional modfications needed if `cover: false` + if (($settings['cover'] ?? false) === false) { + return $image->srcset($sizes); + } + + // for card layouts with `cover: true` provide + // crops based on the card ratio + if ($layout === 'cards') { + $ratio = explode('/', $settings['ratio'] ?? '1/1'); + $ratio = $ratio[0] / $ratio[1]; + + return $image->srcset([ + $sizes[0] . 'w' => [ + 'width' => $sizes[0], + 'height' => round($sizes[0] / $ratio), + 'crop' => true + ], + $sizes[1] . 'w' => [ + 'width' => $sizes[1], + 'height' => round($sizes[1] / $ratio), + 'crop' => true + ], + $sizes[2] . 'w' => [ + 'width' => $sizes[2], + 'height' => round($sizes[2] / $ratio), + 'crop' => true + ] + ]); + } + + // for list and cardlets with `cover: true` + // provide square crops in two resolutions + return $image->srcset([ + '1x' => [ + 'width' => $sizes[0], + 'height' => $sizes[0], + 'crop' => true + ], + '2x' => [ + 'width' => $sizes[1], + 'height' => $sizes[1], + 'crop' => true + ] + ]); + } + /** * Checks for disabled dropdown options according * to the given permissions */ - public function isDisabledDropdownOption(string $action, array $options, array $permissions): bool - { + public function isDisabledDropdownOption( + string $action, + array $options, + array $permissions + ): bool { $option = $options[$action] ?? true; - return $permissions[$action] === false || $option === false || $option === 'false'; + + return + $permissions[$action] === false || + $option === false || + $option === 'false'; } /** @@ -235,11 +294,7 @@ abstract class Model */ public function lock(): array|false { - if ($lock = $this->model->lock()) { - return $lock->toArray(); - } - - return false; + return $this->model->lock()?->toArray() ?? false; } /** @@ -287,7 +342,7 @@ abstract class Model 'link' => $this->url(true), 'sortable' => true, 'text' => $this->model->toSafeString($params['text'] ?? false), - 'uuid' => $this->model->uuid()?->toString() ?? $this->model->id(), + 'uuid' => $this->model->uuid()?->toString() ]; } @@ -320,33 +375,34 @@ abstract class Model } /** - * Returns link url and tooltip - * for model (e.g. used for prev/next - * navigation) + * Returns link url and title + * for model (e.g. used for prev/next navigation) * @internal */ - public function toLink(string $tooltip = 'title'): array + public function toLink(string $title = 'title'): array { return [ 'link' => $this->url(true), - 'tooltip' => (string)$this->model->{$tooltip}() + 'title' => $title = (string)$this->model->{$title}() ]; } /** - * Returns link url and tooltip + * Returns link url and title * for optional sibling model and * preserves tab selection * * @internal */ - protected function toPrevNextLink(ModelWithContent|null $model = null, string $tooltip = 'title'): array|null - { + protected function toPrevNextLink( + ModelWithContent|null $model = null, + string $title = 'title' + ): array|null { if ($model === null) { return null; } - $data = $model->panel()->toLink($tooltip); + $data = $model->panel()->toLink($title); if ($tab = $model->kirby()->request()->get('tab')) { $uri = new Uri($data['link'], [ diff --git a/kirby/src/Panel/Page.php b/kirby/src/Panel/Page.php index 088822c..40801c9 100644 --- a/kirby/src/Panel/Page.php +++ b/kirby/src/Panel/Page.php @@ -3,6 +3,7 @@ namespace Kirby\Panel; use Kirby\Cms\File as CmsFile; +use Kirby\Cms\ModelWithContent; use Kirby\Filesystem\Asset; use Kirby\Toolkit\I18n; @@ -18,16 +19,24 @@ use Kirby\Toolkit\I18n; */ class Page extends Model { + /** + * @var \Kirby\Cms\Page + */ + protected ModelWithContent $model; + /** * Breadcrumb array */ public function breadcrumb(): array { $parents = $this->model->parents()->flip()->merge($this->model); - return $parents->values(fn ($parent) => [ - 'label' => $parent->title()->toString(), - 'link' => $parent->panel()->url(true), - ]); + + return $parents->values( + fn ($parent) => [ + 'label' => $parent->title()->toString(), + 'link' => $parent->panel()->url(true), + ] + ); } /** @@ -65,9 +74,9 @@ class Page extends Model */ public function dropdown(array $options = []): array { - $page = $this->model; - - $defaults = $page->kirby()->request()->get(['view', 'sort', 'delete']); + $page = $this->model; + $request = $page->kirby()->request(); + $defaults = $request->get(['view', 'sort', 'delete']); $options = array_merge($defaults, $options); $permissions = $this->options(['preview']); @@ -98,15 +107,6 @@ class Page extends Model 'disabled' => $this->isDisabledDropdownOption('changeTitle', $options, $permissions) ]; - $result['duplicate'] = [ - 'dialog' => $url . '/duplicate', - 'icon' => 'copy', - 'text' => I18n::translate('duplicate'), - 'disabled' => $this->isDisabledDropdownOption('duplicate', $options, $permissions) - ]; - - $result[] = '-'; - $result['changeSlug'] = [ 'dialog' => [ 'url' => $url . '/changeTitle', @@ -143,6 +143,23 @@ class Page extends Model ]; $result[] = '-'; + + $result['move'] = [ + 'dialog' => $url . '/move', + 'icon' => 'parent', + 'text' => I18n::translate('page.move'), + 'disabled' => $this->isDisabledDropdownOption('move', $options, $permissions) + ]; + + $result['duplicate'] = [ + 'dialog' => $url . '/duplicate', + 'icon' => 'copy', + 'text' => I18n::translate('duplicate'), + 'disabled' => $this->isDisabledDropdownOption('duplicate', $options, $permissions) + ]; + + $result[] = '-'; + $result['delete'] = [ 'dialog' => $url . '/delete', 'icon' => 'trash', @@ -290,7 +307,7 @@ class Page extends Model ->filter('status', $page->status()); } - return $siblings->filter('isReadable', true); + return $siblings->filter('isListable', true); }; return [ @@ -322,6 +339,7 @@ class Page extends Model 'previewUrl' => $page->previewUrl(), 'status' => $page->status(), 'title' => $page->title()->toString(), + 'uuid' => fn () => $page->uuid()?->toString(), ], 'status' => function () use ($page) { if ($status = $page->status()) { diff --git a/kirby/src/Panel/PageCreateDialog.php b/kirby/src/Panel/PageCreateDialog.php new file mode 100644 index 0000000..a2aa712 --- /dev/null +++ b/kirby/src/Panel/PageCreateDialog.php @@ -0,0 +1,386 @@ + + * @link https://getkirby.com + * @copyright Bastian Allgeier + * @license https://getkirby.com/license + */ +class PageCreateDialog +{ + protected PageBlueprint $blueprint; + protected Page $model; + protected Page|Site $parent; + protected string $parentId; + protected string|null $sectionId; + protected string|null $slug; + protected string|null $template; + protected string|null $title; + protected Page|Site|User|File $view; + protected string|null $viewId; + + public static array $fieldTypes = [ + 'checkboxes', + 'date', + 'email', + 'info', + 'line', + 'link', + 'list', + 'number', + 'multiselect', + 'radio', + 'range', + 'select', + 'slug', + 'tags', + 'tel', + 'text', + 'toggle', + 'toggles', + 'time', + 'url' + ]; + + public function __construct( + string|null $parentId, + string|null $sectionId, + string|null $template, + string|null $viewId, + + // optional + string|null $slug = null, + string|null $title = null, + ) { + $this->parentId = $parentId ?? 'site'; + $this->parent = Find::parent($this->parentId); + $this->sectionId = $sectionId; + $this->slug = $slug; + $this->template = $template; + $this->title = $title; + $this->viewId = $viewId; + $this->view = Find::parent($this->viewId ?? $this->parentId); + } + + /** + * Get the blueprint settings for the new page + */ + public function blueprint(): PageBlueprint + { + // create a temporary page object + return $this->blueprint ??= $this->model()->blueprint(); + } + + /** + * Get an array of all blueprints for the parent view + */ + public function blueprints(): array + { + return A::map( + $this->view->blueprints($this->sectionId), + function ($blueprint) { + $blueprint['name'] ??= $blueprint['value'] ?? null; + return $blueprint; + } + ); + } + + /** + * All the default fields for the dialog + */ + public function coreFields(): array + { + $fields = []; + + $title = $this->blueprint()->create()['title'] ?? null; + $slug = $this->blueprint()->create()['slug'] ?? null; + + if ($title === false || $slug === false) { + throw new InvalidArgumentException('Page create dialog: title and slug must not be false'); + } + + // title field + if ($title === null || is_array($title) === true) { + $label = $title['label'] ?? 'title'; + $fields['title'] = Field::title([ + ...$title ?? [], + 'label' => I18n::translate($label, $label), + 'required' => true, + 'preselect' => true + ]); + } + + // slug field + if ($slug === null) { + $fields['slug'] = Field::slug([ + 'required' => true, + 'sync' => 'title', + 'path' => $this->parent instanceof Page ? '/' . $this->parent->id() . '/' : '/' + ]); + } + + return [ + ...$fields, + 'parent' => Field::hidden(), + 'section' => Field::hidden(), + 'template' => Field::hidden(), + 'view' => Field::hidden(), + ]; + } + + /** + * Loads custom fields for the page type + */ + public function customFields(): array + { + $custom = []; + $ignore = ['title', 'slug', 'parent', 'template']; + $blueprint = $this->blueprint(); + $fields = $blueprint->fields(); + + foreach ($blueprint->create()['fields'] ?? [] as $name) { + if (!$field = ($fields[$name] ?? null)) { + throw new InvalidArgumentException('Unknown field "' . $name . '" in create dialog'); + } + + if (in_array($field['type'], static::$fieldTypes) === false) { + throw new InvalidArgumentException('Field type "' . $field['type'] . '" not supported in create dialog'); + } + + if (in_array($name, $ignore) === true) { + throw new InvalidArgumentException('Field name "' . $name . '" not allowed as custom field in create dialog'); + } + + // switch all fields to 1/1 + $field['width'] = '1/1'; + + // add the field to the form + $custom[$name] = $field; + } + + // create form so that field props, options etc. + // can be properly resolved + $form = new Form([ + 'fields' => $custom, + 'model' => $this->model(), + 'strict' => true + ]); + + return $form->fields()->toArray(); + } + + /** + * Loads all the fields for the dialog + */ + public function fields(): array + { + return [ + ...$this->coreFields(), + ...$this->customFields() + ]; + } + + /** + * Provides all the props for the + * dialog, including the fields and + * initial values + */ + public function load(): array + { + $blueprints = $this->blueprints(); + + $this->template ??= $blueprints[0]['name']; + + $status = $this->blueprint()->create()['status'] ?? 'draft'; + $status = $this->blueprint()->status()[$status]['label'] ?? null; + $status ??= I18n::translate('page.status.' . $status); + + $fields = $this->fields(); + $visible = array_filter( + $fields, + fn ($field) => ($field['hidden'] ?? null) !== true + ); + + // immediately submit the dialog if there is no editable field + if (count($visible) === 0 && count($blueprints) < 2) { + $input = $this->value(); + $response = $this->submit($input); + $response['redirect'] ??= $this->parent->panel()->url(true); + Panel::go($response['redirect']); + } + + return [ + 'component' => 'k-page-create-dialog', + 'props' => [ + 'blueprints' => $blueprints, + 'fields' => $fields, + 'submitButton' => I18n::template('page.create', [ + 'status' => $status + ]), + 'template' => $this->template, + 'value' => $this->value() + ] + ]; + } + + /** + * Temporary model for the page to + * be created, used to properly render + * the blueprint for fields + */ + public function model(): Page + { + // TODO: use actual in-memory page in v5 + return $this->model ??= Page::factory([ + 'slug' => '__new__', + 'template' => $this->template, + 'model' => $this->template, + 'parent' => $this->parent instanceof Page ? $this->parent : null + ]); + } + + /** + * Generates values for title and slug + * from template strings from the blueprint + */ + public function resolveFieldTemplates(array $input): array + { + $title = $this->blueprint()->create()['title'] ?? null; + $slug = $this->blueprint()->create()['slug'] ?? null; + + // create temporary page object + // to resolve the template strings + $page = $this->model()->clone(['content' => $input]); + + if (is_string($title)) { + $input['title'] = $page->toSafeString($title); + } + + if (is_string($slug)) { + $input['slug'] = $page->toSafeString($slug); + } + + return $input; + } + + /** + * Prepares and cleans up the input data + */ + public function sanitize(array $input): array + { + $input['title'] ??= $this->title ?? ''; + $input['slug'] ??= $this->slug ?? ''; + + $input = $this->resolveFieldTemplates($input); + $content = ['title' => trim($input['title'])]; + + foreach ($this->customFields() as $name => $field) { + $content[$name] = $input[$name] ?? null; + } + + // create temporary form to sanitize the input + // and add default values + $form = Form::for($this->model(), ['values' => $content]); + + return [ + 'content' => $form->strings(true), + 'slug' => $input['slug'], + 'template' => $this->template, + ]; + } + + /** + * Submits the dialog form and creates the new page + */ + public function submit(array $input): array + { + $input = $this->sanitize($input); + $status = $this->blueprint()->create()['status'] ?? 'draft'; + + // validate the input before creating the page + $this->validate($input, $status); + + $page = $this->parent->createChild($input); + + if ($status !== 'draft') { + // grant all permissions as the status is set in the blueprint and + // should not be treated as if the user would try to change it + $page->kirby()->impersonate( + 'kirby', + fn () => $page->changeStatus($status) + ); + } + + $payload = [ + 'event' => 'page.create' + ]; + + // add redirect, if not explicitly disabled + if (($this->blueprint()->create()['redirect'] ?? null) !== false) { + $payload['redirect'] = $page->panel()->url(true); + } + + return $payload; + } + + public function validate(array $input, string $status = 'draft'): bool + { + // basic validation + PageRules::validateTitleLength($input['content']['title']); + PageRules::validateSlugLength($input['slug']); + + // if the page is supposed to be published directly, + // ensure that all field validations are met + if ($status !== 'draft') { + // create temporary form to validate the input + $form = Form::for($this->model(), ['values' => $input['content']]); + + if ($form->isInvalid() === true) { + throw new InvalidArgumentException([ + 'key' => 'page.changeStatus.incomplete' + ]); + } + } + + return true; + } + + public function value(): array + { + $value = [ + 'parent' => $this->parentId, + 'section' => $this->sectionId, + 'slug' => $this->slug ?? '', + 'template' => $this->template, + 'title' => $this->title ?? '', + 'view' => $this->viewId, + ]; + + // add default values for custom fields + foreach ($this->customFields() as $name => $field) { + if ($default = $field['default'] ?? null) { + $value[$name] = $default; + } + } + + return $value; + } +} diff --git a/kirby/src/Panel/Panel.php b/kirby/src/Panel/Panel.php index 48f3267..b88bcab 100644 --- a/kirby/src/Panel/Panel.php +++ b/kirby/src/Panel/Panel.php @@ -35,7 +35,7 @@ class Panel /** * Normalize a panel area */ - public static function area(string $id, array|string $area): array + public static function area(string $id, array $area): array { $area['id'] = $id; $area['label'] ??= $id; @@ -60,9 +60,15 @@ class Panel $areas = $kirby->load()->areas(); // the system is not ready - if ($system->isOk() === false || $system->isInstalled() === false) { + if ( + $system->isOk() === false || + $system->isInstalled() === false + ) { return [ - 'installation' => static::area('installation', $areas['installation']), + 'installation' => static::area( + 'installation', + $areas['installation'] + ), ]; } @@ -70,7 +76,6 @@ class Panel if (!$user) { return [ 'logout' => static::area('logout', $areas['logout']), - // login area last because it defines a fallback route 'login' => static::area('login', $areas['login']), ]; @@ -85,24 +90,8 @@ class Panel unset($areas['languages']); } - $menu = $kirby->option('panel.menu', [ - 'site', - 'languages', - 'users', - 'system', - ]); - $result = []; - // add the sorted areas - foreach ($menu as $id) { - if ($area = ($areas[$id] ?? null)) { - $result[$id] = static::area($id, $area); - unset($areas[$id]); - } - } - - // add the remaining areas foreach ($areas as $id => $area) { $result[$id] = static::area($id, $area); } @@ -185,7 +174,9 @@ class Panel $request = App::instance()->request(); if ($request->method() === 'GET') { - return (bool)($request->get('_json') ?? $request->header('X-Fiber')); + return + (bool)($request->get('_json') ?? + $request->header('X-Fiber')); } return false; @@ -200,7 +191,7 @@ class Panel $request = App::instance()->request(); return Response::json($data, $code, $request->get('_pretty'), [ - 'X-Fiber' => 'true', + 'X-Fiber' => 'true', 'Cache-Control' => 'no-store, private' ]); } @@ -244,7 +235,7 @@ class Panel if ($result === null || $result === false) { $result = new NotFoundException('The data could not be found'); - // interpret strings as errors + // interpret strings as errors } elseif (is_string($result) === true) { $result = new Exception($result); } @@ -252,7 +243,9 @@ class Panel // handle different response types (view, dialog, ...) return match ($options['type'] ?? null) { 'dialog' => Dialog::response($result, $options), + 'drawer' => Drawer::response($result, $options), 'dropdown' => Dropdown::response($result, $options), + 'request' => Request::response($result, $options), 'search' => Search::response($result, $options), default => View::response($result, $options) }; @@ -291,7 +284,11 @@ class Panel // call the route action to check the result try { // trigger hook - $route = $kirby->apply('panel.route:before', compact('route', 'path', 'method'), 'route'); + $route = $kirby->apply( + 'panel.route:before', + compact('route', 'path', 'method'), + 'route' + ); // check for access before executing area routes if ($auth !== false) { @@ -310,7 +307,11 @@ class Panel 'type' => $type ]); - return $kirby->apply('panel.route:after', compact('route', 'path', 'method', 'response'), 'response'); + return $kirby->apply( + 'panel.route:after', + compact('route', 'path', 'method', 'response'), + 'response' + ); }); } @@ -341,7 +342,9 @@ class Panel static::routesForViews($areaId, $area), static::routesForSearches($areaId, $area), static::routesForDialogs($areaId, $area), + static::routesForDrawers($areaId, $area), static::routesForDropdowns($areaId, $area), + static::routesForRequests($areaId, $area), ); } @@ -362,7 +365,7 @@ class Panel // catch all route $routes[] = [ 'pattern' => '(:all)', - 'action' => fn () => 'The view could not be found' + 'action' => fn (string $pattern) => 'Could not find Panel view for route: ' . $pattern ]; return $routes; @@ -376,26 +379,33 @@ class Panel $dialogs = $area['dialogs'] ?? []; $routes = []; - foreach ($dialogs as $key => $dialog) { - // create the full pattern with dialogs prefix - $pattern = 'dialogs/' . trim(($dialog['pattern'] ?? $key), '/'); + foreach ($dialogs as $dialogId => $dialog) { + $routes = array_merge($routes, Dialog::routes( + id: $dialogId, + areaId: $areaId, + prefix: 'dialogs', + options: $dialog + )); + } - // load event - $routes[] = [ - 'pattern' => $pattern, - 'type' => 'dialog', - 'area' => $areaId, - 'action' => $dialog['load'] ?? fn () => 'The load handler for your dialog is missing' - ]; + return $routes; + } - // submit event - $routes[] = [ - 'pattern' => $pattern, - 'type' => 'dialog', - 'area' => $areaId, - 'method' => 'POST', - 'action' => $dialog['submit'] ?? fn () => 'Your dialog does not define a submit handler' - ]; + /** + * Extract all routes from an area + */ + public static function routesForDrawers(string $areaId, array $area): array + { + $drawers = $area['drawers'] ?? []; + $routes = []; + + foreach ($drawers as $drawerId => $drawer) { + $routes = array_merge($routes, Drawer::routes( + id: $drawerId, + areaId: $areaId, + prefix: 'drawers', + options: $drawer + )); } return $routes; @@ -409,27 +419,28 @@ class Panel $dropdowns = $area['dropdowns'] ?? []; $routes = []; - foreach ($dropdowns as $name => $dropdown) { - // Handle shortcuts for dropdowns. The name is the pattern - // and options are defined in a Closure - if ($dropdown instanceof Closure) { - $dropdown = [ - 'pattern' => $name, - 'action' => $dropdown - ]; - } + foreach ($dropdowns as $dropdownId => $dropdown) { + $routes = array_merge($routes, Dropdown::routes( + id: $dropdownId, + areaId: $areaId, + prefix: 'dropdowns', + options: $dropdown + )); + } - // create the full pattern with dropdowns prefix - $pattern = 'dropdowns/' . trim(($dropdown['pattern'] ?? $name), '/'); + return $routes; + } - // load event - $routes[] = [ - 'pattern' => $pattern, - 'type' => 'dropdown', - 'area' => $areaId, - 'method' => 'GET|POST', - 'action' => $dropdown['options'] ?? $dropdown['action'] - ]; + /** + * Extract all routes from an area + */ + public static function routesForRequests(string $areaId, array $area): array + { + $routes = $area['requests'] ?? []; + + foreach ($routes as $key => $route) { + $routes[$key]['area'] = $areaId; + $routes[$key]['type'] = 'request'; } return $routes; @@ -453,9 +464,13 @@ class Panel 'type' => 'search', 'area' => $areaId, 'action' => function () use ($params) { - $request = App::instance()->request(); + $kirby = App::instance(); + $request = $kirby->request(); + $query = $request->get('query'); + $limit = (int)$request->get('limit', $kirby->option('panel.search.limit', 10)); + $page = (int)$request->get('page', 1); - return $params['query']($request->get('query')); + return $params['query']($query, $limit, $page); } ]; } @@ -474,7 +489,18 @@ class Panel foreach ($views as $view) { $view['area'] = $areaId; $view['type'] = 'view'; - $routes[] = $view; + + $when = $view['when'] ?? null; + unset($view['when']); + + // enable the route by default, but if there is a + // when condition closure, it must return `true` + if ( + $when instanceof Closure === false || + $when($view, $area) === true + ) { + $routes[] = $view; + } } return $routes; @@ -537,7 +563,7 @@ class Panel * Creates an absolute Panel URL * independent of the Panel slug config */ - public static function url(string|null $url = null): string + public static function url(string|null $url = null, array $options = []): string { // only touch relative paths if (Url::isAbsolute($url) === false) { @@ -559,7 +585,7 @@ class Panel } // create an absolute URL - $url = CmsUrl::to($path); + $url = CmsUrl::to($path, $options); } return $url; diff --git a/kirby/src/Panel/Request.php b/kirby/src/Panel/Request.php new file mode 100644 index 0000000..9656d7d --- /dev/null +++ b/kirby/src/Panel/Request.php @@ -0,0 +1,24 @@ + + * @link https://getkirby.com + * @copyright Bastian Allgeier + * @license https://getkirby.com/license + */ +class Request +{ + /** + * Renders request responses + */ + public static function response($data, array $options = []): Response + { + $data = Json::responseData($data); + return Panel::json($data, $data['code'] ?? 200); + } +} diff --git a/kirby/src/Panel/Search.php b/kirby/src/Panel/Search.php index 984d38d..f9b4295 100644 --- a/kirby/src/Panel/Search.php +++ b/kirby/src/Panel/Search.php @@ -22,9 +22,17 @@ class Search extends Json public static function response($data, array $options = []): Response { - if (is_array($data) === true) { + if ( + is_array($data) === true && + array_key_exists('results', $data) === false + ) { $data = [ - 'results' => $data + 'results' => $data, + 'pagination' => [ + 'page' => 1, + 'limit' => $total = count($data), + 'total' => $total + ] ]; } diff --git a/kirby/src/Panel/Site.php b/kirby/src/Panel/Site.php index fc32b9d..7286098 100644 --- a/kirby/src/Panel/Site.php +++ b/kirby/src/Panel/Site.php @@ -3,6 +3,7 @@ namespace Kirby\Panel; use Kirby\Cms\File as CmsFile; +use Kirby\Cms\ModelWithContent; use Kirby\Filesystem\Asset; /** @@ -17,6 +18,11 @@ use Kirby\Filesystem\Asset; */ class Site extends Model { + /** + * @var \Kirby\Cms\Site + */ + protected ModelWithContent $model; + /** * Returns the setup for a dropdown option * which is used in the changes dropdown @@ -65,6 +71,7 @@ class Site extends Model 'link' => $this->url(true), 'previewUrl' => $this->model->previewUrl(), 'title' => $this->model->title()->toString(), + 'uuid' => fn () => $this->model->uuid()?->toString(), ] ]); } diff --git a/kirby/src/Panel/User.php b/kirby/src/Panel/User.php index 3b8ae2e..17f641e 100644 --- a/kirby/src/Panel/User.php +++ b/kirby/src/Panel/User.php @@ -3,6 +3,7 @@ namespace Kirby\Panel; use Kirby\Cms\File as CmsFile; +use Kirby\Cms\ModelWithContent; use Kirby\Cms\Translation; use Kirby\Cms\Url; use Kirby\Filesystem\Asset; @@ -20,6 +21,11 @@ use Kirby\Toolkit\I18n; */ class User extends Model { + /** + * @var \Kirby\Cms\User + */ + protected ModelWithContent $model; + /** * Breadcrumb array */ @@ -64,9 +70,18 @@ class User extends Model 'dialog' => $url . '/changeRole', 'icon' => 'bolt', 'text' => I18n::translate('user.changeRole'), - 'disabled' => $this->isDisabledDropdownOption('changeRole', $options, $permissions) + 'disabled' => $this->isDisabledDropdownOption('changeRole', $options, $permissions) || $this->model->roles()->count() < 2 ]; + $result[] = [ + 'dialog' => $url . '/changeLanguage', + 'icon' => 'translate', + 'text' => I18n::translate('user.changeLanguage'), + 'disabled' => $this->isDisabledDropdownOption('changeLanguage', $options, $permissions) + ]; + + $result[] = '-'; + $result[] = [ 'dialog' => $url . '/changePassword', 'icon' => 'key', @@ -74,12 +89,23 @@ class User extends Model 'disabled' => $this->isDisabledDropdownOption('changePassword', $options, $permissions) ]; - $result[] = [ - 'dialog' => $url . '/changeLanguage', - 'icon' => 'globe', - 'text' => I18n::translate('user.changeLanguage'), - 'disabled' => $this->isDisabledDropdownOption('changeLanguage', $options, $permissions) - ]; + if ($this->model->kirby()->system()->is2FAWithTOTP() === true) { + if ($account || $this->model->kirby()->user()->isAdmin()) { + if ($this->model->secret('totp') !== null) { + $result[] = [ + 'dialog' => $url . '/totp/disable', + 'icon' => 'qr-code', + 'text' => I18n::translate('login.totp.disable.option'), + ]; + } elseif ($account) { + $result[] = [ + 'dialog' => $url . '/totp/enable', + 'icon' => 'qr-code', + 'text' => I18n::translate('login.totp.enable.option') + ]; + } + } + } $result[] = '-'; @@ -192,18 +218,22 @@ class User extends Model */ public function props(): array { - $user = $this->model; - $account = $user->isLoggedIn(); - $avatar = $user->avatar(); + $user = $this->model; + $account = $user->isLoggedIn(); + $permissions = $this->options(); return array_merge( parent::props(), - $account ? [] : $this->prevNext(), + $this->prevNext(), [ - 'blueprint' => $this->model->role()->name(), + 'blueprint' => $this->model->role()->name(), + 'canChangeEmail' => $permissions['changeEmail'], + 'canChangeLanguage' => $permissions['changeLanguage'], + 'canChangeName' => $permissions['changeName'], + 'canChangeRole' => $this->model->roles()->count() > 1, 'model' => [ 'account' => $account, - 'avatar' => $avatar ? $avatar->url() : null, + 'avatar' => $user->avatar()?->url(), 'content' => $this->content(), 'email' => $user->email(), 'id' => $user->id(), @@ -212,6 +242,7 @@ class User extends Model 'name' => $user->name()->toString(), 'role' => $user->role()->title(), 'username' => $user->username(), + 'uuid' => fn () => $user->uuid()?->toString() ] ] ); diff --git a/kirby/src/Panel/UserTotpDisableDialog.php b/kirby/src/Panel/UserTotpDisableDialog.php new file mode 100644 index 0000000..7050cc7 --- /dev/null +++ b/kirby/src/Panel/UserTotpDisableDialog.php @@ -0,0 +1,114 @@ + + * @link https://getkirby.com + * @copyright Bastian Allgeier + * @license https://getkirby.com/license + */ +class UserTotpDisableDialog +{ + public App $kirby; + public User $user; + + public function __construct( + string|null $id = null + ) { + $this->kirby = App::instance(); + $this->user = $id ? Find::user($id) : $this->kirby->user(); + } + + /** + * Returns the Panel dialog state when opening the dialog + */ + public function load(): array + { + $currentUser = $this->kirby->user(); + $submitBtn = [ + 'text' => I18n::translate('disable'), + 'icon' => 'protected', + 'theme' => 'negative' + ]; + + // admins can disable TOTP for other users without + // entering their password (but not for themselves) + if ( + $currentUser->isAdmin() === true && + $currentUser->is($this->user) === false + ) { + $name = $this->user->name()->or($this->user->email()); + + return [ + 'component' => 'k-remove-dialog', + 'props' => [ + 'text' => I18n::template('login.totp.disable.admin', ['user' => Escape::html($name)]), + 'submitButton' => $submitBtn, + ] + ]; + } + + // everybody else + return [ + 'component' => 'k-form-dialog', + 'props' => [ + 'fields' => [ + 'password' => [ + 'type' => 'password', + 'required' => true, + 'counter' => false, + 'label' => I18n::translate('login.totp.disable.label'), + 'help' => I18n::translate('login.totp.disable.help'), + ] + ], + 'submitButton' => $submitBtn, + ] + ]; + } + + /** + * Removes the user's TOTP secret when the dialog is submitted + */ + public function submit(): array + { + $password = $this->kirby->request()->get('password'); + + try { + if ($this->kirby->user()->is($this->user) === true) { + $this->user->validatePassword($password); + } elseif ($this->kirby->user()->isAdmin() === false) { + throw new PermissionException('You are not allowed to disable TOTP for other users'); + } + + // Remove the TOTP secret from the account + $this->user->changeTotp(null); + + return [ + 'message' => I18n::translate('login.totp.disable.success') + ]; + } catch (InvalidArgumentException $e) { + // Catch and re-throw exception so that any + // Unauthenticated exception for incorrect passwords + // does not trigger a logout + throw new InvalidArgumentException([ + 'key' => $e->getKey(), + 'data' => $e->getData(), + 'fallback' => $e->getMessage(), + 'previous' => $e + ]); + } + } +} diff --git a/kirby/src/Panel/UserTotpEnableDialog.php b/kirby/src/Panel/UserTotpEnableDialog.php new file mode 100644 index 0000000..e2917db --- /dev/null +++ b/kirby/src/Panel/UserTotpEnableDialog.php @@ -0,0 +1,95 @@ + + * @link https://getkirby.com + * @copyright Bastian Allgeier + * @license https://getkirby.com/license + */ +class UserTotpEnableDialog +{ + public App $kirby; + public Totp $totp; + public User $user; + + public function __construct() + { + $this->kirby = App::instance(); + $this->user = $this->kirby->user(); + } + + /** + * Returns the Panel dialog state when opening the dialog + */ + public function load(): array + { + return [ + 'component' => 'k-totp-dialog', + 'props' => [ + 'qr' => $this->qr()->toSvg(size: '100%'), + 'value' => ['secret' => $this->secret()] + ] + ]; + } + + /** + * Creates a QR code with a new TOTP secret for the user + */ + public function qr(): QrCode + { + $issuer = $this->kirby->site()->title(); + $label = $this->user->email(); + $uri = $this->totp()->uri($issuer, $label); + return new QrCode($uri); + } + + public function secret(): string + { + return $this->totp()->secret(); + } + + /** + * Changes the user's TOTP secret when the dialog is submitted + */ + public function submit(): array + { + $secret = $this->kirby->request()->get('secret'); + $confirm = $this->kirby->request()->get('confirm'); + + if ($confirm === null) { + throw new InvalidArgumentException( + ['key' => 'login.totp.confirm.missing'] + ); + } + + if ($this->totp($secret)->verify($confirm) === false) { + throw new InvalidArgumentException( + ['key' => 'login.totp.confirm.invalid'] + ); + } + + $this->user->changeTotp($secret); + + return [ + 'message' => I18n::translate('login.totp.enable.success') + ]; + } + + public function totp(string|null $secret = null): Totp + { + return $this->totp ??= new Totp($secret); + } +} diff --git a/kirby/src/Panel/View.php b/kirby/src/Panel/View.php index 328fd0a..c857710 100644 --- a/kirby/src/Panel/View.php +++ b/kirby/src/Panel/View.php @@ -2,12 +2,10 @@ namespace Kirby\Panel; -use Closure; use Kirby\Cms\App; use Kirby\Exception\Exception; use Kirby\Http\Response; use Kirby\Toolkit\A; -use Kirby\Toolkit\I18n; use Kirby\Toolkit\Str; use Throwable; @@ -40,7 +38,9 @@ class View return static::applyOnly($data, $only); } - $globals = $request->header('X-Fiber-Globals') ?? $request->get('_globals'); + $globals = + $request->header('X-Fiber-Globals') ?? + $request->get('_globals'); if (empty($globals) === false) { return static::applyGlobals($data, $globals); @@ -56,8 +56,10 @@ class View * A global request can be activated with the `X-Fiber-Globals` header or the * `_globals` query parameter. */ - public static function applyGlobals(array $data, string|null $globals = null): array - { + public static function applyGlobals( + array $data, + string|null $globals = null + ): array { // split globals string into an array of fields $globalKeys = Str::split($globals, ','); @@ -86,8 +88,10 @@ class View * Such requests can fetch shared data or globals. * Globals will be loaded on demand. */ - public static function applyOnly(array $data, string|null $only = null): array - { + public static function applyOnly( + array $data, + string|null $only = null + ): array { // split include string into an array of fields $onlyKeys = Str::split($only, ','); @@ -115,9 +119,7 @@ class View } // Nest dotted keys in array but ignore $translation - return A::nest($result, [ - '$translation' - ]); + return A::nest($result, ['$translation']); } /** @@ -146,14 +148,18 @@ class View return [ '$direction' => function () use ($kirby, $multilang, $language, $user) { if ($multilang === true && $language && $user) { - $isDefault = $language->direction() === $kirby->defaultLanguage()->direction(); - $isFromUser = $language->code() === $user->language(); + $default = $kirby->defaultLanguage(); - if ($isDefault === false && $isFromUser === false) { + if ( + $language->direction() !== $default->direction() && + $language->code() !== $user->language() + ) { return $language->direction(); } } }, + '$dialog' => null, + '$drawer' => null, '$language' => function () use ($kirby, $multilang, $language) { if ($multilang === true && $language) { return [ @@ -178,15 +184,20 @@ class View return []; }, - '$menu' => function () use ($options, $permissions) { - return static::menu($options['areas'] ?? [], $permissions, $options['area']['id'] ?? null); + '$menu' => function () use ($options, $permissions) { + $menu = new Menu( + $options['areas'] ?? [], + $permissions, + $options['area']['id'] ?? null + ); + return $menu->entries(); }, '$permissions' => $permissions, - '$license' => (bool)$kirby->system()->license(), - '$multilang' => $multilang, - '$searches' => static::searches($options['areas'] ?? [], $permissions), - '$url' => $kirby->request()->url()->toString(), - '$user' => function () use ($user) { + '$license' => $kirby->system()->license()->status()->value(), + '$multilang' => $multilang, + '$searches' => static::searches($options['areas'] ?? [], $permissions), + '$url' => $kirby->request()->url()->toString(), + '$user' => function () use ($user) { if ($user) { return [ 'email' => $user->email(), @@ -204,17 +215,25 @@ class View 'breadcrumb' => [], 'code' => 200, 'path' => Str::after($kirby->path(), '/'), - 'timestamp' => (int)(microtime(true) * 1000), 'props' => [], - 'search' => $kirby->option('panel.search.type', 'pages') + 'query' => App::instance()->request()->query()->toArray(), + 'referrer' => Panel::referrer(), + 'search' => $kirby->option('panel.search.type', 'pages'), + 'timestamp' => (int)(microtime(true) * 1000), ]; - $view = array_replace_recursive($defaults, $options['area'] ?? [], $view); + $view = array_replace_recursive( + $defaults, + $options['area'] ?? [], + $view + ); // make sure that views and dialogs are gone unset( $view['dialogs'], + $view['drawers'], $view['dropdowns'], + $view['requests'], $view['searches'], $view['views'] ); @@ -255,17 +274,14 @@ class View $kirby = App::instance(); return [ - '$config' => function () use ($kirby) { - return [ - 'debug' => $kirby->option('debug', false), - 'kirbytext' => $kirby->option('panel.kirbytext', true), - 'search' => [ - 'limit' => $kirby->option('panel.search.limit', 10), - 'type' => $kirby->option('panel.search.type', 'pages') - ], - 'translation' => $kirby->option('panel.language', 'en'), - ]; - }, + '$config' => fn () => [ + 'api' => [ + 'methodOverwrite' => $kirby->option('api.methodOverwrite', true) + ], + 'debug' => $kirby->option('debug', false), + 'kirbytext' => $kirby->option('panel.kirbytext', true), + 'translation' => $kirby->option('panel.language', 'en'), + ], '$system' => function () use ($kirby) { $locales = []; @@ -303,67 +319,6 @@ class View ]; } - /** - * Creates the menu for the topbar - */ - public static function menu(array|null $areas = [], array|null $permissions = [], string|null $current = null): array - { - $menu = []; - - // areas - foreach ($areas as $areaId => $area) { - $access = $permissions['access'][$areaId] ?? true; - - // areas without access permissions get skipped entirely - if ($access === false) { - continue; - } - - // fetch custom menu settings from the area definition - $menuSetting = $area['menu'] ?? false; - - // menu settings can be a callback that can return true, false or disabled - if ($menuSetting instanceof Closure) { - $menuSetting = $menuSetting($areas, $permissions, $current); - } - - // false will remove the area entirely just like with - // disabled permissions - if ($menuSetting === false) { - continue; - } - - $menu[] = [ - 'current' => $areaId === $current, - 'disabled' => $menuSetting === 'disabled', - 'icon' => $area['icon'], - 'id' => $areaId, - 'link' => $area['link'], - 'text' => $area['label'], - ]; - } - - $menu[] = '-'; - $menu[] = [ - 'current' => $current === 'account', - 'icon' => 'account', - 'id' => 'account', - 'link' => 'account', - 'disabled' => ($permissions['access']['account'] ?? false) === false, - 'text' => I18n::translate('view.account'), - ]; - $menu[] = '-'; - - // logout - $menu[] = [ - 'icon' => 'logout', - 'id' => 'logout', - 'link' => 'logout', - 'text' => I18n::translate('logout') - ]; - return $menu; - } - /** * Renders the main panel view either as * JSON response or full HTML document based @@ -375,15 +330,15 @@ class View if ($data instanceof Redirect) { return Response::redirect($data->location(), $data->code()); - // handle Kirby exceptions + // handle Kirby exceptions } elseif ($data instanceof Exception) { $data = static::error($data->getMessage(), $data->getHttpCode()); - // handle regular exceptions + // handle regular exceptions } elseif ($data instanceof Throwable) { $data = static::error($data->getMessage(), 500); - // only expect arrays from here on + // only expect arrays from here on } elseif (is_array($data) === false) { $data = static::error('Invalid Panel response', 500); } @@ -414,13 +369,17 @@ class View { $searches = []; - foreach ($areas as $area) { - foreach ($area['searches'] ?? [] as $id => $params) { - $searches[$id] = [ - 'icon' => $params['icon'] ?? 'search', - 'label' => $params['label'] ?? Str::ucfirst($id), - 'id' => $id - ]; + foreach ($areas as $areaId => $area) { + // by default, all areas are accessible unless + // the permissions are explicitly set to false + if (($permissions['access'][$areaId] ?? true) !== false) { + foreach ($area['searches'] ?? [] as $id => $params) { + $searches[$id] = [ + 'icon' => $params['icon'] ?? 'search', + 'label' => $params['label'] ?? Str::ucfirst($id), + 'id' => $id + ]; + } } } return $searches; diff --git a/kirby/src/Parsley/Element.php b/kirby/src/Parsley/Element.php index 2deae25..649c5a3 100644 --- a/kirby/src/Parsley/Element.php +++ b/kirby/src/Parsley/Element.php @@ -21,22 +21,21 @@ use Kirby\Toolkit\Str; */ class Element { - protected array $marks; - protected DOMElement $node; - - public function __construct(DOMElement $node, array $marks = []) - { - $this->marks = $marks; - $this->node = $node; + public function __construct( + protected DOMElement $node, + protected array $marks = [] + ) { } /** * The returns the attribute value or * the given fallback if the attribute does not exist */ - public function attr(string $attr, string|null $fallback = null): string|null - { - if ($this->node->hasAttribute($attr)) { + public function attr( + string $attr, + string|null $fallback = null + ): string|null { + if ($this->node->hasAttribute($attr) === true) { return $this->node->getAttribute($attr) ?? $fallback; } @@ -112,7 +111,9 @@ class Element */ public function innerHtml(array|null $marks = null): string { - return (new Inline($this->node, $marks ?? $this->marks))->innerHtml(); + $marks ??= $this->marks; + $inline = new Inline($this->node, $marks); + return $inline->innerHtml(); } /** diff --git a/kirby/src/Parsley/Inline.php b/kirby/src/Parsley/Inline.php index 8c6a08e..32e6b08 100644 --- a/kirby/src/Parsley/Inline.php +++ b/kirby/src/Parsley/Inline.php @@ -2,7 +2,7 @@ namespace Kirby\Parsley; -use DOMComment; +use DOMElement; use DOMNode; use DOMNodeList; use DOMText; @@ -52,21 +52,22 @@ class Inline } /** - * Get all allowed attributes for a DOMNode + * Get all allowed attributes for a DOMElement * as clean array */ - public static function parseAttrs(DOMNode $node, array $marks = []): array - { + public static function parseAttrs( + DOMElement $node, + array $marks = [] + ): array { $attrs = []; $mark = $marks[$node->tagName]; $defaults = $mark['defaults'] ?? []; foreach ($mark['attrs'] ?? [] as $attr) { - if ($node->hasAttribute($attr)) { - $attrs[$attr] = $node->getAttribute($attr); - } else { - $attrs[$attr] = $defaults[$attr] ?? null; - } + $attrs[$attr] = match ($node->hasAttribute($attr)) { + true => $node->getAttribute($attr), + default => $defaults[$attr] ?? null + }; } return $attrs; @@ -76,8 +77,10 @@ class Inline * Parses all children and creates clean HTML * for each of them. */ - public static function parseChildren(DOMNodeList $children, array $marks): string - { + public static function parseChildren( + DOMNodeList $children, + array $marks + ): string { $html = ''; foreach ($children as $child) { $html .= static::parseNode($child, $marks); @@ -89,8 +92,10 @@ class Inline * Go through all child elements and create * clean inner HTML for them */ - public static function parseInnerHtml(DOMNode $node, array $marks = []): string|null - { + public static function parseInnerHtml( + DOMElement $node, + array $marks = [] + ): string|null { $html = static::parseChildren($node->childNodes, $marks); // trim the inner HTML for paragraphs @@ -115,33 +120,35 @@ class Inline return Html::encode($node->textContent); } - // ignore comments - if ($node instanceof DOMComment) { - return null; + if ($node instanceof DOMElement) { + // unknown marks + if (array_key_exists($node->tagName, $marks) === false) { + return static::parseChildren($node->childNodes, $marks); + } + + // collect all allowed attributes + $attrs = static::parseAttrs($node, $marks); + + // close self-closing elements + if (Html::isVoid($node->tagName) === true) { + return '<' . $node->tagName . Html::attr($attrs, null, ' ') . ' />'; + } + + $innerHtml = static::parseInnerHtml($node, $marks); + + // skip empty paragraphs + if ($innerHtml === null && $node->tagName === 'p') { + return null; + } + + // create the outer html for the element + $html = '<' . $node->tagName . Html::attr($attrs, null, ' ') . '>'; + $html .= $innerHtml; + $html .= 'tagName . '>'; + return $html; } - // unknown marks - if (array_key_exists($node->tagName, $marks) === false) { - return static::parseChildren($node->childNodes, $marks); - } - - // collect all allowed attributes - $attrs = static::parseAttrs($node, $marks); - - // close self-closing elements - if (Html::isVoid($node->tagName) === true) { - return '<' . $node->tagName . Html::attr($attrs, null, ' ') . ' />'; - } - - $innerHtml = static::parseInnerHtml($node, $marks); - - // skip empty paragraphs - if ($innerHtml === null && $node->tagName === 'p') { - return null; - } - - // create the outer html for the element - return '<' . $node->tagName . Html::attr($attrs, null, ' ') . '>' . $innerHtml . 'tagName . '>'; + return null; } /** diff --git a/kirby/src/Parsley/Parsley.php b/kirby/src/Parsley/Parsley.php index 460ecfb..b6d834b 100644 --- a/kirby/src/Parsley/Parsley.php +++ b/kirby/src/Parsley/Parsley.php @@ -40,10 +40,8 @@ class Parsley // or should be skipped if ($this->useXmlExtension() === false) { $this->blocks[] = [ - 'type' => 'markdown', - 'content' => [ - 'text' => $html, - ] + 'type' => 'markdown', + 'content' => ['text' => $html] ]; return; } @@ -104,7 +102,10 @@ class Parsley } foreach ($element->childNodes as $childNode) { - if ($this->isBlock($childNode) === true || $this->containsBlock($childNode)) { + if ( + $this->isBlock($childNode) === true || + $this->containsBlock($childNode) + ) { return true; } } @@ -129,7 +130,7 @@ class Parsley $html = []; foreach ($this->inline as $inline) { - $node = new Inline($inline, $this->marks); + $node = new Inline($inline, $this->marks); $html[] = $node->innerHTML(); } @@ -161,11 +162,11 @@ class Parsley */ public function isBlock(DOMNode $element): bool { - if ($element instanceof DOMElement === false) { - return false; + if ($element instanceof DOMElement) { + return array_key_exists($element->tagName, $this->nodes) === true; } - return array_key_exists($element->tagName, $this->nodes) === true; + return false; } /** @@ -204,10 +205,14 @@ class Parsley $lastItem = $this->blocks[$lastIndex] ?? null; // merge with previous block - if ($block['type'] === 'text' && $lastItem && $lastItem['type'] === 'text') { + if ( + $block['type'] === 'text' && + $lastItem && + $lastItem['type'] === 'text' + ) { $this->blocks[$lastIndex]['content']['text'] .= ' ' . $block['content']['text']; - // append + // append } else { $this->blocks[] = $block; } @@ -227,15 +232,18 @@ class Parsley } // inline context - if ($this->isInline($element)) { + if ($this->isInline($element) === true) { $this->inline[] = $element; return true; - } else { - $this->endInlineBlock(); } + $this->endInlineBlock(); + // known block nodes if ($this->isBlock($element) === true) { + /** + * @var DOMElement $element + */ if ($parser = ($this->nodes[$element->tagName]['parse'] ?? null)) { if ($result = $parser(new Element($element, $this->marks))) { $this->blocks[] = $result; @@ -246,6 +254,9 @@ class Parsley // has only unknown children (div, etc.) if ($this->containsBlock($element) === false) { + /** + * @var DOMElement $element + */ if (in_array($element->tagName, $this->skip) === true) { return false; } diff --git a/kirby/src/Parsley/Schema/Blocks.php b/kirby/src/Parsley/Schema/Blocks.php index 1f54619..a72106c 100644 --- a/kirby/src/Parsley/Schema/Blocks.php +++ b/kirby/src/Parsley/Schema/Blocks.php @@ -23,8 +23,7 @@ class Blocks extends Plain { public function blockquote(Element $node): array { - $citation = null; - $text = []; + $text = []; // get all the text for the quote foreach ($node->children() as $child) { @@ -36,7 +35,8 @@ class Blocks extends Plain $child instanceof DOMElement && $child->tagName !== 'footer' ) { - $text[] = (new Element($child))->innerHTML($this->marks()); + $element = new Element($child); + $text[] = $element->innerHTML($this->marks()); } } @@ -44,9 +44,7 @@ class Blocks extends Plain $text = implode('', array_filter($text)); // get the citation from the footer - if ($footer = $node->find('footer')) { - $citation = $footer->innerHTML($this->marks()); - } + $citation = $node->find('footer')?->innerHTML($this->marks()); return [ 'content' => [ @@ -115,15 +113,12 @@ class Blocks extends Plain public function iframe(Element $node): array { - $caption = null; - $src = $node->attr('src'); + $src = $node->attr('src'); + $figcaption = $node->find('ancestor::figure[1]//figcaption'); + $caption = $figcaption?->innerHTML($this->marks()); - if ($figcaption = $node->find('ancestor::figure[1]//figcaption')) { - $caption = $figcaption->innerHTML($this->marks()); - - // avoid parsing the caption twice - $figcaption->remove(); - } + // avoid parsing the caption twice + $figcaption?->remove(); // reverse engineer video URLs if (preg_match('!player.vimeo.com\/video\/([0-9]+)!i', $src, $array) === 1) { @@ -157,19 +152,12 @@ class Blocks extends Plain public function img(Element $node): array { - $caption = null; - $link = null; + $link = $node->find('ancestor::a')?->attr('href'); + $figcaption = $node->find('ancestor::figure[1]//figcaption'); + $caption = $figcaption?->innerHTML($this->marks()); - if ($figcaption = $node->find('ancestor::figure[1]//figcaption')) { - $caption = $figcaption->innerHTML($this->marks()); - - // avoid parsing the caption twice - $figcaption->remove(); - } - - if ($a = $node->find('ancestor::a')) { - $link = $a->attr('href'); - } + // avoid parsing the caption twice + $figcaption?->remove(); return [ 'content' => [ @@ -198,19 +186,21 @@ class Blocks extends Plain $innerHtml .= $child->textContent; } elseif ($child instanceof DOMElement) { $child = new Element($child); - - if (in_array($child->tagName(), ['ul', 'ol']) === true) { - $innerHtml .= $this->list($child); - } else { - $innerHtml .= $child->innerHTML($this->marks()); - } + $list = ['ul', 'ol']; + $innerHtml .= match (in_array($child->tagName(), $list)) { + true => $this->list($child), + default => $child->innerHTML($this->marks()) + }; } } $html[] = '
    • ' . trim($innerHtml) . '
    • '; } - return '<' . $node->tagName() . '>' . implode($html) . 'tagName() . '>'; + $outerHtml = '<' . $node->tagName() . '>'; + $outerHtml .= implode($html); + $outerHtml .= 'tagName() . '>'; + return $outerHtml; } /** diff --git a/kirby/src/Query/Expression.php b/kirby/src/Query/Expression.php index e9ff509..84928c3 100644 --- a/kirby/src/Query/Expression.php +++ b/kirby/src/Query/Expression.php @@ -25,7 +25,7 @@ class Expression /** * Parses an expression string into its parts */ - public static function factory(string $expression, Query $parent = null): static|Segments + public static function factory(string $expression, Query|null $parent = null): static|Segments { // split into different expression parts and operators $parts = static::parse($expression); @@ -62,7 +62,7 @@ class Expression return preg_split( '/\s+([\?\:]+)\s+|' . Arguments::OUTSIDE . '/', trim($string), - flags: PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY + flags: PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY ); } diff --git a/kirby/src/Query/Query.php b/kirby/src/Query/Query.php index 5469116..20cefa5 100644 --- a/kirby/src/Query/Query.php +++ b/kirby/src/Query/Query.php @@ -9,6 +9,7 @@ use Kirby\Cms\File; use Kirby\Cms\Page; use Kirby\Cms\Site; use Kirby\Cms\User; +use Kirby\Image\QrCode; use Kirby\Toolkit\I18n; /** @@ -120,18 +121,22 @@ Query::$entries['page'] = function (string $id): Page|null { return App::instance()->page($id); }; +Query::$entries['qr'] = function (string $data): QrCode { + return new QrCode($data); +}; + Query::$entries['site'] = function (): Site { return App::instance()->site(); }; Query::$entries['t'] = function ( string $key, - string|array $fallback = null, - string $locale = null + string|array|null $fallback = null, + string|null $locale = null ): string|null { return I18n::translate($key, $fallback, $locale); }; -Query::$entries['user'] = function (string $id = null): User|null { +Query::$entries['user'] = function (string|null $id = null): User|null { return App::instance()->user($id); }; diff --git a/kirby/src/Query/Segments.php b/kirby/src/Query/Segments.php index 5d2d009..c4069a1 100644 --- a/kirby/src/Query/Segments.php +++ b/kirby/src/Query/Segments.php @@ -28,7 +28,7 @@ class Segments extends Collection * Split query string into segments by dot * but not inside (nested) parens */ - public static function factory(string $query, Query $parent = null): static + public static function factory(string $query, Query|null $parent = null): static { $segments = static::parse($query); $position = 0; @@ -61,7 +61,7 @@ class Segments extends Collection return preg_split( '/(\??\.)|(\(([^()]+|(?2))*+\))(*SKIP)(*FAIL)/', trim($string), - flags: PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY + flags: PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY ); } diff --git a/kirby/src/Sane/DomHandler.php b/kirby/src/Sane/DomHandler.php index 960e549..2fe2e09 100644 --- a/kirby/src/Sane/DomHandler.php +++ b/kirby/src/Sane/DomHandler.php @@ -16,6 +16,8 @@ use Kirby\Toolkit\Dom; * @link https://getkirby.com * @copyright Bastian Allgeier * @license https://opensource.org/licenses/MIT + * + * @SuppressWarnings(PHPMD.LongVariable) */ class DomHandler extends Handler { @@ -43,6 +45,13 @@ class DomHandler extends Handler */ public static array|bool $allowedDomains = true; + /** + * Whether URLs that begin with `/` should be allowed even if the + * site index URL is in a subfolder (useful when using the HTML + * `` element where the sanitized code will be rendered) + */ + public static bool $allowHostRelativeUrls = true; + /** * Names of allowed XML processing instructions */ @@ -57,27 +66,34 @@ class DomHandler extends Handler /** * Sanitizes the given string * + * @param bool $isExternal Whether the string is from an external file + * that may be accessed directly + * * @throws \Kirby\Exception\InvalidArgumentException If the file couldn't be parsed */ - public static function sanitize(string $string): string + public static function sanitize(string $string, bool $isExternal = false): string { $dom = static::parse($string); - $dom->sanitize(static::options()); + $dom->sanitize(static::options($isExternal)); return $dom->toString(); } /** * Validates file contents * + * @param bool $isExternal Whether the string is from an external file + * that may be accessed directly + * * @throws \Kirby\Exception\InvalidArgumentException If the file couldn't be parsed * @throws \Kirby\Exception\InvalidArgumentException If the file didn't pass validation */ - public static function validate(string $string): void + public static function validate(string $string, bool $isExternal = false): void { - $dom = static::parse($string); - $errors = $dom->sanitize(static::options()); + $dom = static::parse($string); + $errors = $dom->sanitize(static::options($isExternal)); + + // there may be multiple errors, we can only throw one of them at a time if (count($errors) > 0) { - // there may be multiple errors, we can only throw one of them at a time throw $errors[0]; } } @@ -88,7 +104,7 @@ class DomHandler extends Handler * * @return array Array with exception objects for each modification */ - public static function sanitizeAttr(DOMAttr $attr): array + public static function sanitizeAttr(DOMAttr $attr, array $options): array { // to be extended in child classes return []; @@ -100,7 +116,7 @@ class DomHandler extends Handler * * @return array Array with exception objects for each modification */ - public static function sanitizeElement(DOMElement $element): array + public static function sanitizeElement(DOMElement $element, array $options): array { // to be extended in child classes return []; @@ -110,7 +126,7 @@ class DomHandler extends Handler * Custom callback for additional doctype validation * @internal */ - public static function validateDoctype(DOMDocumentType $doctype): void + public static function validateDoctype(DOMDocumentType $doctype, array $options): void { // to be extended in child classes } @@ -118,17 +134,29 @@ class DomHandler extends Handler /** * Returns the sanitization options for the handler * (to be extended in child classes) + * + * @param bool $isExternal Whether the string is from an external file + * that may be accessed directly */ - protected static function options(): array + protected static function options(bool $isExternal): array { - return [ - 'allowedDataUris' => static::$allowedDataUris, - 'allowedDomains' => static::$allowedDomains, - 'allowedPIs' => static::$allowedPIs, - 'attrCallback' => [static::class, 'sanitizeAttr'], - 'doctypeCallback' => [static::class, 'validateDoctype'], - 'elementCallback' => [static::class, 'sanitizeElement'], + $options = [ + 'allowedDataUris' => static::$allowedDataUris, + 'allowedDomains' => static::$allowedDomains, + 'allowHostRelativeUrls' => static::$allowHostRelativeUrls, + 'allowedPIs' => static::$allowedPIs, + 'attrCallback' => [static::class, 'sanitizeAttr'], + 'doctypeCallback' => [static::class, 'validateDoctype'], + 'elementCallback' => [static::class, 'sanitizeElement'], ]; + + // never allow host-relative URLs in external files as we + // cannot set a `` element for them when accessed directly + if ($isExternal === true) { + $options['allowHostRelativeUrls'] = false; + } + + return $options; } /** diff --git a/kirby/src/Sane/Handler.php b/kirby/src/Sane/Handler.php index 5b97044..7dfcd98 100644 --- a/kirby/src/Sane/Handler.php +++ b/kirby/src/Sane/Handler.php @@ -21,8 +21,11 @@ abstract class Handler { /** * Sanitizes the given string + * + * @param bool $isExternal Whether the string is from an external file + * that may be accessed directly */ - abstract public static function sanitize(string $string): string; + abstract public static function sanitize(string $string, bool $isExternal = false): string; /** * Sanitizes the contents of a file by overwriting @@ -33,17 +36,21 @@ abstract class Handler */ public static function sanitizeFile(string $file): void { - $sanitized = static::sanitize(static::readFile($file)); + $content = static::readFile($file); + $sanitized = static::sanitize($content, isExternal: true); F::write($file, $sanitized); } /** * Validates file contents * + * @param bool $isExternal Whether the string is from an external file + * that may be accessed directly + * * @throws \Kirby\Exception\InvalidArgumentException If the file didn't pass validation * @throws \Kirby\Exception\Exception On other errors */ - abstract public static function validate(string $string): void; + abstract public static function validate(string $string, bool $isExternal = false): void; /** * Validates the contents of a file @@ -54,7 +61,8 @@ abstract class Handler */ public static function validateFile(string $file): void { - static::validate(static::readFile($file)); + $content = static::readFile($file); + static::validate($content, isExternal: true); } /** diff --git a/kirby/src/Sane/Html.php b/kirby/src/Sane/Html.php index c0b00aa..0766e36 100644 --- a/kirby/src/Sane/Html.php +++ b/kirby/src/Sane/Html.php @@ -107,10 +107,13 @@ class Html extends DomHandler /** * Returns the sanitization options for the handler + * + * @param bool $isExternal Whether the string is from an external file + * that may be accessed directly */ - protected static function options(): array + protected static function options(bool $isExternal): array { - return array_merge(parent::options(), [ + return array_merge(parent::options($isExternal), [ 'allowedAttrPrefixes' => static::$allowedAttrPrefixes, 'allowedAttrs' => static::$allowedAttrs, 'allowedNamespaces' => [], diff --git a/kirby/src/Sane/Sane.php b/kirby/src/Sane/Sane.php index d52ea65..c079e2a 100644 --- a/kirby/src/Sane/Sane.php +++ b/kirby/src/Sane/Sane.php @@ -36,10 +36,10 @@ class Sane * All registered handlers */ public static array $handlers = [ - 'html' => 'Kirby\Sane\Html', - 'svg' => 'Kirby\Sane\Svg', - 'svgz' => 'Kirby\Sane\Svgz', - 'xml' => 'Kirby\Sane\Xml', + 'html' => Html::class, + 'svg' => Svg::class, + 'svgz' => Svgz::class, + 'xml' => Xml::class, ]; /** @@ -49,16 +49,19 @@ class Sane * * @throws \Kirby\Exception\NotFoundException If no handler was found and `$lazy` was set to `false` */ - public static function handler(string $type, bool $lazy = false): Handler|null - { + public static function handler( + string $type, + bool $lazy = false + ): Handler|null { // normalize the type $type = mb_strtolower($type); // find a handler or alias - $alias = static::$aliases[$type] ?? null; - $handler = - static::$handlers[$type] ?? - ($alias ? static::$handlers[$alias] ?? null : null); + $handler = static::$handlers[$type] ?? null; + + if ($alias = static::$aliases[$type] ?? null) { + $handler ??= static::$handlers[$alias] ?? null; + } if (empty($handler) === false && class_exists($handler) === true) { return new $handler(); @@ -74,10 +77,13 @@ class Sane /** * Sanitizes the given string with the specified handler * @since 3.6.0 + * + * @param bool $isExternal Whether the string is from an external file + * that may be accessed directly */ - public static function sanitize(string $string, string $type): string + public static function sanitize(string $string, string $type, bool $isExternal = false): string { - return static::handler($type)->sanitize($string); + return static::handler($type)->sanitize($string, $isExternal); } /** @@ -96,8 +102,10 @@ class Sane * @throws \Kirby\Exception\NotFoundException If the handler was not found * @throws \Kirby\Exception\Exception On other errors */ - public static function sanitizeFile(string $file, string|bool $typeLazy = false): void - { + public static function sanitizeFile( + string $file, + string|bool $typeLazy = false + ): void { if (is_string($typeLazy) === true) { static::handler($typeLazy)->sanitizeFile($file); return; @@ -126,13 +134,16 @@ class Sane /** * Validates file contents with the specified handler * + * @param bool $isExternal Whether the string is from an external file + * that may be accessed directly + * * @throws \Kirby\Exception\InvalidArgumentException If the file didn't pass validation * @throws \Kirby\Exception\NotFoundException If the handler was not found * @throws \Kirby\Exception\Exception On other errors */ - public static function validate(string $string, string $type): void + public static function validate(string $string, string $type, bool $isExternal = false): void { - static::handler($type)->validate($string); + static::handler($type)->validate($string, $isExternal); } /** @@ -148,14 +159,18 @@ class Sane * @throws \Kirby\Exception\NotFoundException If the handler was not found * @throws \Kirby\Exception\Exception On other errors */ - public static function validateFile(string $file, string|bool $typeLazy = false): void - { + public static function validateFile( + string $file, + string|bool $typeLazy = false + ): void { if (is_string($typeLazy) === true) { static::handler($typeLazy)->validateFile($file); return; } - foreach (static::handlersForFile($file, $typeLazy === true) as $handler) { + $handlers = static::handlersForFile($file, $typeLazy === true); + + foreach ($handlers as $handler) { $handler->validateFile($file); } } @@ -167,8 +182,10 @@ class Sane * @param bool $lazy If set to `true`, undefined handlers are skipped * @return array<\Kirby\Sane\Handler> */ - protected static function handlersForFile(string $file, bool $lazy = false): array - { + protected static function handlersForFile( + string $file, + bool $lazy = false + ): array { $handlers = $handlerClasses = []; // all values that can be used for the handler search; @@ -180,7 +197,10 @@ class Sane $handlerClass = $handler ? get_class($handler) : null; // ensure that each handler class is only returned once - if ($handler && in_array($handlerClass, $handlerClasses) === false) { + if ( + $handler && + in_array($handlerClass, $handlerClasses) === false + ) { $handlers[] = $handler; $handlerClasses[] = $handlerClass; } diff --git a/kirby/src/Sane/Svg.php b/kirby/src/Sane/Svg.php index 2910272..1a947ec 100644 --- a/kirby/src/Sane/Svg.php +++ b/kirby/src/Sane/Svg.php @@ -392,7 +392,7 @@ class Svg extends Xml * * @return array Array with exception objects for each modification */ - public static function sanitizeAttr(DOMAttr $attr): array + public static function sanitizeAttr(DOMAttr $attr, array $options): array { $element = $attr->ownerElement; $name = $attr->name; @@ -406,8 +406,9 @@ class Svg extends Xml Str::startsWith($value, '#') === true ) { // find the target (used element) - $id = str_replace('"', '', mb_substr($value, 1)); - $target = (new DOMXPath($attr->ownerDocument))->query('//*[@id="' . $id . '"]')->item(0); + $id = str_replace('"', '', mb_substr($value, 1)); + $path = new DOMXPath($attr->ownerDocument); + $target = $path->query('//*[@id="' . $id . '"]')->item(0); // the target must not contain any other elements if ( @@ -431,14 +432,14 @@ class Svg extends Xml * * @return array Array with exception objects for each modification */ - public static function sanitizeElement(DOMElement $element): array + public static function sanitizeElement(DOMElement $element, array $options): array { $errors = []; // check for URLs inside - - - - - - - - - - {% if portfolio is not empty %} - - {% endif %} -
      - - - -
      - -
      -
      -
        - {% for image in images %} -
      • - {{ image.alt_text }} -
      • - {% endfor %} -
      -
      -
      - + {% endfor %}