diff --git a/plugins/cache-on-save/classes/PhotoRenumberer.php b/plugins/cache-on-save/classes/PhotoRenumberer.php
index ebf0bcd..9eb77f3 100644
--- a/plugins/cache-on-save/classes/PhotoRenumberer.php
+++ b/plugins/cache-on-save/classes/PhotoRenumberer.php
@@ -18,6 +18,14 @@ namespace Grav\Plugin\Shared;
* renamed. A crafted manifest entry naming the entry `.md`, a `.gpx`, or a
* `.meta.yaml` sidecar is silently skipped — it can never be renamed or clobbered.
* This guard lives here (not only in the callers) so every caller inherits it.
+ *
+ * Completeness: renumber() ALWAYS renumbers every image already in $dir, not just
+ * the manifest subset. $names only supplies the preferred ORDER; any on-disk image
+ * the manifest omits is appended at the end. This makes an incomplete/stale
+ * manifest (e.g. a second browser tab whose list predates a change) harmless —
+ * without it, an unlisted image left sitting at a target slot would be silently
+ * OVERWRITTEN (destroyed) by the second-phase rename. The reorder route trusts a
+ * client-supplied list, so this guard is what keeps it from losing photos.
*/
class PhotoRenumberer
{
@@ -26,13 +34,16 @@ class PhotoRenumberer
private const IMAGE_EXTS = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'heic', 'heif'];
/**
- * Rename the image files named in $names to photo-01..NN in that order, in $dir.
+ * Renumber every image in $dir to photo-01..NN, using $names as the preferred
+ * order and appending any unlisted on-disk images at the end.
*
* Two-phase via temp names so a target (photo-02.jpg) can't clobber a
* not-yet-moved source of the same name (e.g. a straight swap or the
* un-padded photo-N → photo-0N normalisation pass). Non-image and missing
- * files in $names are skipped and do not consume an index, so the surviving
- * images are always numbered contiguously from 01.
+ * files in $names are skipped and do not consume an index; a repeated name is
+ * counted once. Because every on-disk image becomes a target (see the
+ * completeness note on the class), the surviving images are numbered
+ * contiguously from 01 and no untouched file is ever overwritten.
*
* Idempotent: a file already at its correct padded name is left untouched,
* so re-running with the same manifest (e.g. an auto-retried reorder) is a
@@ -40,14 +51,18 @@ class PhotoRenumberer
*/
public static function renumber(string $dir, array $names): void
{
- // Keep only real image files, in the requested order — this is both the
- // security filter and what determines the pad width.
+ // Keep only real image files, in the requested order, de-duplicated —
+ // this is both the security filter and what determines the pad width.
$targets = [];
+ $seen = [];
foreach ($names as $name) {
if (!is_string($name) || $name === '') {
continue;
}
$base = basename(str_replace('\\', '/', $name));
+ if (isset($seen[$base])) {
+ continue; // a repeated name must not consume a second index
+ }
$src = $dir . DIRECTORY_SEPARATOR . $base;
if (!is_file($src)) {
continue; // not on disk — skip (idempotent for auto-retry)
@@ -56,11 +71,43 @@ class PhotoRenumberer
if (!in_array($ext, self::IMAGE_EXTS, true)) {
continue; // never rename the entry .md, a .gpx, or a sidecar
}
+ $seen[$base] = true;
$targets[] = [$src, $ext ?: 'jpg'];
}
+ // Completeness guard: append EVERY other image already in $dir that the
+ // manifest didn't list (natural name order), so an incomplete/stale
+ // manifest can't leave an unlisted image at a target slot for phase-2 to
+ // overwrite. Hidden files ('.'-prefixed temp/sidecar) are never targets.
+ $extra = [];
+ foreach (@scandir($dir) ?: [] as $f) {
+ if ($f === '' || $f[0] === '.' || isset($seen[$f])) {
+ continue;
+ }
+ $p = $dir . DIRECTORY_SEPARATOR . $f;
+ if (!is_file($p)) {
+ continue;
+ }
+ $ext = strtolower(pathinfo($f, PATHINFO_EXTENSION));
+ if (!in_array($ext, self::IMAGE_EXTS, true)) {
+ continue;
+ }
+ $extra[$f] = [$p, $ext ?: 'jpg'];
+ }
+ if ($extra) {
+ uksort($extra, 'strnatcasecmp');
+ foreach ($extra as $t) {
+ $targets[] = $t;
+ }
+ }
+
$width = max(2, strlen((string) count($targets)));
+ // Unique per-call token in the temp name so two concurrent renumbers on
+ // the same folder can't collide on a shared '.reorder-tmp-N' path and
+ // overwrite one photo's bytes.
+ $token = bin2hex(random_bytes(4));
+
$planned = [];
$i = 1;
foreach ($targets as [$src, $ext]) {
@@ -70,7 +117,7 @@ class PhotoRenumberer
$i++;
continue; // already correctly named — leave it
}
- $tmp = $dir . DIRECTORY_SEPARATOR . '.reorder-tmp-' . $i . '.' . $ext;
+ $tmp = $dir . DIRECTORY_SEPARATOR . '.reorder-tmp-' . $token . '-' . $i . '.' . $ext;
@rename($src, $tmp);
$planned[] = [$tmp, $final];
$i++;
diff --git a/plugins/entry-actions/classes/EntryActionsApiController.php b/plugins/entry-actions/classes/EntryActionsApiController.php
index 2d8497a..b8ad249 100644
--- a/plugins/entry-actions/classes/EntryActionsApiController.php
+++ b/plugins/entry-actions/classes/EntryActionsApiController.php
@@ -35,6 +35,11 @@ class EntryActionsApiController extends AbstractApiController
{
// Authenticated OWNER only (KTD8). getUser() throws 401 for anonymous.
$user = $this->getUser($request);
+ // Enforce the API-key scope cap (GHSA-x7hm) with the SAME permission the
+ // stock media/page-write endpoints require. The owner already holds it
+ // (their add/delete media uploads pass it), so this only caps a scoped
+ // key — it never blocks the legitimate owner.
+ $this->requirePermission($request, 'api.pages.write');
if (!EntryScopeGuard::isOwnerUser($this->grav, $user)) {
throw new ForbiddenException('Only the site owner can delete journal entries.');
}
@@ -73,12 +78,16 @@ class EntryActionsApiController extends AbstractApiController
* Filename safety is defence in depth: unsafe segments (containing '/' or '..')
* are dropped here, and PhotoRenumberer only ever renames files that already
* exist as image media in the folder — so a crafted order body can never touch
- * the entry .md, a .gpx or a .meta.yaml sidecar.
+ * the entry .md, a .gpx or a .meta.yaml sidecar. An incomplete `order` (e.g. a
+ * stale second tab) is safe too: PhotoRenumberer renumbers every on-disk image,
+ * appending any the manifest omits, so no photo is lost — `order` only sorts.
*/
public function reorderPhotos(ServerRequestInterface $request): ResponseInterface
{
// Authenticated OWNER only (KTD8). getUser() throws 401 for anonymous.
$user = $this->getUser($request);
+ // Enforce the API-key scope cap (GHSA-x7hm) — see deleteEntry above.
+ $this->requirePermission($request, 'api.pages.write');
if (!EntryScopeGuard::isOwnerUser($this->grav, $user)) {
throw new ForbiddenException('Only the site owner can reorder entry photos.');
}
diff --git a/themes/intotheeast/js/post/post-form.js b/themes/intotheeast/js/post/post-form.js
index 07a2ff4..832a963 100644
--- a/themes/intotheeast/js/post/post-form.js
+++ b/themes/intotheeast/js/post/post-form.js
@@ -92,7 +92,7 @@ Please report this to https://github.com/markedjs/marked.`,o){var f="
An error
-----
`]},_p={link:"URL for the link:",image:"URL of the image:"},Bp={locale:"en-US",format:{hour:"2-digit",minute:"2-digit"}},Np={bold:"**",code:"```",italic:"*"},Op={sbInit:"Attach files by drag and dropping or pasting from clipboard.",sbOnDragEnter:"Drop image to upload it.",sbOnDrop:"Uploading image #images_names#...",sbProgress:"Uploading #file_name#: #progress#%",sbOnUploaded:"Uploaded #image_name#",sizeUnits:" B, KB, MB"},Ip={noFileGiven:"You must select a file.",typeNotAllowed:"This image type is not allowed.",fileTooLarge:`Image #image_name# is too big (#image_size#).
-Maximum file size is #image_max_size#.`,importError:"Something went wrong when uploading the image #image_name#."};function te(o){o=o||{},o.parent=this;var l=!0;if(o.autoDownloadFontAwesome===!1&&(l=!1),o.autoDownloadFontAwesome!==!0)for(var s=document.styleSheets,a=0;a-1&&(l=!1);if(l){var f=document.createElement("link");f.rel="stylesheet",f.href="https://maxcdn.bootstrapcdn.com/font-awesome/latest/css/font-awesome.min.css",document.getElementsByTagName("head")[0].appendChild(f)}if(o.element)this.element=o.element;else if(o.element===null){console.log("EasyMDE: Error. No element was found.");return}if(o.toolbar===void 0){o.toolbar=[];for(var p in Ur)Object.prototype.hasOwnProperty.call(Ur,p)&&(p.indexOf("separator-")!=-1&&o.toolbar.push("|"),(Ur[p].default===!0||o.showIcons&&o.showIcons.constructor===Array&&o.showIcons.indexOf(p)!=-1)&&o.toolbar.push(p))}if(Object.prototype.hasOwnProperty.call(o,"previewClass")||(o.previewClass="editor-preview"),Object.prototype.hasOwnProperty.call(o,"status")||(o.status=["autosave","lines","words","cursor"],o.uploadImage&&o.status.unshift("upload-image")),o.previewRender||(o.previewRender=function(h){return this.parent.markdown(h)}),o.parsingConfig=sr({highlightFormatting:!0},o.parsingConfig||{}),o.insertTexts=sr({},Mp,o.insertTexts||{}),o.promptTexts=sr({},_p,o.promptTexts||{}),o.blockStyles=sr({},Np,o.blockStyles||{}),o.autosave!=null&&(o.autosave.timeFormat=sr({},Bp,o.autosave.timeFormat||{})),o.iconClassMap=sr({},Me,o.iconClassMap||{}),o.shortcuts=sr({},Cp,o.shortcuts||{}),o.maxHeight=o.maxHeight||void 0,o.direction=o.direction||"ltr",typeof o.maxHeight<"u"?o.minHeight=o.maxHeight:o.minHeight=o.minHeight||"300px",o.errorCallback=o.errorCallback||function(h){alert(h)},o.uploadImage=o.uploadImage||!1,o.imageMaxSize=o.imageMaxSize||2097152,o.imageAccept=o.imageAccept||"image/png, image/jpeg, image/gif, image/avif",o.imageTexts=sr({},Op,o.imageTexts||{}),o.errorMessages=sr({},Ip,o.errorMessages||{}),o.imagePathAbsolute=o.imagePathAbsolute||!1,o.imageCSRFName=o.imageCSRFName||"csrfmiddlewaretoken",o.imageCSRFHeader=o.imageCSRFHeader||!1,o.imageInputName=o.imageInputName||"image",o.autosave!=null&&o.autosave.unique_id!=null&&o.autosave.unique_id!=""&&(o.autosave.uniqueId=o.autosave.unique_id),o.overlayMode&&o.overlayMode.combine===void 0&&(o.overlayMode.combine=!0),this.options=o,this.render(),o.initialValue&&(!this.options.autosave||this.options.autosave.foundSavedValue!==!0)&&this.value(o.initialValue),o.uploadImage){var c=this;this.codemirror.on("dragenter",function(h,b){c.updateStatusBar("upload-image",c.options.imageTexts.sbOnDragEnter),b.stopPropagation(),b.preventDefault()}),this.codemirror.on("dragend",function(h,b){c.updateStatusBar("upload-image",c.options.imageTexts.sbInit),b.stopPropagation(),b.preventDefault()}),this.codemirror.on("dragleave",function(h,b){c.updateStatusBar("upload-image",c.options.imageTexts.sbInit),b.stopPropagation(),b.preventDefault()}),this.codemirror.on("dragover",function(h,b){c.updateStatusBar("upload-image",c.options.imageTexts.sbOnDragEnter),b.stopPropagation(),b.preventDefault()}),this.codemirror.on("drop",function(h,b){b.stopPropagation(),b.preventDefault(),o.imageUploadFunction?c.uploadImagesUsingCustomFunction(o.imageUploadFunction,b.dataTransfer.files):c.uploadImages(b.dataTransfer.files)}),this.codemirror.on("paste",function(h,b){o.imageUploadFunction?c.uploadImagesUsingCustomFunction(o.imageUploadFunction,b.clipboardData.files):c.uploadImages(b.clipboardData.files)})}}te.prototype.uploadImages=function(o,l,s){if(o.length!==0){for(var a=[],f=0;f=2){var P=N[1];if(l.imagesPreviewHandler){var I=l.imagesPreviewHandler(N[1]);typeof I=="string"&&(P=I)}if(window.EMDEimagesCache[P])F(O,window.EMDEimagesCache[P]);else{window.EMDEimagesCache[P]={};var q=document.createElement("img");q.onload=function(){window.EMDEimagesCache[P]={naturalWidth:q.naturalWidth,naturalHeight:q.naturalHeight,url:P},F(O,window.EMDEimagesCache[P])},q.src=P}}}})}this.codemirror.on("update",function(){_()}),this.gui.sideBySide=this.createSideBySide(),this._rendered=this.element,(l.autofocus===!0||o.autofocus)&&this.codemirror.focus();var L=this.codemirror;setTimeout(function(){L.refresh()}.bind(L),0)};te.prototype.cleanup=function(){document.removeEventListener("keydown",this.documentOnKeyDown)};function Vf(){if(typeof localStorage=="object")try{localStorage.setItem("smde_localStorage",1),localStorage.removeItem("smde_localStorage")}catch{return!1}else return!1;return!0}te.prototype.autosave=function(){if(Vf()){var o=this;if(this.options.autosave.uniqueId==null||this.options.autosave.uniqueId==""){console.log("EasyMDE: You must set a uniqueId to use the autosave feature");return}this.options.autosave.binded!==!0&&(o.element.form!=null&&o.element.form!=null&&o.element.form.addEventListener("submit",function(){clearTimeout(o.autosaveTimeoutId),o.autosaveTimeoutId=void 0,localStorage.removeItem("smde_"+o.options.autosave.uniqueId)}),this.options.autosave.binded=!0),this.options.autosave.loaded!==!0&&(typeof localStorage.getItem("smde_"+this.options.autosave.uniqueId)=="string"&&localStorage.getItem("smde_"+this.options.autosave.uniqueId)!=""&&(this.codemirror.setValue(localStorage.getItem("smde_"+this.options.autosave.uniqueId)),this.options.autosave.foundSavedValue=!0),this.options.autosave.loaded=!0);var l=o.value();l!==""?localStorage.setItem("smde_"+this.options.autosave.uniqueId,l):localStorage.removeItem("smde_"+this.options.autosave.uniqueId);var s=document.getElementById("autosaved");if(s!=null&&s!=null&&s!=""){var a=new Date,f=new Intl.DateTimeFormat([this.options.autosave.timeFormat.locale,"en-US"],this.options.autosave.timeFormat.format).format(a),p=this.options.autosave.text==null?"Autosaved: ":this.options.autosave.text;s.innerHTML=p+f}}else console.log("EasyMDE: localStorage not available, cannot autosave")};te.prototype.clearAutosavedValue=function(){if(Vf()){if(this.options.autosave==null||this.options.autosave.uniqueId==null||this.options.autosave.uniqueId==""){console.log("EasyMDE: You must set a uniqueId to clear the autosave value");return}localStorage.removeItem("smde_"+this.options.autosave.uniqueId)}else console.log("EasyMDE: localStorage not available, cannot autosave")};te.prototype.openBrowseFileWindow=function(o,l){var s=this,a=this.gui.toolbar.getElementsByClassName("imageInput")[0];a.click();function f(p){s.options.imageUploadFunction?s.uploadImagesUsingCustomFunction(s.options.imageUploadFunction,p.target.files):s.uploadImages(p.target.files,o,l),a.removeEventListener("change",f)}a.addEventListener("change",f)};te.prototype.uploadImage=function(o,l,s){var a=this;l=l||function(y){Qf(a,y)};function f(b){a.updateStatusBar("upload-image",b),setTimeout(function(){a.updateStatusBar("upload-image",a.options.imageTexts.sbInit)},1e4),s&&typeof s=="function"&&s(b),a.options.errorCallback(b)}function p(b){var y=a.options.imageTexts.sizeUnits.split(",");return b.replace("#image_name#",o.name).replace("#image_size#",uo(o.size,y)).replace("#image_max_size#",uo(a.options.imageMaxSize,y))}if(o.size>this.options.imageMaxSize){f(p(this.options.errorMessages.fileTooLarge));return}var c=new FormData;c.append("image",o),a.options.imageCSRFToken&&!a.options.imageCSRFHeader&&c.append(a.options.imageCSRFName,a.options.imageCSRFToken);var h=new XMLHttpRequest;h.upload.onprogress=function(b){if(b.lengthComputable){var y=""+Math.round(b.loaded*100/b.total);a.updateStatusBar("upload-image",a.options.imageTexts.sbProgress.replace("#file_name#",o.name).replace("#progress#",y))}},h.open("POST",this.options.imageUploadEndpoint),a.options.imageCSRFToken&&a.options.imageCSRFHeader&&h.setRequestHeader(a.options.imageCSRFName,a.options.imageCSRFToken),h.onload=function(){try{var b=JSON.parse(this.responseText)}catch{console.error("EasyMDE: The server did not return a valid json."),f(p(a.options.errorMessages.importError));return}this.status===200&&b&&!b.error&&b.data&&b.data.filePath?l((a.options.imagePathAbsolute?"":window.location.origin+"/")+b.data.filePath):b.error&&b.error in a.options.errorMessages?f(p(a.options.errorMessages[b.error])):b.error?f(p(b.error)):(console.error("EasyMDE: Received an unexpected response after uploading the image."+this.status+" ("+this.statusText+")"),f(p(a.options.errorMessages.importError)))},h.onerror=function(b){console.error("EasyMDE: An unexpected error occurred when trying to upload the image."+b.target.status+" ("+b.target.statusText+")"),f(a.options.errorMessages.importError)},h.send(c)};te.prototype.uploadImageUsingCustomFunction=function(o,l){var s=this;function a(c){Qf(s,c)}function f(c){var h=p(c);s.updateStatusBar("upload-image",h),setTimeout(function(){s.updateStatusBar("upload-image",s.options.imageTexts.sbInit)},1e4),s.options.errorCallback(h)}function p(c){var h=s.options.imageTexts.sizeUnits.split(",");return c.replace("#image_name#",l.name).replace("#image_size#",uo(l.size,h)).replace("#image_max_size#",uo(s.options.imageMaxSize,h))}o.apply(this,[l,a,f])};te.prototype.setPreviewMaxHeight=function(){var o=this.codemirror,l=o.getWrapperElement(),s=l.nextSibling,a=parseInt(window.getComputedStyle(l).paddingTop),f=parseInt(window.getComputedStyle(l).borderTopWidth),p=parseInt(this.options.maxHeight),c=p+a*2+f*2,h=c.toString()+"px";s.style.height=h};te.prototype.createSideBySide=function(){var o=this.codemirror,l=o.getWrapperElement(),s=l.nextSibling;if(!s||!s.classList.contains("editor-preview-side")){if(s=document.createElement("div"),s.className="editor-preview-side",this.options.previewClass)if(Array.isArray(this.options.previewClass))for(var a=0;a"&&(l=l.substring(1)),o)try{if(o.matches)return o.matches(l);if(o.msMatchesSelector)return o.msMatchesSelector(l);if(o.webkitMatchesSelector)return o.webkitMatchesSelector(l)}catch{return!1}return!1}}function hc(o){return o.host&&o!==document&&o.host.nodeType&&o.host!==o?o.host:o.parentNode}function Gt(o,l,s,a){if(o){s=s||document;do{if(l!=null&&(l[0]===">"?o.parentNode===s&&Ro(o,l):Ro(o,l))||a&&o===s)return o;if(o===s)break}while(o=hc(o))}return null}var ic=/\s+/g;function Bt(o,l,s){if(o&&l)if(o.classList)o.classList[s?"add":"remove"](l);else{var a=(" "+o.className+" ").replace(ic," ").replace(" "+l+" "," ");o.className=(a+(s?" "+l:"")).replace(ic," ")}}function he(o,l,s){var a=o&&o.style;if(a){if(s===void 0)return document.defaultView&&document.defaultView.getComputedStyle?s=document.defaultView.getComputedStyle(o,""):o.currentStyle&&(s=o.currentStyle),l===void 0?s:s[l];!(l in a)&&l.indexOf("webkit")===-1&&(l="-webkit-"+l),a[l]=s+(typeof s=="string"?"":"px")}}function An(o,l){var s="";if(typeof o=="string")s=o;else do{var a=he(o,"transform");a&&a!=="none"&&(s=a+" "+s)}while(!l&&(o=o.parentNode));var f=window.DOMMatrix||window.WebKitCSSMatrix||window.CSSMatrix||window.MSCSSMatrix;return f&&new f(s)}function pc(o,l,s){if(o){var a=o.getElementsByTagName(l),f=0,p=a.length;if(s)for(;f
=p:c=f<=p,!c)return a;if(a===Vt())break;a=Tr(a,!1)}return!1}function Fn(o,l,s,a){for(var f=0,p=0,c=o.children;p2&&arguments[2]!==void 0?arguments[2]:{},f=a.evt,p=zp(a,Qp);xi.pluginEvent.bind(pe)(l,s,er({dragEl:$,parentEl:qe,ghostEl:be,rootEl:Pe,nextEl:Yr,lastDownEl:Io,cloneEl:Re,cloneHidden:Fr,dragStarted:ci,putSortable:ut,activeSortable:pe.active,originalEvent:f,oldIndex:En,oldDraggableIndex:vi,newIndex:Nt,newDraggableIndex:Ar,hideGhostForTarget:wc,unhideGhostForTarget:Cc,cloneNowHidden:function(){Fr=!0},cloneNowShown:function(){Fr=!1},dispatchSortableEvent:function(h){Ct({sortable:s,name:h,originalEvent:f})}},p))};function Ct(o){Zp(er({putSortable:ut,cloneEl:Re,targetEl:$,rootEl:Pe,oldIndex:En,oldDraggableIndex:vi,newIndex:Nt,newDraggableIndex:Ar},o))}var $,qe,be,Pe,Yr,Io,Re,Fr,En,Nt,vi,Ar,_o,ut,Sn=!1,Wo=!1,qo=[],Xr,jt,Dl,wl,lc,sc,ci,kn,mi,bi=!1,Bo=!1,Po,pt,Cl=[],Tl=!1,Uo=[],Go=typeof document<"u",No=Bl,uc=yi||cr?"cssFloat":"float",Jp=Go&&!cc&&!Bl&&"draggable"in document.createElement("div"),yc=function(){if(Go){if(cr)return!1;var o=document.createElement("x");return o.style.cssText="pointer-events:auto",o.style.pointerEvents==="auto"}}(),xc=function(l,s){var a=he(l),f=parseInt(a.width)-parseInt(a.paddingLeft)-parseInt(a.paddingRight)-parseInt(a.borderLeftWidth)-parseInt(a.borderRightWidth),p=Fn(l,0,s),c=Fn(l,1,s),h=p&&he(p),b=c&&he(c),y=h&&parseInt(h.marginLeft)+parseInt(h.marginRight)+$e(p).width,x=b&&parseInt(b.marginLeft)+parseInt(b.marginRight)+$e(c).width;if(a.display==="flex")return a.flexDirection==="column"||a.flexDirection==="column-reverse"?"vertical":"horizontal";if(a.display==="grid")return a.gridTemplateColumns.split(" ").length<=1?"vertical":"horizontal";if(p&&h.float&&h.float!=="none"){var C=h.float==="left"?"left":"right";return c&&(b.clear==="both"||b.clear===C)?"vertical":"horizontal"}return p&&(h.display==="block"||h.display==="flex"||h.display==="table"||h.display==="grid"||y>=f&&a[uc]==="none"||c&&a[uc]==="none"&&y+x>f)?"vertical":"horizontal"},$p=function(l,s,a){var f=a?l.left:l.top,p=a?l.right:l.bottom,c=a?l.width:l.height,h=a?s.left:s.top,b=a?s.right:s.bottom,y=a?s.width:s.height;return f===h||p===b||f+c/2===h+y/2},Vp=function(l,s){var a;return qo.some(function(f){var p=f[Ft].options.emptyInsertThreshold;if(!(!p||Nl(f))){var c=$e(f),h=l>=c.left-p&&l<=c.right+p,b=s>=c.top-p&&s<=c.bottom+p;if(h&&b)return a=f}}),a},Dc=function(l){function s(p,c){return function(h,b,y,x){var C=h.options.group.name&&b.options.group.name&&h.options.group.name===b.options.group.name;if(p==null&&(c||C))return!0;if(p==null||p===!1)return!1;if(c&&p==="clone")return p;if(typeof p=="function")return s(p(h,b,y,x),c)(h,b,y,x);var S=(c?h:b).options.group.name;return p===!0||typeof p=="string"&&p===S||p.join&&p.indexOf(S)>-1}}var a={},f=l.group;(!f||Fl(f)!="object")&&(f={name:f}),a.name=f.name,a.checkPull=s(f.pull,!0),a.checkPut=s(f.put),a.revertClone=f.revertClone,l.group=a},wc=function(){!yc&&be&&he(be,"display","none")},Cc=function(){!yc&&be&&he(be,"display","")};Go&&!cc&&document.addEventListener("click",function(o){if(Wo)return o.preventDefault(),o.stopPropagation&&o.stopPropagation(),o.stopImmediatePropagation&&o.stopImmediatePropagation(),Wo=!1,!1},!0);var Kr=function(l){if($){l=l.touches?l.touches[0]:l;var s=Vp(l.clientX,l.clientY);if(s){var a={};for(var f in l)l.hasOwnProperty(f)&&(a[f]=l[f]);a.target=a.rootEl=s,a.preventDefault=void 0,a.stopPropagation=void 0,s[Ft]._onDragOver(a)}}},eg=function(l){$&&$.parentNode[Ft]._isOutsideThisEl(l.target)};function pe(o,l){if(!(o&&o.nodeType&&o.nodeType===1))throw"Sortable: `el` must be an HTMLElement, not ".concat({}.toString.call(o));this.el=o,this.options=l=fr({},l),o[Ft]=this;var s={group:null,sort:!0,disabled:!1,store:null,handle:null,draggable:/^[uo]l$/i.test(o.nodeName)?">li":">*",swapThreshold:1,invertSwap:!1,invertedSwapThreshold:null,removeCloneOnHide:!0,direction:function(){return xc(o,this.options)},ghostClass:"sortable-ghost",chosenClass:"sortable-chosen",dragClass:"sortable-drag",ignore:"a, img",filter:null,preventOnFilter:!0,animation:0,easing:null,setData:function(c,h){c.setData("Text",h.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:pe.supportPointer!==!1&&"PointerEvent"in window&&(!pi||Bl),emptyInsertThreshold:5};xi.initializePlugins(this,o,s);for(var a in s)!(a in l)&&(l[a]=s[a]);Dc(l);for(var f in this)f.charAt(0)==="_"&&typeof this[f]=="function"&&(this[f]=this[f].bind(this));this.nativeDraggable=l.forceFallback?!1:Jp,this.nativeDraggable&&(this.options.touchStartThreshold=1),l.supportPointer?ke(o,"pointerdown",this._onTapStart):(ke(o,"mousedown",this._onTapStart),ke(o,"touchstart",this._onTapStart)),this.nativeDraggable&&(ke(o,"dragover",this),ke(o,"dragenter",this)),qo.push(this.el),l.store&&l.store.get&&this.sort(l.store.get(this)||[]),fr(this,Xp())}pe.prototype={constructor:pe,_isOutsideThisEl:function(l){!this.el.contains(l)&&l!==this.el&&(kn=null)},_getDirection:function(l,s){return typeof this.options.direction=="function"?this.options.direction.call(this,l,s,$):this.options.direction},_onTapStart:function(l){if(l.cancelable){var s=this,a=this.el,f=this.options,p=f.preventOnFilter,c=l.type,h=l.touches&&l.touches[0]||l.pointerType&&l.pointerType==="touch"&&l,b=(h||l).target,y=l.target.shadowRoot&&(l.path&&l.path[0]||l.composedPath&&l.composedPath()[0])||b,x=f.filter;if(sg(a),!$&&!(/mousedown|pointerdown/.test(c)&&l.button!==0||f.disabled)&&!y.isContentEditable&&!(!this.nativeDraggable&&pi&&b&&b.tagName.toUpperCase()==="SELECT")&&(b=Gt(b,f.draggable,a,!1),!(b&&b.animated)&&Io!==b)){if(En=Pt(b),vi=Pt(b,f.draggable),typeof x=="function"){if(x.call(this,l,b,this)){Ct({sortable:s,rootEl:y,name:"filter",targetEl:b,toEl:a,fromEl:a}),At("filter",s,{evt:l}),p&&l.preventDefault();return}}else if(x&&(x=x.split(",").some(function(C){if(C=Gt(y,C.trim(),a,!1),C)return Ct({sortable:s,rootEl:C,name:"filter",targetEl:b,fromEl:a,toEl:a}),At("filter",s,{evt:l}),!0}),x)){p&&l.preventDefault();return}f.handle&&!Gt(y,f.handle,a,!1)||this._prepareDragStart(l,h,b)}}},_prepareDragStart:function(l,s,a){var f=this,p=f.el,c=f.options,h=p.ownerDocument,b;if(a&&!$&&a.parentNode===p){var y=$e(a);if(Pe=p,$=a,qe=$.parentNode,Yr=$.nextSibling,Io=a,_o=c.group,pe.dragged=$,Xr={target:$,clientX:(s||l).clientX,clientY:(s||l).clientY},lc=Xr.clientX-y.left,sc=Xr.clientY-y.top,this._lastX=(s||l).clientX,this._lastY=(s||l).clientY,$.style["will-change"]="all",b=function(){if(At("delayEnded",f,{evt:l}),pe.eventCanceled){f._onDrop();return}f._disableDelayedDragEvents(),!nc&&f.nativeDraggable&&($.draggable=!0),f._triggerDragStart(l,s),Ct({sortable:f,name:"choose",originalEvent:l}),Bt($,c.chosenClass,!0)},c.ignore.split(",").forEach(function(x){pc($,x.trim(),kl)}),ke(h,"dragover",Kr),ke(h,"mousemove",Kr),ke(h,"touchmove",Kr),c.supportPointer?(ke(h,"pointerup",f._onDrop),!this.nativeDraggable&&ke(h,"pointercancel",f._onDrop)):(ke(h,"mouseup",f._onDrop),ke(h,"touchend",f._onDrop),ke(h,"touchcancel",f._onDrop)),nc&&this.nativeDraggable&&(this.options.touchStartThreshold=4,$.draggable=!0),At("delayStart",this,{evt:l}),c.delay&&(!c.delayOnTouchOnly||s)&&(!this.nativeDraggable||!(yi||cr))){if(pe.eventCanceled){this._onDrop();return}c.supportPointer?(ke(h,"pointerup",f._disableDelayedDrag),ke(h,"pointercancel",f._disableDelayedDrag)):(ke(h,"mouseup",f._disableDelayedDrag),ke(h,"touchend",f._disableDelayedDrag),ke(h,"touchcancel",f._disableDelayedDrag)),ke(h,"mousemove",f._delayedDragTouchMoveHandler),ke(h,"touchmove",f._delayedDragTouchMoveHandler),c.supportPointer&&ke(h,"pointermove",f._delayedDragTouchMoveHandler),f._dragStartTimer=setTimeout(b,c.delay)}else b()}},_delayedDragTouchMoveHandler:function(l){var s=l.touches?l.touches[0]:l;Math.max(Math.abs(s.clientX-this._lastX),Math.abs(s.clientY-this._lastY))>=Math.floor(this.options.touchStartThreshold/(this.nativeDraggable&&window.devicePixelRatio||1))&&this._disableDelayedDrag()},_disableDelayedDrag:function(){$&&kl($),clearTimeout(this._dragStartTimer),this._disableDelayedDragEvents()},_disableDelayedDragEvents:function(){var l=this.el.ownerDocument;Ce(l,"mouseup",this._disableDelayedDrag),Ce(l,"touchend",this._disableDelayedDrag),Ce(l,"touchcancel",this._disableDelayedDrag),Ce(l,"pointerup",this._disableDelayedDrag),Ce(l,"pointercancel",this._disableDelayedDrag),Ce(l,"mousemove",this._delayedDragTouchMoveHandler),Ce(l,"touchmove",this._delayedDragTouchMoveHandler),Ce(l,"pointermove",this._delayedDragTouchMoveHandler)},_triggerDragStart:function(l,s){s=s||l.pointerType=="touch"&&l,!this.nativeDraggable||s?this.options.supportPointer?ke(document,"pointermove",this._onTouchMove):s?ke(document,"touchmove",this._onTouchMove):ke(document,"mousemove",this._onTouchMove):(ke($,"dragend",this),ke(Pe,"dragstart",this._onDragStart));try{document.selection?zo(function(){document.selection.empty()}):window.getSelection().removeAllRanges()}catch{}},_dragStarted:function(l,s){if(Sn=!1,Pe&&$){At("dragStarted",this,{evt:s}),this.nativeDraggable&&ke(document,"dragover",eg);var a=this.options;!l&&Bt($,a.dragClass,!1),Bt($,a.ghostClass,!0),pe.active=this,l&&this._appendGhost(),Ct({sortable:this,name:"start",originalEvent:s})}else this._nulling()},_emulateDragOver:function(){if(jt){this._lastX=jt.clientX,this._lastY=jt.clientY,wc();for(var l=document.elementFromPoint(jt.clientX,jt.clientY),s=l;l&&l.shadowRoot&&(l=l.shadowRoot.elementFromPoint(jt.clientX,jt.clientY),l!==s);)s=l;if($.parentNode[Ft]._isOutsideThisEl(l),s)do{if(s[Ft]){var a=void 0;if(a=s[Ft]._onDragOver({clientX:jt.clientX,clientY:jt.clientY,target:l,rootEl:s}),a&&!this.options.dragoverBubble)break}l=s}while(s=hc(s));Cc()}},_onTouchMove:function(l){if(Xr){var s=this.options,a=s.fallbackTolerance,f=s.fallbackOffset,p=l.touches?l.touches[0]:l,c=be&&An(be,!0),h=be&&c&&c.a,b=be&&c&&c.d,y=No&&pt&&ac(pt),x=(p.clientX-Xr.clientX+f.x)/(h||1)+(y?y[0]-Cl[0]:0)/(h||1),C=(p.clientY-Xr.clientY+f.y)/(b||1)+(y?y[1]-Cl[1]:0)/(b||1);if(!pe.active&&!Sn){if(a&&Math.max(Math.abs(p.clientX-this._lastX),Math.abs(p.clientY-this._lastY))=0&&(Ct({rootEl:qe,name:"add",toEl:qe,fromEl:Pe,originalEvent:l}),Ct({sortable:this,name:"remove",toEl:qe,originalEvent:l}),Ct({rootEl:qe,name:"sort",toEl:qe,fromEl:Pe,originalEvent:l}),Ct({sortable:this,name:"sort",toEl:qe,originalEvent:l})),ut&&ut.save()):Nt!==En&&Nt>=0&&(Ct({sortable:this,name:"update",toEl:qe,originalEvent:l}),Ct({sortable:this,name:"sort",toEl:qe,originalEvent:l})),pe.active&&((Nt==null||Nt===-1)&&(Nt=En,Ar=vi),Ct({sortable:this,name:"end",toEl:qe,originalEvent:l}),this.save()))),this._nulling()},_nulling:function(){At("nulling",this),Pe=$=qe=be=Yr=Re=Io=Fr=Xr=jt=ci=Nt=Ar=En=vi=kn=mi=ut=_o=pe.dragged=pe.ghost=pe.clone=pe.active=null;var l=this.el;Uo.forEach(function(s){l.contains(s)&&(s.checked=!0)}),Uo.length=Dl=wl=0},handleEvent:function(l){switch(l.type){case"drop":case"dragend":this._onDrop(l);break;case"dragenter":case"dragover":$&&(this._onDragOver(l),tg(l));break;case"selectstart":l.preventDefault();break}},toArray:function(){for(var l=[],s,a=this.el.children,f=0,p=a.length,c=this.options;f