CVE-2023-33196: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') (CWE-79)
value: Craft is a CMS for creating custom digital experiences. Cross site scripting (XSS) can be triggered by review volumes. This issue has been fixed in version 4.4.7.


CWE description: The product does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users.
CVSS v3 base score: 5.4
Published: 2023-05-26T21:15Z
Repository: https://github.com/craftcms/cms
Language: TypeScript
Files changed: CHANGELOG.md, packages/craftcms-webpack/Craft.d.ts, src/web/assets/assetindexes/dist/AssetIndexer.js, src/web/assets/assetindexes/dist/AssetIndexer.js.map, src/web/assets/assetindexes/src/AssetIndexer.ts
Commit date: 2023-04-05 14:49:10 -0700
Commit message: Fixed XSS vulnerabilities

Diff: diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2d2707d1819..af0df3fdce9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,10 +3,11 @@
 ## Unreleased
 
 - Fixed a bug where users were “View other users’ drafts” section permissions weren’t being enforced for unpublished drafts.
 - Fixed a bug where element editors were showing provisional changes, even if the user didn’t have permission to save them.
 - `craft\web\View::renderObjectTemplate()` now trims the returned template output.
+- Fixed XSS vulnerabilities.
 
 ## 4.4.6.1 - 2023-04-04
 
 - Fixed a bug where Categories fields weren’t retaining custom sort orders when “Maintain hierarchy” was disabled. ([#10560](https://github.com/craftcms/cms/discussions/10560))
 
diff --git a/packages/craftcms-webpack/Craft.d.ts b/packages/craftcms-webpack/Craft.d.ts
index aac141665ad..c591680d785 100644
--- a/packages/craftcms-webpack/Craft.d.ts
+++ b/packages/craftcms-webpack/Craft.d.ts
@@ -18,10 +18,11 @@ declare var Craft: {
   ProgressBar: ProgressBarInterface;
   t(category: string, message: string, params?: object): string;
   sendActionRequest(method: string, action: string, options?: object): Promise;
   initUiElements($container: JQuery): void;
   expandPostArray(arr: object): any;
+  escapeHtml(str: string);
   Preview: any;
   cp: any;
 };
 
 declare var Garnish: any;
diff --git a/src/web/assets/assetindexes/dist/AssetIndexer.js b/src/web/assets/assetindexes/dist/AssetIndexer.js
index 028dddb22c0..8db0453ef13 100644
--- a/src/web/assets/assetindexes/dist/AssetIndexer.js
+++ b/src/web/assets/assetindexes/dist/AssetIndexer.js
@@ -1,2 +1,2 @@
-!function(){var s={d:function(e,n){for(var t in n)s.o(n,t)&&!s.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:n[t]})},o:function(s,e){return Object.prototype.hasOwnProperty.call(s,e)},r:function(s){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(s,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(s,"__esModule",{value:!0})}},e={};!function(){"use strict";var n,t;s.r(e),s.d(e,{AssetIndexer:function(){return i}}),function(s){s[s.ACTIONREQUIRED=0]="ACTIONREQUIRED",s[s.ACTIVE=1]="ACTIVE",s[s.WAITING=2]="WAITING"}(n||(n={})),function(s){s.START="asset-indexes/start-indexing",s.STOP="asset-indexes/stop-indexing-session",s.PROCESS="asset-indexes/process-indexing-session",s.OVERVIEW="asset-indexes/indexing-session-overview",s.FINISH="asset-indexes/finish-indexing-session"}(t||(t={}));class i{constructor(s,e,t=3){this._currentIndexingSession=null,this._currentConnectionCount=0,this._tasksWaiting=[],this._priorityTasks=[],this._prunedSessionIds=[],this._currentlyReviewing=!1,this.indexingSessions={},this._maxConcurrentConnections=t,this.$indexingSessionTable=s,this.indexingSessions={};let i=0;for(const s of e){let e=this.createSessionFromModel(s);e.getSessionStatus()!==n.ACTIONREQUIRED||i||(i=e.getSessionId()),i||null!=this._currentIndexingSession||e.getSessionStatus()===n.ACTIONREQUIRED||(this._currentIndexingSession=e.getSessionId()),this.updateIndexingSessionData(e)}this._currentIndexingSession&&this.performIndexingStep()}get currentIndexingSession(){return this._currentIndexingSession}updateIndexingSessionData(s){this.indexingSessions[s.getSessionId()]=s,this.renderIndexingSessionRow(s)}renderIndexingSessionRow(s){let e;if(void 0===s)return;if(!this.indexingSessions[s.getSessionId()]||this._prunedSessionIds.includes(s.getSessionId()))return this.$indexingSessionTable.find('tr[data-session-id="'+s.getSessionId()+'"]').remove(),void(0==this.$indexingSessionTable.find("tbody tr").length&&this.$indexingSessionTable.addClass("hidden"));e=s.getIndexingSessionRowHtml();const n=this.$indexingSessionTable.find('tr[data-session-id="'+s.getSessionId()+'"]');n.length>0?n.replaceWith(e):this.$indexingSessionTable.find("tbody").append(e),this.$indexingSessionTable.removeClass("hidden")}discardIndexingSession(s){const e=this.indexingSessions[s];delete this.indexingSessions[s],this._currentIndexingSession===s&&(this._currentIndexingSession=null),this.renderIndexingSessionRow(e),this.runTasks()}processFailureResponse(s){const e=s.data;this._currentConnectionCount--,this._updateCurrentIndexingSession(),Craft.cp.displayError(e.message),e.stop&&this.discardIndexingSession(e.stop),this.runTasks()}processSuccessResponse(s){const e=s.data;if(this._currentConnectionCount--,e.session){const s=this.createSessionFromModel(e.session);this.indexingSessions[s.getSessionId()]=s,this.renderIndexingSessionRow(s),this._updateCurrentIndexingSession(),s.getSessionStatus()!==n.ACTIONREQUIRED||e.skipDialog?this._prunedSessionIds.includes(this._currentIndexingSession)?this.runTasks():this.performIndexingStep():this._prunedSessionIds.includes(this._currentIndexingSession)?this.runTasks():this.reviewSession(s)}this._updateCurrentIndexingSession(),e.stop&&this.discardIndexingSession(e.stop)}getReviewData(s){const e={sessionId:s.getSessionId(),action:t.OVERVIEW,params:{sessionId:s.getSessionId()},callback:()=>{this.renderIndexingSessionRow(s)}};this.enqueueTask(e)}reviewSession(s){if(this._currentlyReviewing)return;this._currentlyReviewing=!0,this.pruneWaitingTasks(s.getSessionId());let e=$("<div></div>");const n=s.getMissingEntries(),i=n.files?Object.entries(n.files):[],o=n.folders?Object.entries(n.folders):[],r=s.getSkippedEntries();if(r.length){let s="";for(const e of r)s+=`<li>${e}</li>`;e.append(`\n                <h2>${Craft.t("app","Skipped files")}</h2>\n                <p>${Craft.t("app","The following items were not indexed.")}</p>\n                <ul>\n                    ${s}\n                </ul>\n            `)}const a=i.length||o.length;if(a){if(o.length){let n="";for(const[s,e]of o)n+=`<li><label><input type="checkbox" checked="checked" name="deleteFolder[]" value="${s}"> ${e}</label></li>`;const t={items:"folders"};let i=this._getMissingItemsHeading("folders",t,s),r=this._getMissingItemsCopy("folders",t,s);e.append($(`\n                <h2>${i}</h2>\n                <p>${r}</p>\n                <ul>\n                    ${n}\n                </ul>\n            `))}if(i.length){let n="";for(const[s,e]of i)n+=`<li><label><input type="checkbox" checked="checked" name="deleteAsset[]" value="${s}"> ${e}</label></li>`;const t={items:"files"};let o=this._getMissingItemsHeading("files",t,s),r=this._getMissingItemsCopy("files",t,s);e.append($(`\n                <h2>${o}</h2>\n                <p>${r}</p>\n                <ul>\n                    ${n}\n                </ul>\n            `))}}const d=$('<form class="modal fitted confirmmodal"/>').appendTo(Garnish.$bod),g=$('<div class="body"/>').appendTo(d).html(e.html()),p=$('<footer class="footer"/>').appendTo(d),c=$('<div class="buttons right"/>').appendTo(p),h=new Garnish.Modal(d,{hideOnEsc:!1,hideOnShadeClick:!1,onHide:()=>{this._currentlyReviewing=!1}});a?($("<button/>",{type:"button",class:"btn",text:Craft.t("app","Keep them")}).on("click",(e=>{e.preventDefault(),this.stopIndexingSession(s),h.hide()})).appendTo(c),$("<button/>",{type:"submit",class:"btn submit",text:Craft.t("app","Delete them")}).appendTo(c)):$("<button/>",{type:"submit",class:"btn submit",text:Craft.t("app","OK")}).appendTo(c),Craft.initUiElements(g),h.updateSizeAndPosition(),d.on("submit",(e=>{e.preventDefault(),h.hide();const n=Garnish.getPostData(g),i=Craft.expandPostArray(n);i.sessionId=s.getSessionId();const o={sessionId:s.getSessionId(),action:t.FINISH,params:i};this.enqueueTask(o,!0)}))}_getMissingItemsHeading(s,e,n){let t=Craft.t("app","Missing {items}",e);return"folders"==s&&n.getListEmptyFolders()&&(t=Craft.t("app","Missing or empty {items}",e)),t}_getMissingItemsCopy(s,e,n){let t=Craft.t("app","The following {items} could not be found. Should they be deleted from the index?",e);return"folders"==s&&n.getListEmptyFolders()&&(t=Craft.t("app","The following {items} could not be found or are empty. Should they be deleted from the index?",e)),t}startIndexing(s,e){Craft.sendActionRequest("POST",t.START,{data:s}).then((s=>this.processSuccessResponse(s))).catch((({response:s})=>this.processFailureResponse(s))).finally((()=>e()))}performIndexingStep(){if(this._currentIndexingSession||this._updateCurrentIndexingSession(),!this._currentIndexingSession)return;const s=this.indexingSessions[this._currentIndexingSession],e=this._maxConcurrentConnections-this._currentConnectionCount;for(let n=0;n<Math.min(e,s.getEntriesRemaining());n++){const e={sessionId:s.getSessionId(),action:t.PROCESS,params:{sessionId:this._currentIndexingSession}};this.enqueueTask(e)}if(s.getProcessIfRootEmpty()){const e={sessionId:s.getSessionId(),action:t.PROCESS,params:{sessionId:this._currentIndexingSession}};this.enqueueTask(e)}}stopIndexingSession(s){this.pruneWaitingTasks(s.getSessionId());const e={sessionId:s.getSessionId(),action:t.STOP,params:{sessionId:s.getSessionId()}};this.enqueueTask(e,!0)}pruneWaitingTasks(s){const e=[];let n=!1;this._prunedSessionIds.push(s);for(const t of this._tasksWaiting)t.sessionId!==s?e.push(t):n=!0;n&&(this._tasksWaiting=e)}enqueueTask(s,e=!1){e?this._priorityTasks.push(s):this._tasksWaiting.push(s),this.runTasks()}runTasks(){if(!(this._tasksWaiting.length+this._priorityTasks.length===0||this._currentConnectionCount>=this._maxConcurrentConnections))for(;this._tasksWaiting.length+this._priorityTasks.length!==0&&this._currentConnectionCount<this._maxConcurrentConnections;){this._currentConnectionCount++;const s=this._priorityTasks.length>0?this._priorityTasks.shift():this._tasksWaiting.shift();Craft.sendActionRequest("POST",s.action,{data:s.params}).then((s=>this.processSuccessResponse(s))).catch((({response:s})=>this.processFailureResponse(s))).finally((()=>{s.callback&&s.callback()}))}}_updateCurrentIndexingSession(){for(const s of Object.values(this.indexingSessions))if(s.getSessionStatus()!==n.ACTIONREQUIRED)return void(this._currentIndexingSession=s.getSessionId())}createSessionFromModel(s){return new o(s,this)}}class o{constructor(s,e){this.indexingSessionData=s,this.indexer=e}getSessionId(){return this.indexingSessionData.id}getProcessIfRootEmpty(){return this.indexingSessionData.processIfRootEmpty}getListEmptyFolders(){return this.indexingSessionData.listEmptyFolders}getEntriesRemaining(){return this.indexingSessionData.totalEntries-this.indexingSessionData.processedEntries}getSessionStatus(){return this.indexingSessionData.actionRequired?n.ACTIONREQUIRED:this.indexer.currentIndexingSession===this.indexingSessionData.id?n.ACTIVE:n.WAITING}getIndexingSessionRowHtml(){const s=$('<tr class="indexingSession" data-session-id="'+this.getSessionId()+'">'),e=$("<td/>").appendTo(s),n=$("<ul/>").appendTo(e);for(const s of Object.values(this.indexingSessionData.indexedVolumes))$("<li/>",{text:s}).appendTo(n);s.append("<td>"+this.indexingSessionData.dateCreated+"</td>");const t=$('<td class="progress"><div class="progressContainer"></div></td>').css("position","relative"),i=new Craft.ProgressBar(t.find(".progressContainer"),!1);i.setItemCount(this.indexingSessionData.totalEntries),i.setProcessedItemCount(this.indexingSessionData.processedEntries),i.updateProgressBar(),i.showProgressBar(),t.data("progressBar",i),t.find(".progressContainer").append(`<div class="progressInfo">${this.indexingSessionData.processedEntries} / ${this.indexingSessionData.totalEntries}</div>`),s.append(t),s.append("<td>"+this.getSessionStatusMessage()+"</td>");const o=this.getActionButtons();return $("<td></td>").append(o).appendTo(s),s}getActionButtons(){const s=$('<div class="buttons"></div>');if(this.getSessionStatus()==n.ACTIONREQUIRED){const e=Craft.t("app","Review");s.append($("<button />",{type:"button",class:"btn submit",title:e,"aria-label":e}).text(e).on("click",(s=>{const e=$(s.target).parent();e.hasClass("disabled")||(e.addClass("disabled"),this.indexer.getReviewData(this))})))}const e=Craft.t("app","Discard");return s.append($("<button />",{type:"button",class:"btn submit",title:e,"aria-label":e}).text(e).on("click",(e=>{s.hasClass("disabled")||(s.addClass("disabled"),this.indexer.stopIndexingSession(this))}))),s}getSessionStatusMessage(){switch(this.getSessionStatus()){case n.ACTIONREQUIRED:return Craft.t("app","Waiting for review");case n.ACTIVE:return Craft.t("app","Active");case n.WAITING:return Craft.t("app","Waiting")}}getMissingEntries(){return this.indexingSessionData.missingEntries}getSkippedEntries(){return this.indexingSessionData.skippedEntries}}}();var n=Craft="undefined"==typeof Craft?{}:Craft;for(var t in e)n[t]=e[t];e.__esModule&&Object.defineProperty(n,"__esModule",{value:!0})}();
+!function(){var s={d:function(e,t){for(var n in t)s.o(t,n)&&!s.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},o:function(s,e){return Object.prototype.hasOwnProperty.call(s,e)},r:function(s){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(s,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(s,"__esModule",{value:!0})}},e={};!function(){"use strict";var t,n;s.r(e),s.d(e,{AssetIndexer:function(){return i}}),function(s){s[s.ACTIONREQUIRED=0]="ACTIONREQUIRED",s[s.ACTIVE=1]="ACTIVE",s[s.WAITING=2]="WAITING"}(t||(t={})),function(s){s.START="asset-indexes/start-indexing",s.STOP="asset-indexes/stop-indexing-session",s.PROCESS="asset-indexes/process-indexing-session",s.OVERVIEW="asset-indexes/indexing-session-overview",s.FINISH="asset-indexes/finish-indexing-session"}(n||(n={}));class i{constructor(s,e,n=3){this._currentIndexingSession=null,this._currentConnectionCount=0,this._tasksWaiting=[],this._priorityTasks=[],this._prunedSessionIds=[],this._currentlyReviewing=!1,this.indexingSessions={},this._maxConcurrentConnections=n,this.$indexingSessionTable=s,this.indexingSessions={};let i=0;for(const s of e){let e=this.createSessionFromModel(s);e.getSessionStatus()!==t.ACTIONREQUIRED||i||(i=e.getSessionId()),i||null!=this._currentIndexingSession||e.getSessionStatus()===t.ACTIONREQUIRED||(this._currentIndexingSession=e.getSessionId()),this.updateIndexingSessionData(e)}this._currentIndexingSession&&this.performIndexingStep()}get currentIndexingSession(){return this._currentIndexingSession}updateIndexingSessionData(s){this.indexingSessions[s.getSessionId()]=s,this.renderIndexingSessionRow(s)}renderIndexingSessionRow(s){let e;if(void 0===s)return;if(!this.indexingSessions[s.getSessionId()]||this._prunedSessionIds.includes(s.getSessionId()))return this.$indexingSessionTable.find('tr[data-session-id="'+s.getSessionId()+'"]').remove(),void(0==this.$indexingSessionTable.find("tbody tr").length&&this.$indexingSessionTable.addClass("hidden"));e=s.getIndexingSessionRowHtml();const t=this.$indexingSessionTable.find('tr[data-session-id="'+s.getSessionId()+'"]');t.length>0?t.replaceWith(e):this.$indexingSessionTable.find("tbody").append(e),this.$indexingSessionTable.removeClass("hidden")}discardIndexingSession(s){const e=this.indexingSessions[s];delete this.indexingSessions[s],this._currentIndexingSession===s&&(this._currentIndexingSession=null),this.renderIndexingSessionRow(e),this.runTasks()}processFailureResponse(s){const e=s.data;this._currentConnectionCount--,this._updateCurrentIndexingSession(),Craft.cp.displayError(e.message),e.stop&&this.discardIndexingSession(e.stop),this.runTasks()}processSuccessResponse(s){const e=s.data;if(this._currentConnectionCount--,e.session){const s=this.createSessionFromModel(e.session);this.indexingSessions[s.getSessionId()]=s,this.renderIndexingSessionRow(s),this._updateCurrentIndexingSession(),s.getSessionStatus()!==t.ACTIONREQUIRED||e.skipDialog?this._prunedSessionIds.includes(this._currentIndexingSession)?this.runTasks():this.performIndexingStep():this._prunedSessionIds.includes(this._currentIndexingSession)?this.runTasks():this.reviewSession(s)}this._updateCurrentIndexingSession(),e.stop&&this.discardIndexingSession(e.stop)}getReviewData(s){const e={sessionId:s.getSessionId(),action:n.OVERVIEW,params:{sessionId:s.getSessionId()},callback:()=>{this.renderIndexingSessionRow(s)}};this.enqueueTask(e)}reviewSession(s){if(this._currentlyReviewing)return;this._currentlyReviewing=!0,this.pruneWaitingTasks(s.getSessionId());let e=$("<div></div>");const t=s.getMissingEntries(),i=t.files?Object.entries(t.files):[],o=t.folders?Object.entries(t.folders):[],r=s.getSkippedEntries();if(r.length){let s="";for(const e of r)s+=`<li>${Craft.escapeHtml(e)}</li>`;e.append(`\n                <h2>${Craft.t("app","Skipped files")}</h2>\n                <p>${Craft.t("app","The following items were not indexed.")}</p>\n                <ul>\n                    ${s}\n                </ul>\n            `)}const a=i.length||o.length;if(a){if(o.length){let t="";for(const[s,e]of o)t+=`<li><label><input type="checkbox" checked="checked" name="deleteFolder[]" value="${s}"> ${Craft.escapeHtml(e)}</label></li>`;const n={items:"folders"};let i=this._getMissingItemsHeading("folders",n,s),r=this._getMissingItemsCopy("folders",n,s);e.append($(`\n                <h2>${i}</h2>\n                <p>${r}</p>\n                <ul>\n                    ${t}\n                </ul>\n            `))}if(i.length){let t="";for(const[s,e]of i)t+=`<li><label><input type="checkbox" checked="checked" name="deleteAsset[]" value="${s}"> ${Craft.escapeHtml(e)}</label></li>`;const n={items:"files"};let o=this._getMissingItemsHeading("files",n,s),r=this._getMissingItemsCopy("files",n,s);e.append($(`\n                <h2>${o}</h2>\n                <p>${r}</p>\n                <ul>\n                    ${t}\n                </ul>\n            `))}}const d=$('<form class="modal fitted confirmmodal"/>').appendTo(Garnish.$bod),g=$('<div class="body"/>').appendTo(d).html(e.html()),p=$('<footer class="footer"/>').appendTo(d),c=$('<div class="buttons right"/>').appendTo(p),h=new Garnish.Modal(d,{hideOnEsc:!1,hideOnShadeClick:!1,onHide:()=>{this._currentlyReviewing=!1}});a?($("<button/>",{type:"button",class:"btn",text:Craft.t("app","Keep them")}).on("click",(e=>{e.preventDefault(),this.stopIndexingSession(s),h.hide()})).appendTo(c),$("<button/>",{type:"submit",class:"btn submit",text:Craft.t("app","Delete them")}).appendTo(c)):$("<button/>",{type:"submit",class:"btn submit",text:Craft.t("app","OK")}).appendTo(c),Craft.initUiElements(g),h.updateSizeAndPosition(),d.on("submit",(e=>{e.preventDefault(),h.hide();const t=Garnish.getPostData(g),i=Craft.expandPostArray(t);i.sessionId=s.getSessionId();const o={sessionId:s.getSessionId(),action:n.FINISH,params:i};this.enqueueTask(o,!0)}))}_getMissingItemsHeading(s,e,t){let n=Craft.t("app","Missing {items}",e);return"folders"==s&&t.getListEmptyFolders()&&(n=Craft.t("app","Missing or empty {items}",e)),n}_getMissingItemsCopy(s,e,t){let n=Craft.t("app","The following {items} could not be found. Should they be deleted from the index?",e);return"folders"==s&&t.getListEmptyFolders()&&(n=Craft.t("app","The following {items} could not be found or are empty. Should they be deleted from the index?",e)),n}startIndexing(s,e){Craft.sendActionRequest("POST",n.START,{data:s}).then((s=>this.processSuccessResponse(s))).catch((({response:s})=>this.processFailureResponse(s))).finally((()=>e()))}performIndexingStep(){if(this._currentIndexingSession||this._updateCurrentIndexingSession(),!this._currentIndexingSession)return;const s=this.indexingSessions[this._currentIndexingSession],e=this._maxConcurrentConnections-this._currentConnectionCount;for(let t=0;t<Math.min(e,s.getEntriesRemaining());t++){const e={sessionId:s.getSessionId(),action:n.PROCESS,params:{sessionId:this._currentIndexingSession}};this.enqueueTask(e)}if(s.getProcessIfRootEmpty()){const e={sessionId:s.getSessionId(),action:n.PROCESS,params:{sessionId:this._currentIndexingSession}};this.enqueueTask(e)}}stopIndexingSession(s){this.pruneWaitingTasks(s.getSessionId());const e={sessionId:s.getSessionId(),action:n.STOP,params:{sessionId:s.getSessionId()}};this.enqueueTask(e,!0)}pruneWaitingTasks(s){const e=[];let t=!1;this._prunedSessionIds.push(s);for(const n of this._tasksWaiting)n.sessionId!==s?e.push(n):t=!0;t&&(this._tasksWaiting=e)}enqueueTask(s,e=!1){e?this._priorityTasks.push(s):this._tasksWaiting.push(s),this.runTasks()}runTasks(){if(!(this._tasksWaiting.length+this._priorityTasks.length===0||this._currentConnectionCount>=this._maxConcurrentConnections))for(;this._tasksWaiting.length+this._priorityTasks.length!==0&&this._currentConnectionCount<this._maxConcurrentConnections;){this._currentConnectionCount++;const s=this._priorityTasks.length>0?this._priorityTasks.shift():this._tasksWaiting.shift();Craft.sendActionRequest("POST",s.action,{data:s.params}).then((s=>this.processSuccessResponse(s))).catch((({response:s})=>this.processFailureResponse(s))).finally((()=>{s.callback&&s.callback()}))}}_updateCurrentIndexingSession(){for(const s of Object.values(this.indexingSessions))if(s.getSessionStatus()!==t.ACTIONREQUIRED)return void(this._currentIndexingSession=s.getSessionId())}createSessionFromModel(s){return new o(s,this)}}class o{constructor(s,e){this.indexingSessionData=s,this.indexer=e}getSessionId(){return this.indexingSessionData.id}getProcessIfRootEmpty(){return this.indexingSessionData.processIfRootEmpty}getListEmptyFolders(){return this.indexingSessionData.listEmptyFolders}getEntriesRemaining(){return this.indexingSessionData.totalEntries-this.indexingSessionData.processedEntries}getSessionStatus(){return this.indexingSessionData.actionRequired?t.ACTIONREQUIRED:this.indexer.currentIndexingSession===this.indexingSessionData.id?t.ACTIVE:t.WAITING}getIndexingSessionRowHtml(){const s=$('<tr class="indexingSession" data-session-id="'+this.getSessionId()+'">'),e=$("<td/>").appendTo(s),t=$("<ul/>").appendTo(e);for(const s of Object.values(this.indexingSessionData.indexedVolumes))$("<li/>",{text:s}).appendTo(t);s.append("<td>"+this.indexingSessionData.dateCreated+"</td>");const n=$('<td class="progress"><div class="progressContainer"></div></td>').css("position","relative"),i=new Craft.ProgressBar(n.find(".progressContainer"),!1);i.setItemCount(this.indexingSessionData.totalEntries),i.setProcessedItemCount(this.indexingSessionData.processedEntries),i.updateProgressBar(),i.showProgressBar(),n.data("progressBar",i),n.find(".progressContainer").append(`<div class="progressInfo">${this.indexingSessionData.processedEntries} / ${this.indexingSessionData.totalEntries}</div>`),s.append(n),s.append("<td>"+this.getSessionStatusMessage()+"</td>");const o=this.getActionButtons();return $("<td></td>").append(o).appendTo(s),s}getActionButtons(){const s=$('<div class="buttons"></div>');if(this.getSessionStatus()==t.ACTIONREQUIRED){const e=Craft.t("app","Review");s.append($("<button />",{type:"button",class:"btn submit",title:e,"aria-label":e}).text(e).on("click",(s=>{const e=$(s.target).parent();e.hasClass("disabled")||(e.addClass("disabled"),this.indexer.getReviewData(this))})))}const e=Craft.t("app","Discard");return s.append($("<button />",{type:"button",class:"btn submit",title:e,"aria-label":e}).text(e).on("click",(e=>{s.hasClass("disabled")||(s.addClass("disabled"),this.indexer.stopIndexingSession(this))}))),s}getSessionStatusMessage(){switch(this.getSessionStatus()){case t.ACTIONREQUIRED:return Craft.t("app","Waiting for review");case t.ACTIVE:return Craft.t("app","Active");case t.WAITING:return Craft.t("app","Waiting")}}getMissingEntries(){return this.indexingSessionData.missingEntries}getSkippedEntries(){return this.indexingSessionData.skippedEntries}}}();var t=Craft="undefined"==typeof Craft?{}:Craft;for(var n in e)t[n]=e[n];e.__esModule&&Object.defineProperty(t,"__esModule",{value:!0})}();
 //# sourceMappingURL=AssetIndexer.js.map
\ No newline at end of file
diff --git a/src/web/assets/assetindexes/dist/AssetIndexer.js.map b/src/web/assets/assetindexes/dist/AssetIndexer.js.map
index b13fc7e5ca8..af951e8179b 100644
--- a/src/web/assets/assetindexes/dist/AssetIndexer.js.map
+++ b/src/web/assets/assetindexes/dist/AssetIndexer.js.map
@@ -1 +1 @@
-{"version":3,"file":"AssetIndexer.js","mappings":"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","sources":["webpack://Craft/webpack/bootstrap","webpack://Craft/webpack/runtime/define property getters","webpack://Craft/webpack/runtime/hasOwnProperty shorthand","webpack://Craft/webpack/runtime/make namespace object","webpack://Craft/./AssetIndexer.ts"],"sourcesContent":["// The require scope\nvar __webpack_require__ = {};\n\n","// define getter functions for harmony exports\n__webpack_require__.d = function(exports, definition) {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }","// define __esModule on exports\n__webpack_require__.r = function(exports) {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","var SessionStatus;\n(function (SessionStatus) {\n    SessionStatus[SessionStatus[\"ACTIONREQUIRED\"] = 0] = \"ACTIONREQUIRED\";\n    SessionStatus[SessionStatus[\"ACTIVE\"] = 1] = \"ACTIVE\";\n    SessionStatus[SessionStatus[\"WAITING\"] = 2] = \"WAITING\";\n})(SessionStatus || (SessionStatus = {}));\nvar IndexingActions;\n(function (IndexingActions) {\n    IndexingActions[\"START\"] = \"asset-indexes/start-indexing\";\n    IndexingActions[\"STOP\"] = \"asset-indexes/stop-indexing-session\";\n    IndexingActions[\"PROCESS\"] = \"asset-indexes/process-indexing-session\";\n    IndexingActions[\"OVERVIEW\"] = \"asset-indexes/indexing-session-overview\";\n    IndexingActions[\"FINISH\"] = \"asset-indexes/finish-indexing-session\";\n})(IndexingActions || (IndexingActions = {}));\n/**\n * Actual classes start here\n */\n// Asset Indexer\n// =====================================================================================\nexport class AssetIndexer {\n    /**\n     * @param $element The indexing session table\n     * @param sessions Existing indexing sessions\n     */\n    constructor($indexingSessionTable, sessions, maxConcurrentConnections = 3) {\n        this._currentIndexingSession = null;\n        this._currentConnectionCount = 0;\n        this._tasksWaiting = [];\n        this._priorityTasks = [];\n        this._prunedSessionIds = [];\n        this._currentlyReviewing = false;\n        this.indexingSessions = {};\n        this._maxConcurrentConnections = maxConcurrentConnections;\n        this.$indexingSessionTable = $indexingSessionTable;\n        this.indexingSessions = {};\n        let reviewSessionId = 0;\n        for (const sessionModel of sessions) {\n            let session = this.createSessionFromModel(sessionModel);\n            if (session.getSessionStatus() === SessionStatus.ACTIONREQUIRED &&\n                !reviewSessionId) {\n                reviewSessionId = session.getSessionId();\n            }\n            if (!reviewSessionId &&\n                this._currentIndexingSession == null &&\n                session.getSessionStatus() !== SessionStatus.ACTIONREQUIRED) {\n                this._currentIndexingSession = session.getSessionId();\n            }\n            this.updateIndexingSessionData(session);\n        }\n        if (this._currentIndexingSession) {\n            this.performIndexingStep();\n        }\n    }\n    get currentIndexingSession() {\n        return this._currentIndexingSession;\n    }\n    /**\n     * Update indexing session store\n     * @param session\n     */\n    updateIndexingSessionData(indexingSession) {\n        this.indexingSessions[indexingSession.getSessionId()] = indexingSession;\n        this.renderIndexingSessionRow(indexingSession);\n    }\n    /**\n     * Return a rendered indexing session row based on its id\n     * @param sessionId\n     */\n    renderIndexingSessionRow(session) {\n        let $row;\n        if (session === undefined) {\n            return;\n        }\n        if (!this.indexingSessions[session.getSessionId()] ||\n            this._prunedSessionIds.includes(session.getSessionId())) {\n            this.$indexingSessionTable\n                .find('tr[data-session-id=\"' + session.getSessionId() + '\"]')\n                .remove();\n            if (this.$indexingSessionTable.find('tbody tr').length == 0) {\n                this.$indexingSessionTable.addClass('hidden');\n            }\n            return;\n        }\n        $row = session.getIndexingSessionRowHtml();\n        const $existing = this.$indexingSessionTable.find('tr[data-session-id=\"' + session.getSessionId() + '\"]');\n        if ($existing.length > 0) {\n            $existing.replaceWith($row);\n        }\n        else {\n            this.$indexingSessionTable.find('tbody').append($row);\n        }\n        this.$indexingSessionTable.removeClass('hidden');\n    }\n    /**\n     * Remove an indexing session\n     * @param sessionId\n     * @protected\n     */\n    discardIndexingSession(sessionId) {\n        const session = this.indexingSessions[sessionId];\n        delete this.indexingSessions[sessionId];\n        if (this._currentIndexingSession === sessionId) {\n            this._currentIndexingSession = null;\n        }\n        this.renderIndexingSessionRow(session);\n        this.runTasks();\n    }\n    /**\n     * Process a failed indexing response.\n     *\n     * @param response\n     */\n    processFailureResponse(response) {\n        const responseData = response.data;\n        this._currentConnectionCount--;\n        this._updateCurrentIndexingSession();\n        Craft.cp.displayError(responseData.message);\n        if (responseData.stop) {\n            this.discardIndexingSession(responseData.stop);\n        }\n        // A mere error shall not stop the party.\n        this.runTasks();\n        return;\n    }\n    /**\n     * Process a successful indexing response.\n     *\n     * @param response\n     */\n    processSuccessResponse(response) {\n        const responseData = response.data;\n        this._currentConnectionCount--;\n        if (responseData.session) {\n            const session = this.createSessionFromModel(responseData.session);\n            this.indexingSessions[session.getSessionId()] = session;\n            this.renderIndexingSessionRow(session);\n            this._updateCurrentIndexingSession();\n            if (session.getSessionStatus() === SessionStatus.ACTIONREQUIRED &&\n                !responseData.skipDialog) {\n                if (!this._prunedSessionIds.includes(this._currentIndexingSession)) {\n                    this.reviewSession(session);\n                }\n                else {\n                    this.runTasks();\n                }\n            }\n            else if (!this._prunedSessionIds.includes(this._currentIndexingSession)) {\n                this.performIndexingStep();\n            }\n            else {\n                this.runTasks();\n            }\n        }\n        this._updateCurrentIndexingSession();\n        if (responseData.stop) {\n            this.discardIndexingSession(responseData.stop);\n        }\n    }\n    getReviewData(session) {\n        const task = {\n            sessionId: session.getSessionId(),\n            action: IndexingActions.OVERVIEW,\n            params: { sessionId: session.getSessionId() },\n            callback: () => {\n                this.renderIndexingSessionRow(session);\n            },\n        };\n        this.enqueueTask(task);\n    }\n    reviewSession(session) {\n        if (this._currentlyReviewing) {\n            return;\n        }\n        this._currentlyReviewing = true;\n        this.pruneWaitingTasks(session.getSessionId());\n        let $confirmBody = $('<div></div>');\n        const missingEntries = session.getMissingEntries();\n        const missingFiles = missingEntries.files\n            ? Object.entries(missingEntries.files)\n            : [];\n        const missingFolders = missingEntries.folders\n            ? Object.entries(missingEntries.folders)\n            : [];\n        const skippedFiles = session.getSkippedEntries();\n        if (skippedFiles.length) {\n            let skippedFilesList = '';\n            for (const skippedFile of skippedFiles) {\n                skippedFilesList += `<li>${skippedFile}</li>`;\n            }\n            $confirmBody.append(`\n                <h2>${Craft.t('app', 'Skipped files')}</h2>\n                <p>${Craft.t('app', 'The following items were not indexed.')}</p>\n                <ul>\n                    ${skippedFilesList}\n                </ul>\n            `);\n        }\n        const haveMissingItems = missingFiles.length || missingFolders.length;\n        if (haveMissingItems) {\n            if (missingFolders.length) {\n                let missingEntries = '';\n                for (const [id, uri] of missingFolders) {\n                    missingEntries += `<li><label><input type=\"checkbox\" checked=\"checked\" name=\"deleteFolder[]\" value=\"${id}\"> ${uri}</label></li>`;\n                }\n                const translationParams = { items: 'folders' };\n                let missingItemsHeading = this._getMissingItemsHeading('folders', translationParams, session);\n                let missingItemsCopy = this._getMissingItemsCopy('folders', translationParams, session);\n                $confirmBody.append($(`\n                <h2>${missingItemsHeading}</h2>\n                <p>${missingItemsCopy}</p>\n                <ul>\n                    ${missingEntries}\n                </ul>\n            `));\n            }\n            if (missingFiles.length) {\n                let missingEntries = '';\n                for (const [id, uri] of missingFiles) {\n                    missingEntries += `<li><label><input type=\"checkbox\" checked=\"checked\" name=\"deleteAsset[]\" value=\"${id}\"> ${uri}</label></li>`;\n                }\n                const translationParams = { items: 'files' };\n                let missingItemsHeading = this._getMissingItemsHeading('files', translationParams, session);\n                let missingItemsCopy = this._getMissingItemsCopy('files', translationParams, session);\n                $confirmBody.append($(`\n                <h2>${missingItemsHeading}</h2>\n                <p>${missingItemsCopy}</p>\n                <ul>\n                    ${missingEntries}\n                </ul>\n            `));\n            }\n        }\n        const $modal = $('<form class=\"modal fitted confirmmodal\"/>').appendTo(Garnish.$bod);\n        const $body = $('<div class=\"body\"/>')\n            .appendTo($modal)\n            .html($confirmBody.html());\n        const $footer = $('<footer class=\"footer\"/>').appendTo($modal);\n        const $buttons = $('<div class=\"buttons right\"/>').appendTo($footer);\n        const modal = new Garnish.Modal($modal, {\n            hideOnEsc: false,\n            hideOnShadeClick: false,\n            onHide: () => {\n                this._currentlyReviewing = false;\n            },\n        });\n        if (haveMissingItems) {\n            let $cancelBtn = $('<button/>', {\n                type: 'button',\n                class: 'btn',\n                text: Craft.t('app', 'Keep them'),\n            })\n                .on('click', (ev) => {\n                ev.preventDefault();\n                this.stopIndexingSession(session);\n                modal.hide();\n            })\n                .appendTo($buttons);\n            $('<button/>', {\n                type: 'submit',\n                class: 'btn submit',\n                text: Craft.t('app', 'Delete them'),\n            }).appendTo($buttons);\n        }\n        else {\n            $('<button/>', {\n                type: 'submit',\n                class: 'btn submit',\n                text: Craft.t('app', 'OK'),\n            }).appendTo($buttons);\n        }\n        Craft.initUiElements($body);\n        modal.updateSizeAndPosition();\n        $modal.on('submit', (ev) => {\n            ev.preventDefault();\n            modal.hide();\n            const postData = Garnish.getPostData($body);\n            const postParams = Craft.expandPostArray(postData);\n            postParams.sessionId = session.getSessionId();\n            // Make this the next task for sure?\n            const task = {\n                sessionId: session.getSessionId(),\n                action: IndexingActions.FINISH,\n                params: postParams,\n            };\n            this.enqueueTask(task, true);\n        });\n    }\n    _getMissingItemsHeading(missingType, translationParams, session) {\n        let missingItemsHeading = Craft.t('app', 'Missing {items}', translationParams);\n        if (missingType == 'folders' && session.getListEmptyFolders()) {\n            missingItemsHeading = Craft.t('app', 'Missing or empty {items}', translationParams);\n        }\n        return missingItemsHeading;\n    }\n    _getMissingItemsCopy(missingType, translationParams, session) {\n        let missingItemsCopy = Craft.t('app', 'The following {items} could not be found. Should they be deleted from the index?', translationParams);\n        if (missingType == 'folders' && session.getListEmptyFolders()) {\n            missingItemsCopy = Craft.t('app', 'The following {items} could not be found or are empty. Should they be deleted from the index?', translationParams);\n        }\n        return missingItemsCopy;\n    }\n    startIndexing(data, cb) {\n        Craft.sendActionRequest('POST', IndexingActions.START, { data })\n            .then((response) => this.processSuccessResponse(response))\n            .catch(({ response }) => this.processFailureResponse(response))\n            .finally(() => cb());\n    }\n    performIndexingStep() {\n        if (!this._currentIndexingSession) {\n            this._updateCurrentIndexingSession();\n        }\n        if (!this._currentIndexingSession) {\n            return;\n        }\n        const session = this.indexingSessions[this._currentIndexingSession];\n        const concurrentSlots = this._maxConcurrentConnections - this._currentConnectionCount;\n        // Queue up at least enough tasks to use up all the free connections of finish the session.\n        for (let i = 0; i < Math.min(concurrentSlots, session.getEntriesRemaining()); i++) {\n            const task = {\n                sessionId: session.getSessionId(),\n                action: IndexingActions.PROCESS,\n                params: { sessionId: this._currentIndexingSession },\n            };\n            this.enqueueTask(task);\n        }\n        if (session.getProcessIfRootEmpty()) {\n            const task = {\n                sessionId: session.getSessionId(),\n                action: IndexingActions.PROCESS,\n                params: { sessionId: this._currentIndexingSession },\n            };\n            this.enqueueTask(task);\n        }\n    }\n    /**\n     * Stop and discard an indexing session.\n     *\n     * @param session\n     */\n    stopIndexingSession(session) {\n        this.pruneWaitingTasks(session.getSessionId());\n        const task = {\n            sessionId: session.getSessionId(),\n            action: IndexingActions.STOP,\n            params: { sessionId: session.getSessionId() },\n        };\n        this.enqueueTask(task, true);\n    }\n    /**\n     * Pune the waiting task list by removing all tasks for a session id.\n     *\n     * @param sessionId\n     */\n    pruneWaitingTasks(sessionId) {\n        const newTaskList = [];\n        let modified = false;\n        this._prunedSessionIds.push(sessionId);\n        for (const task of this._tasksWaiting) {\n            if (task.sessionId !== sessionId) {\n                newTaskList.push(task);\n            }\n            else {\n                modified = true;\n            }\n        }\n        if (modified) {\n            this._tasksWaiting = newTaskList;\n        }\n    }\n    enqueueTask(task, prioritize = false) {\n        if (prioritize) {\n            this._priorityTasks.push(task);\n        }\n        else {\n            this._tasksWaiting.push(task);\n        }\n        this.runTasks();\n    }\n    runTasks() {\n        if (this._tasksWaiting.length + this._priorityTasks.length === 0 ||\n            this._currentConnectionCount >= this._maxConcurrentConnections) {\n            return;\n        }\n        while (this._tasksWaiting.length + this._priorityTasks.length !== 0 &&\n            this._currentConnectionCount < this._maxConcurrentConnections) {\n            this._currentConnectionCount++;\n            const task = this._priorityTasks.length > 0\n                ? this._priorityTasks.shift()\n                : this._tasksWaiting.shift();\n            Craft.sendActionRequest('POST', task.action, { data: task.params })\n                .then((response) => this.processSuccessResponse(response))\n                .catch(({ response }) => this.processFailureResponse(response))\n                .finally(() => {\n                if (task.callback) {\n                    task.callback();\n                }\n            });\n        }\n    }\n    _updateCurrentIndexingSession() {\n        for (const session of Object.values(this.indexingSessions)) {\n            if (session.getSessionStatus() !== SessionStatus.ACTIONREQUIRED) {\n                this._currentIndexingSession = session.getSessionId();\n                return;\n            }\n        }\n    }\n    /**\n     * Create a session from the data model.\n     *\n     * @param sessionData\n     * @private\n     */\n    createSessionFromModel(sessionData) {\n        return new AssetIndexingSession(sessionData, this);\n    }\n}\nclass AssetIndexingSession {\n    constructor(model, indexer) {\n        this.indexingSessionData = model;\n        this.indexer = indexer;\n    }\n    /**\n     * Get the session id\n     */\n    getSessionId() {\n        return this.indexingSessionData.id;\n    }\n    getProcessIfRootEmpty() {\n        return this.indexingSessionData.processIfRootEmpty;\n    }\n    getListEmptyFolders() {\n        return this.indexingSessionData.listEmptyFolders;\n    }\n    /**\n     * Get the remaining entry count for this sessions.\n     */\n    getEntriesRemaining() {\n        return (this.indexingSessionData.totalEntries -\n            this.indexingSessionData.processedEntries);\n    }\n    /**\n     * Get the session status.\n     */\n    getSessionStatus() {\n        if (this.indexingSessionData.actionRequired) {\n            return SessionStatus.ACTIONREQUIRED;\n        }\n        if (this.indexer.currentIndexingSession === this.indexingSessionData.id) {\n            return SessionStatus.ACTIVE;\n        }\n        return SessionStatus.WAITING;\n    }\n    /**\n     * Create row html as a JQuery object based on an indexing sessions\n     * @param session\n     * @private\n     */\n    getIndexingSessionRowHtml() {\n        const $tr = $('<tr class=\"indexingSession\" data-session-id=\"' +\n            this.getSessionId() +\n            '\">');\n        const $td = $('<td/>').appendTo($tr);\n        const $ul = $('<ul/>').appendTo($td);\n        for (const volume of Object.values(this.indexingSessionData.indexedVolumes)) {\n            $('<li/>', {\n                text: volume,\n            }).appendTo($ul);\n        }\n        $tr.append('<td>' + this.indexingSessionData.dateCreated + '</td>');\n        const $progressCell = $('<td class=\"progress\"><div class=\"progressContainer\"></div></td>').css('position', 'relative');\n        const progressBar = new Craft.ProgressBar($progressCell.find('.progressContainer'), false);\n        progressBar.setItemCount(this.indexingSessionData.totalEntries);\n        progressBar.setProcessedItemCount(this.indexingSessionData.processedEntries);\n        progressBar.updateProgressBar();\n        progressBar.showProgressBar();\n        $progressCell.data('progressBar', progressBar);\n        $progressCell\n            .find('.progressContainer')\n            .append(`<div class=\"progressInfo\">${this.indexingSessionData.processedEntries} / ${this.indexingSessionData.totalEntries}</div>`);\n        $tr.append($progressCell);\n        $tr.append('<td>' + this.getSessionStatusMessage() + '</td>');\n        const $actions = this.getActionButtons();\n        $('<td></td>').append($actions).appendTo($tr);\n        return $tr;\n    }\n    /**\n     * Get action buttons for an indexing session\n     * @param session\n     * @private\n     */\n    getActionButtons() {\n        const $buttons = $('<div class=\"buttons\"></div>');\n        if (this.getSessionStatus() == SessionStatus.ACTIONREQUIRED) {\n            const reviewMessage = Craft.t('app', 'Review');\n            $buttons.append($('<button />', {\n                type: 'button',\n                class: 'btn submit',\n                title: reviewMessage,\n                'aria-label': reviewMessage,\n            })\n                .text(reviewMessage)\n                .on('click', (ev) => {\n                const $container = $(ev.target).parent();\n                if ($container.hasClass('disabled')) {\n                    return;\n                }\n                $container.addClass('disabled');\n                this.indexer.getReviewData(this);\n            }));\n        }\n        const discardMessage = Craft.t('app', 'Discard');\n        $buttons.append($('<button />', {\n            type: 'button',\n            class: 'btn submit',\n            title: discardMessage,\n            'aria-label': discardMessage,\n        })\n            .text(discardMessage)\n            .on('click', (ev) => {\n            if ($buttons.hasClass('disabled')) {\n                return;\n            }\n            $buttons.addClass('disabled');\n            this.indexer.stopIndexingSession(this);\n        }));\n        return $buttons;\n    }\n    /**\n     * Get the session status verbose message\n     *\n     * @param status\n     */\n    getSessionStatusMessage() {\n        switch (this.getSessionStatus()) {\n            case SessionStatus.ACTIONREQUIRED:\n                return Craft.t('app', 'Waiting for review');\n                break;\n            case SessionStatus.ACTIVE:\n                return Craft.t('app', 'Active');\n                break;\n            case SessionStatus.WAITING:\n                return Craft.t('app', 'Waiting');\n                break;\n        }\n    }\n    /**\n     * Return a list of missing entries for this session\n     */\n    getMissingEntries() {\n        return this.indexingSessionData.missingEntries;\n    }\n    /**\n     * Return a list of skipped entries for this session\n     */\n    getSkippedEntries() {\n        return this.indexingSessionData.skippedEntries;\n    }\n}\n"],"names":["__webpack_require__","exports","definition","key","o","Object","defineProperty","enumerable","get","obj","prop","prototype","hasOwnProperty","call","Symbol","toStringTag","value","SessionStatus","IndexingActions","AssetIndexer","constructor","$indexingSessionTable","sessions","maxConcurrentConnections","this","_currentIndexingSession","_currentConnectionCount","_tasksWaiting","_priorityTasks","_prunedSessionIds","_currentlyReviewing","indexingSessions","_maxConcurrentConnections","reviewSessionId","sessionModel","session","createSessionFromModel","getSessionStatus","ACTIONREQUIRED","getSessionId","updateIndexingSessionData","performIndexingStep","currentIndexingSession","indexingSession","renderIndexingSessionRow","$row","undefined","includes","find","remove","length","addClass","getIndexingSessionRowHtml","$existing","replaceWith","append","removeClass","discardIndexingSession","sessionId","runTasks","processFailureResponse","response","responseData","data","_updateCurrentIndexingSession","Craft","cp","displayError","message","stop","processSuccessResponse","skipDialog","reviewSession","getReviewData","task","action","OVERVIEW","params","callback","enqueueTask","pruneWaitingTasks","$confirmBody","$","missingEntries","getMissingEntries","missingFiles","files","entries","missingFolders","folders","skippedFiles","getSkippedEntries","skippedFilesList","skippedFile","t","haveMissingItems","id","uri","translationParams","items","missingItemsHeading","_getMissingItemsHeading","missingItemsCopy","_getMissingItemsCopy","$modal","appendTo","Garnish","$bod","$body","html","$footer","$buttons","modal","Modal","hideOnEsc","hideOnShadeClick","onHide","type","class","text","on","ev","preventDefault","stopIndexingSession","hide","initUiElements","updateSizeAndPosition","postData","getPostData","postParams","expandPostArray","FINISH","missingType","getListEmptyFolders","startIndexing","cb","sendActionRequest","START","then","catch","finally","concurrentSlots","i","Math","min","getEntriesRemaining","PROCESS","getProcessIfRootEmpty","STOP","newTaskList","modified","push","prioritize","shift","values","sessionData","AssetIndexingSession","model","indexer","indexingSessionData","processIfRootEmpty","listEmptyFolders","totalEntries","processedEntries","actionRequired","ACTIVE","WAITING","$tr","$td","$ul","volume","indexedVolumes","dateCreated","$progressCell","css","progressBar","ProgressBar","setItemCount","setProcessedItemCount","updateProgressBar","showProgressBar","getSessionStatusMessage","$actions","getActionButtons","reviewMessage","title","$container","target","parent","hasClass","discardMessage","skippedEntries"],"sourceRoot":""}
\ No newline at end of file
+{"version":3,"file":"AssetIndexer.js","mappings":"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","sources":["webpack://Craft/webpack/bootstrap","webpack://Craft/webpack/runtime/define property getters","webpack://Craft/webpack/runtime/hasOwnProperty shorthand","webpack://Craft/webpack/runtime/make namespace object","webpack://Craft/./AssetIndexer.ts"],"sourcesContent":["// The require scope\nvar __webpack_require__ = {};\n\n","// define getter functions for harmony exports\n__webpack_require__.d = function(exports, definition) {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }","// define __esModule on exports\n__webpack_require__.r = function(exports) {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","var SessionStatus;\n(function (SessionStatus) {\n    SessionStatus[SessionStatus[\"ACTIONREQUIRED\"] = 0] = \"ACTIONREQUIRED\";\n    SessionStatus[SessionStatus[\"ACTIVE\"] = 1] = \"ACTIVE\";\n    SessionStatus[SessionStatus[\"WAITING\"] = 2] = \"WAITING\";\n})(SessionStatus || (SessionStatus = {}));\nvar IndexingActions;\n(function (IndexingActions) {\n    IndexingActions[\"START\"] = \"asset-indexes/start-indexing\";\n    IndexingActions[\"STOP\"] = \"asset-indexes/stop-indexing-session\";\n    IndexingActions[\"PROCESS\"] = \"asset-indexes/process-indexing-session\";\n    IndexingActions[\"OVERVIEW\"] = \"asset-indexes/indexing-session-overview\";\n    IndexingActions[\"FINISH\"] = \"asset-indexes/finish-indexing-session\";\n})(IndexingActions || (IndexingActions = {}));\n/**\n * Actual classes start here\n */\n// Asset Indexer\n// =====================================================================================\nexport class AssetIndexer {\n    /**\n     * @param $element The indexing session table\n     * @param sessions Existing indexing sessions\n     */\n    constructor($indexingSessionTable, sessions, maxConcurrentConnections = 3) {\n        this._currentIndexingSession = null;\n        this._currentConnectionCount = 0;\n        this._tasksWaiting = [];\n        this._priorityTasks = [];\n        this._prunedSessionIds = [];\n        this._currentlyReviewing = false;\n        this.indexingSessions = {};\n        this._maxConcurrentConnections = maxConcurrentConnections;\n        this.$indexingSessionTable = $indexingSessionTable;\n        this.indexingSessions = {};\n        let reviewSessionId = 0;\n        for (const sessionModel of sessions) {\n            let session = this.createSessionFromModel(sessionModel);\n            if (session.getSessionStatus() === SessionStatus.ACTIONREQUIRED &&\n                !reviewSessionId) {\n                reviewSessionId = session.getSessionId();\n            }\n            if (!reviewSessionId &&\n                this._currentIndexingSession == null &&\n                session.getSessionStatus() !== SessionStatus.ACTIONREQUIRED) {\n                this._currentIndexingSession = session.getSessionId();\n            }\n            this.updateIndexingSessionData(session);\n        }\n        if (this._currentIndexingSession) {\n            this.performIndexingStep();\n        }\n    }\n    get currentIndexingSession() {\n        return this._currentIndexingSession;\n    }\n    /**\n     * Update indexing session store\n     * @param session\n     */\n    updateIndexingSessionData(indexingSession) {\n        this.indexingSessions[indexingSession.getSessionId()] = indexingSession;\n        this.renderIndexingSessionRow(indexingSession);\n    }\n    /**\n     * Return a rendered indexing session row based on its id\n     * @param sessionId\n     */\n    renderIndexingSessionRow(session) {\n        let $row;\n        if (session === undefined) {\n            return;\n        }\n        if (!this.indexingSessions[session.getSessionId()] ||\n            this._prunedSessionIds.includes(session.getSessionId())) {\n            this.$indexingSessionTable\n                .find('tr[data-session-id=\"' + session.getSessionId() + '\"]')\n                .remove();\n            if (this.$indexingSessionTable.find('tbody tr').length == 0) {\n                this.$indexingSessionTable.addClass('hidden');\n            }\n            return;\n        }\n        $row = session.getIndexingSessionRowHtml();\n        const $existing = this.$indexingSessionTable.find('tr[data-session-id=\"' + session.getSessionId() + '\"]');\n        if ($existing.length > 0) {\n            $existing.replaceWith($row);\n        }\n        else {\n            this.$indexingSessionTable.find('tbody').append($row);\n        }\n        this.$indexingSessionTable.removeClass('hidden');\n    }\n    /**\n     * Remove an indexing session\n     * @param sessionId\n     * @protected\n     */\n    discardIndexingSession(sessionId) {\n        const session = this.indexingSessions[sessionId];\n        delete this.indexingSessions[sessionId];\n        if (this._currentIndexingSession === sessionId) {\n            this._currentIndexingSession = null;\n        }\n        this.renderIndexingSessionRow(session);\n        this.runTasks();\n    }\n    /**\n     * Process a failed indexing response.\n     *\n     * @param response\n     */\n    processFailureResponse(response) {\n        const responseData = response.data;\n        this._currentConnectionCount--;\n        this._updateCurrentIndexingSession();\n        Craft.cp.displayError(responseData.message);\n        if (responseData.stop) {\n            this.discardIndexingSession(responseData.stop);\n        }\n        // A mere error shall not stop the party.\n        this.runTasks();\n        return;\n    }\n    /**\n     * Process a successful indexing response.\n     *\n     * @param response\n     */\n    processSuccessResponse(response) {\n        const responseData = response.data;\n        this._currentConnectionCount--;\n        if (responseData.session) {\n            const session = this.createSessionFromModel(responseData.session);\n            this.indexingSessions[session.getSessionId()] = session;\n            this.renderIndexingSessionRow(session);\n            this._updateCurrentIndexingSession();\n            if (session.getSessionStatus() === SessionStatus.ACTIONREQUIRED &&\n                !responseData.skipDialog) {\n                if (!this._prunedSessionIds.includes(this._currentIndexingSession)) {\n                    this.reviewSession(session);\n                }\n                else {\n                    this.runTasks();\n                }\n            }\n            else if (!this._prunedSessionIds.includes(this._currentIndexingSession)) {\n                this.performIndexingStep();\n            }\n            else {\n                this.runTasks();\n            }\n        }\n        this._updateCurrentIndexingSession();\n        if (responseData.stop) {\n            this.discardIndexingSession(responseData.stop);\n        }\n    }\n    getReviewData(session) {\n        const task = {\n            sessionId: session.getSessionId(),\n            action: IndexingActions.OVERVIEW,\n            params: { sessionId: session.getSessionId() },\n            callback: () => {\n                this.renderIndexingSessionRow(session);\n            },\n        };\n        this.enqueueTask(task);\n    }\n    reviewSession(session) {\n        if (this._currentlyReviewing) {\n            return;\n        }\n        this._currentlyReviewing = true;\n        this.pruneWaitingTasks(session.getSessionId());\n        let $confirmBody = $('<div></div>');\n        const missingEntries = session.getMissingEntries();\n        const missingFiles = missingEntries.files\n            ? Object.entries(missingEntries.files)\n            : [];\n        const missingFolders = missingEntries.folders\n            ? Object.entries(missingEntries.folders)\n            : [];\n        const skippedFiles = session.getSkippedEntries();\n        if (skippedFiles.length) {\n            let skippedFilesList = '';\n            for (const skippedFile of skippedFiles) {\n                skippedFilesList += `<li>${Craft.escapeHtml(skippedFile)}</li>`;\n            }\n            $confirmBody.append(`\n                <h2>${Craft.t('app', 'Skipped files')}</h2>\n                <p>${Craft.t('app', 'The following items were not indexed.')}</p>\n                <ul>\n                    ${skippedFilesList}\n                </ul>\n            `);\n        }\n        const haveMissingItems = missingFiles.length || missingFolders.length;\n        if (haveMissingItems) {\n            if (missingFolders.length) {\n                let missingEntries = '';\n                for (const [id, uri] of missingFolders) {\n                    missingEntries += `<li><label><input type=\"checkbox\" checked=\"checked\" name=\"deleteFolder[]\" value=\"${id}\"> ${Craft.escapeHtml(uri)}</label></li>`;\n                }\n                const translationParams = { items: 'folders' };\n                let missingItemsHeading = this._getMissingItemsHeading('folders', translationParams, session);\n                let missingItemsCopy = this._getMissingItemsCopy('folders', translationParams, session);\n                $confirmBody.append($(`\n                <h2>${missingItemsHeading}</h2>\n                <p>${missingItemsCopy}</p>\n                <ul>\n                    ${missingEntries}\n                </ul>\n            `));\n            }\n            if (missingFiles.length) {\n                let missingEntries = '';\n                for (const [id, uri] of missingFiles) {\n                    missingEntries += `<li><label><input type=\"checkbox\" checked=\"checked\" name=\"deleteAsset[]\" value=\"${id}\"> ${Craft.escapeHtml(uri)}</label></li>`;\n                }\n                const translationParams = { items: 'files' };\n                let missingItemsHeading = this._getMissingItemsHeading('files', translationParams, session);\n                let missingItemsCopy = this._getMissingItemsCopy('files', translationParams, session);\n                $confirmBody.append($(`\n                <h2>${missingItemsHeading}</h2>\n                <p>${missingItemsCopy}</p>\n                <ul>\n                    ${missingEntries}\n                </ul>\n            `));\n            }\n        }\n        const $modal = $('<form class=\"modal fitted confirmmodal\"/>').appendTo(Garnish.$bod);\n        const $body = $('<div class=\"body\"/>')\n            .appendTo($modal)\n            .html($confirmBody.html());\n        const $footer = $('<footer class=\"footer\"/>').appendTo($modal);\n        const $buttons = $('<div class=\"buttons right\"/>').appendTo($footer);\n        const modal = new Garnish.Modal($modal, {\n            hideOnEsc: false,\n            hideOnShadeClick: false,\n            onHide: () => {\n                this._currentlyReviewing = false;\n            },\n        });\n        if (haveMissingItems) {\n            let $cancelBtn = $('<button/>', {\n                type: 'button',\n                class: 'btn',\n                text: Craft.t('app', 'Keep them'),\n            })\n                .on('click', (ev) => {\n                ev.preventDefault();\n                this.stopIndexingSession(session);\n                modal.hide();\n            })\n                .appendTo($buttons);\n            $('<button/>', {\n                type: 'submit',\n                class: 'btn submit',\n                text: Craft.t('app', 'Delete them'),\n            }).appendTo($buttons);\n        }\n        else {\n            $('<button/>', {\n                type: 'submit',\n                class: 'btn submit',\n                text: Craft.t('app', 'OK'),\n            }).appendTo($buttons);\n        }\n        Craft.initUiElements($body);\n        modal.updateSizeAndPosition();\n        $modal.on('submit', (ev) => {\n            ev.preventDefault();\n            modal.hide();\n            const postData = Garnish.getPostData($body);\n            const postParams = Craft.expandPostArray(postData);\n            postParams.sessionId = session.getSessionId();\n            // Make this the next task for sure?\n            const task = {\n                sessionId: session.getSessionId(),\n                action: IndexingActions.FINISH,\n                params: postParams,\n            };\n            this.enqueueTask(task, true);\n        });\n    }\n    _getMissingItemsHeading(missingType, translationParams, session) {\n        let missingItemsHeading = Craft.t('app', 'Missing {items}', translationParams);\n        if (missingType == 'folders' && session.getListEmptyFolders()) {\n            missingItemsHeading = Craft.t('app', 'Missing or empty {items}', translationParams);\n        }\n        return missingItemsHeading;\n    }\n    _getMissingItemsCopy(missingType, translationParams, session) {\n        let missingItemsCopy = Craft.t('app', 'The following {items} could not be found. Should they be deleted from the index?', translationParams);\n        if (missingType == 'folders' && session.getListEmptyFolders()) {\n            missingItemsCopy = Craft.t('app', 'The following {items} could not be found or are empty. Should they be deleted from the index?', translationParams);\n        }\n        return missingItemsCopy;\n    }\n    startIndexing(data, cb) {\n        Craft.sendActionRequest('POST', IndexingActions.START, { data })\n            .then((response) => this.processSuccessResponse(response))\n            .catch(({ response }) => this.processFailureResponse(response))\n            .finally(() => cb());\n    }\n    performIndexingStep() {\n        if (!this._currentIndexingSession) {\n            this._updateCurrentIndexingSession();\n        }\n        if (!this._currentIndexingSession) {\n            return;\n        }\n        const session = this.indexingSessions[this._currentIndexingSession];\n        const concurrentSlots = this._maxConcurrentConnections - this._currentConnectionCount;\n        // Queue up at least enough tasks to use up all the free connections of finish the session.\n        for (let i = 0; i < Math.min(concurrentSlots, session.getEntriesRemaining()); i++) {\n            const task = {\n                sessionId: session.getSessionId(),\n                action: IndexingActions.PROCESS,\n                params: { sessionId: this._currentIndexingSession },\n            };\n            this.enqueueTask(task);\n        }\n        if (session.getProcessIfRootEmpty()) {\n            const task = {\n                sessionId: session.getSessionId(),\n                action: IndexingActions.PROCESS,\n                params: { sessionId: this._currentIndexingSession },\n            };\n            this.enqueueTask(task);\n        }\n    }\n    /**\n     * Stop and discard an indexing session.\n     *\n     * @param session\n     */\n    stopIndexingSession(session) {\n        this.pruneWaitingTasks(session.getSessionId());\n        const task = {\n            sessionId: session.getSessionId(),\n            action: IndexingActions.STOP,\n            params: { sessionId: session.getSessionId() },\n        };\n        this.enqueueTask(task, true);\n    }\n    /**\n     * Pune the waiting task list by removing all tasks for a session id.\n     *\n     * @param sessionId\n     */\n    pruneWaitingTasks(sessionId) {\n        const newTaskList = [];\n        let modified = false;\n        this._prunedSessionIds.push(sessionId);\n        for (const task of this._tasksWaiting) {\n            if (task.sessionId !== sessionId) {\n                newTaskList.push(task);\n            }\n            else {\n                modified = true;\n            }\n        }\n        if (modified) {\n            this._tasksWaiting = newTaskList;\n        }\n    }\n    enqueueTask(task, prioritize = false) {\n        if (prioritize) {\n            this._priorityTasks.push(task);\n        }\n        else {\n            this._tasksWaiting.push(task);\n        }\n        this.runTasks();\n    }\n    runTasks() {\n        if (this._tasksWaiting.length + this._priorityTasks.length === 0 ||\n            this._currentConnectionCount >= this._maxConcurrentConnections) {\n            return;\n        }\n        while (this._tasksWaiting.length + this._priorityTasks.length !== 0 &&\n            this._currentConnectionCount < this._maxConcurrentConnections) {\n            this._currentConnectionCount++;\n            const task = this._priorityTasks.length > 0\n                ? this._priorityTasks.shift()\n                : this._tasksWaiting.shift();\n            Craft.sendActionRequest('POST', task.action, { data: task.params })\n                .then((response) => this.processSuccessResponse(response))\n                .catch(({ response }) => this.processFailureResponse(response))\n                .finally(() => {\n                if (task.callback) {\n                    task.callback();\n                }\n            });\n        }\n    }\n    _updateCurrentIndexingSession() {\n        for (const session of Object.values(this.indexingSessions)) {\n            if (session.getSessionStatus() !== SessionStatus.ACTIONREQUIRED) {\n                this._currentIndexingSession = session.getSessionId();\n                return;\n            }\n        }\n    }\n    /**\n     * Create a session from the data model.\n     *\n     * @param sessionData\n     * @private\n     */\n    createSessionFromModel(sessionData) {\n        return new AssetIndexingSession(sessionData, this);\n    }\n}\nclass AssetIndexingSession {\n    constructor(model, indexer) {\n        this.indexingSessionData = model;\n        this.indexer = indexer;\n    }\n    /**\n     * Get the session id\n     */\n    getSessionId() {\n        return this.indexingSessionData.id;\n    }\n    getProcessIfRootEmpty() {\n        return this.indexingSessionData.processIfRootEmpty;\n    }\n    getListEmptyFolders() {\n        return this.indexingSessionData.listEmptyFolders;\n    }\n    /**\n     * Get the remaining entry count for this sessions.\n     */\n    getEntriesRemaining() {\n        return (this.indexingSessionData.totalEntries -\n            this.indexingSessionData.processedEntries);\n    }\n    /**\n     * Get the session status.\n     */\n    getSessionStatus() {\n        if (this.indexingSessionData.actionRequired) {\n            return SessionStatus.ACTIONREQUIRED;\n        }\n        if (this.indexer.currentIndexingSession === this.indexingSessionData.id) {\n            return SessionStatus.ACTIVE;\n        }\n        return SessionStatus.WAITING;\n    }\n    /**\n     * Create row html as a JQuery object based on an indexing sessions\n     * @param session\n     * @private\n     */\n    getIndexingSessionRowHtml() {\n        const $tr = $('<tr class=\"indexingSession\" data-session-id=\"' +\n            this.getSessionId() +\n            '\">');\n        const $td = $('<td/>').appendTo($tr);\n        const $ul = $('<ul/>').appendTo($td);\n        for (const volume of Object.values(this.indexingSessionData.indexedVolumes)) {\n            $('<li/>', {\n                text: volume,\n            }).appendTo($ul);\n        }\n        $tr.append('<td>' + this.indexingSessionData.dateCreated + '</td>');\n        const $progressCell = $('<td class=\"progress\"><div class=\"progressContainer\"></div></td>').css('position', 'relative');\n        const progressBar = new Craft.ProgressBar($progressCell.find('.progressContainer'), false);\n        progressBar.setItemCount(this.indexingSessionData.totalEntries);\n        progressBar.setProcessedItemCount(this.indexingSessionData.processedEntries);\n        progressBar.updateProgressBar();\n        progressBar.showProgressBar();\n        $progressCell.data('progressBar', progressBar);\n        $progressCell\n            .find('.progressContainer')\n            .append(`<div class=\"progressInfo\">${this.indexingSessionData.processedEntries} / ${this.indexingSessionData.totalEntries}</div>`);\n        $tr.append($progressCell);\n        $tr.append('<td>' + this.getSessionStatusMessage() + '</td>');\n        const $actions = this.getActionButtons();\n        $('<td></td>').append($actions).appendTo($tr);\n        return $tr;\n    }\n    /**\n     * Get action buttons for an indexing session\n     * @param session\n     * @private\n     */\n    getActionButtons() {\n        const $buttons = $('<div class=\"buttons\"></div>');\n        if (this.getSessionStatus() == SessionStatus.ACTIONREQUIRED) {\n            const reviewMessage = Craft.t('app', 'Review');\n            $buttons.append($('<button />', {\n                type: 'button',\n                class: 'btn submit',\n                title: reviewMessage,\n                'aria-label': reviewMessage,\n            })\n                .text(reviewMessage)\n                .on('click', (ev) => {\n                const $container = $(ev.target).parent();\n                if ($container.hasClass('disabled')) {\n                    return;\n                }\n                $container.addClass('disabled');\n                this.indexer.getReviewData(this);\n            }));\n        }\n        const discardMessage = Craft.t('app', 'Discard');\n        $buttons.append($('<button />', {\n            type: 'button',\n            class: 'btn submit',\n            title: discardMessage,\n            'aria-label': discardMessage,\n        })\n            .text(discardMessage)\n            .on('click', (ev) => {\n            if ($buttons.hasClass('disabled')) {\n                return;\n            }\n            $buttons.addClass('disabled');\n            this.indexer.stopIndexingSession(this);\n        }));\n        return $buttons;\n    }\n    /**\n     * Get the session status verbose message\n     *\n     * @param status\n     */\n    getSessionStatusMessage() {\n        switch (this.getSessionStatus()) {\n            case SessionStatus.ACTIONREQUIRED:\n                return Craft.t('app', 'Waiting for review');\n                break;\n            case SessionStatus.ACTIVE:\n                return Craft.t('app', 'Active');\n                break;\n            case SessionStatus.WAITING:\n                return Craft.t('app', 'Waiting');\n                break;\n        }\n    }\n    /**\n     * Return a list of missing entries for this session\n     */\n    getMissingEntries() {\n        return this.indexingSessionData.missingEntries;\n    }\n    /**\n     * Return a list of skipped entries for this session\n     */\n    getSkippedEntries() {\n        return this.indexingSessionData.skippedEntries;\n    }\n}\n"],"names":["__webpack_require__","exports","definition","key","o","Object","defineProperty","enumerable","get","obj","prop","prototype","hasOwnProperty","call","Symbol","toStringTag","value","SessionStatus","IndexingActions","AssetIndexer","constructor","$indexingSessionTable","sessions","maxConcurrentConnections","this","_currentIndexingSession","_currentConnectionCount","_tasksWaiting","_priorityTasks","_prunedSessionIds","_currentlyReviewing","indexingSessions","_maxConcurrentConnections","reviewSessionId","sessionModel","session","createSessionFromModel","getSessionStatus","ACTIONREQUIRED","getSessionId","updateIndexingSessionData","performIndexingStep","currentIndexingSession","indexingSession","renderIndexingSessionRow","$row","undefined","includes","find","remove","length","addClass","getIndexingSessionRowHtml","$existing","replaceWith","append","removeClass","discardIndexingSession","sessionId","runTasks","processFailureResponse","response","responseData","data","_updateCurrentIndexingSession","Craft","cp","displayError","message","stop","processSuccessResponse","skipDialog","reviewSession","getReviewData","task","action","OVERVIEW","params","callback","enqueueTask","pruneWaitingTasks","$confirmBody","$","missingEntries","getMissingEntries","missingFiles","files","entries","missingFolders","folders","skippedFiles","getSkippedEntries","skippedFilesList","skippedFile","escapeHtml","t","haveMissingItems","id","uri","translationParams","items","missingItemsHeading","_getMissingItemsHeading","missingItemsCopy","_getMissingItemsCopy","$modal","appendTo","Garnish","$bod","$body","html","$footer","$buttons","modal","Modal","hideOnEsc","hideOnShadeClick","onHide","type","class","text","on","ev","preventDefault","stopIndexingSession","hide","initUiElements","updateSizeAndPosition","postData","getPostData","postParams","expandPostArray","FINISH","missingType","getListEmptyFolders","startIndexing","cb","sendActionRequest","START","then","catch","finally","concurrentSlots","i","Math","min","getEntriesRemaining","PROCESS","getProcessIfRootEmpty","STOP","newTaskList","modified","push","prioritize","shift","values","sessionData","AssetIndexingSession","model","indexer","indexingSessionData","processIfRootEmpty","listEmptyFolders","totalEntries","processedEntries","actionRequired","ACTIVE","WAITING","$tr","$td","$ul","volume","indexedVolumes","dateCreated","$progressCell","css","progressBar","ProgressBar","setItemCount","setProcessedItemCount","updateProgressBar","showProgressBar","getSessionStatusMessage","$actions","getActionButtons","reviewMessage","title","$container","target","parent","hasClass","discardMessage","skippedEntries"],"sourceRoot":""}
\ No newline at end of file
diff --git a/src/web/assets/assetindexes/src/AssetIndexer.ts b/src/web/assets/assetindexes/src/AssetIndexer.ts
index 53ed8bfed5c..8710d3cb975 100644
--- a/src/web/assets/assetindexes/src/AssetIndexer.ts
+++ b/src/web/assets/assetindexes/src/AssetIndexer.ts
@@ -279,11 +279,11 @@ export class AssetIndexer {
 
     if (skippedFiles.length) {
       let skippedFilesList = '';
 
       for (const skippedFile of skippedFiles) {
-        skippedFilesList += `<li>${skippedFile}</li>`;
+        skippedFilesList += `<li>${Craft.escapeHtml(skippedFile)}</li>`;
       }
 
       $confirmBody.append(`
                 <h2>${Craft.t('app', 'Skipped files')}</h2>
                 <p>${Craft.t(
@@ -300,11 +300,13 @@ export class AssetIndexer {
 
     if (haveMissingItems) {
       if (missingFolders.length) {
         let missingEntries = '';
         for (const [id, uri] of missingFolders) {
-          missingEntries += `<li><label><input type="checkbox" checked="checked" name="deleteFolder[]" value="${id}"> ${uri}</label></li>`;
+          missingEntries += `<li><label><input type="checkbox" checked="checked" name="deleteFolder[]" value="${id}"> ${Craft.escapeHtml(
+            uri
+          )}</label></li>`;
         }
 
         const translationParams = {items: 'folders'};
         let missingItemsHeading = this._getMissingItemsHeading(
           'folders',
@@ -329,11 +331,13 @@ export class AssetIndexer {
       }
 
       if (missingFiles.length) {
         let missingEntries = '';
         for (const [id, uri] of missingFiles) {
-          missingEntries += `<li><label><input type="checkbox" checked="checked" name="deleteAsset[]" value="${id}"> ${uri}</label></li>`;
+          missingEntries += `<li><label><input type="checkbox" checked="checked" name="deleteAsset[]" value="${id}"> ${Craft.escapeHtml(
+            uri
+          )}</label></li>`;
         }
 
         const translationParams = {items: 'files'};
         let missingItemsHeading = this._getMissingItemsHeading(
           'files',

Vulnerable code: !function(){var s={d:function(e,n){for(var t in n)s.o(n,t)&&!s.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:n[t]})},o:function(s,e){return Object.prototype.hasOwnProperty.call(s,e)},r:function(s){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(s,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(s,"__esModule",{value:!0})}},e={};!function(){"use strict";var n,t;s.r(e),s.d(e,{AssetIndexer:function(){return i}}),function(s){s[s.ACTIONREQUIRED=0]="ACTIONREQUIRED",s[s.ACTIVE=1]="ACTIVE",s[s.WAITING=2]="WAITING"}(n||(n={})),function(s){s.START="asset-indexes/start-indexing",s.STOP="asset-indexes/stop-indexing-session",s.PROCESS="asset-indexes/process-indexing-session",s.OVERVIEW="asset-indexes/indexing-session-overview",s.FINISH="asset-indexes/finish-indexing-session"}(t||(t={}));class i{constructor(s,e,t=3){this._currentIndexingSession=null,this._currentConnectionCount=0,this._tasksWaiting=[],this._priorityTasks=[],this._prunedSessionIds=[],this._currentlyReviewing=!1,this.indexingSessions={},this._maxConcurrentConnections=t,this.$indexingSessionTable=s,this.indexingSessions={};let i=0;for(const s of e){let e=this.createSessionFromModel(s);e.getSessionStatus()!==n.ACTIONREQUIRED||i||(i=e.getSessionId()),i||null!=this._currentIndexingSession||e.getSessionStatus()===n.ACTIONREQUIRED||(this._currentIndexingSession=e.getSessionId()),this.updateIndexingSessionData(e)}this._currentIndexingSession&&this.performIndexingStep()}get currentIndexingSession(){return this._currentIndexingSession}updateIndexingSessionData(s){this.indexingSessions[s.getSessionId()]=s,this.renderIndexingSessionRow(s)}renderIndexingSessionRow(s){let e;if(void 0===s)return;if(!this.indexingSessions[s.getSessionId()]||this._prunedSessionIds.includes(s.getSessionId()))return this.$indexingSessionTable.find('tr[data-session-id="'+s.getSessionId()+'"]').remove(),void(0==this.$indexingSessionTable.find("tbody tr").length&&this.$indexingSessionTable.addClass("hidden"));e=s.getIndexingSessionRowHtml();const n=this.$indexingSessionTable.find('tr[data-session-id="'+s.getSessionId()+'"]');n.length>0?n.replaceWith(e):this.$indexingSessionTable.find("tbody").append(e),this.$indexingSessionTable.removeClass("hidden")}discardIndexingSession(s){const e=this.indexingSessions[s];delete this.indexingSessions[s],this._currentIndexingSession===s&&(this._currentIndexingSession=null),this.renderIndexingSessionRow(e),this.runTasks()}processFailureResponse(s){const e=s.data;this._currentConnectionCount--,this._updateCurrentIndexingSession(),Craft.cp.displayError(e.message),e.stop&&this.discardIndexingSession(e.stop),this.runTasks()}processSuccessResponse(s){const e=s.data;if(this._currentConnectionCount--,e.session){const s=this.createSessionFromModel(e.session);this.indexingSessions[s.getSessionId()]=s,this.renderIndexingSessionRow(s),this._updateCurrentIndexingSession(),s.getSessionStatus()!==n.ACTIONREQUIRED||e.skipDialog?this._prunedSessionIds.includes(this._currentIndexingSession)?this.runTasks():this.performIndexingStep():this._prunedSessionIds.includes(this._currentIndexingSession)?this.runTasks():this.reviewSession(s)}this._updateCurrentIndexingSession(),e.stop&&this.discardIndexingSession(e.stop)}getReviewData(s){const e={sessionId:s.getSessionId(),action:t.OVERVIEW,params:{sessionId:s.getSessionId()},callback:()=>{this.renderIndexingSessionRow(s)}};this.enqueueTask(e)}reviewSession(s){if(this._currentlyReviewing)return;this._currentlyReviewing=!0,this.pruneWaitingTasks(s.getSessionId());let e=$("<div></div>");const n=s.getMissingEntries(),i=n.files?Object.entries(n.files):[],o=n.folders?Object.entries(n.folders):[],r=s.getSkippedEntries();if(r.length){let s="";for(const e of r)s+=`<li>${e}</li>`;e.append(`\n                <h2>${Craft.t("app","Skipped files")}</h2>\n                <p>${Craft.t("app","The following items were not indexed.")}</p>\n                <ul>\n                    ${s}\n                </ul>\n            `)}const a=i.length||o.length;if(a){if(o.length){let n="";for(const[s,e]of o)n+=`<li><label><input type="checkbox" checked="checked" name="deleteFolder[]" value="${s}"> ${e}</label></li>`;const t={items:"folders"};let i=this._getMissingItemsHeading("folders",t,s),r=this._getMissingItemsCopy("folders",t,s);e.append($(`\n                <h2>${i}</h2>\n                <p>${r}</p>\n                <ul>\n                    ${n}\n                </ul>\n            `))}if(i.length){let n="";for(const[s,e]of i)n+=`<li><label><input type="checkbox" checked="checked" name="deleteAsset[]" value="${s}"> ${e}</label></li>`;const t={items:"files"};let o=this._getMissingItemsHeading("files",t,s),r=this._getMissingItemsCopy("files",t,s);e.append($(`\n                <h2>${o}</h2>\n                <p>${r}</p>\n                <ul>\n                    ${n}\n                </ul>\n            `))}}const d=$('<form class="modal fitted confirmmodal"/>').appendTo(Garnish.$bod),g=$('<div class="body"/>').appendTo(d).html(e.html()),p=$('<footer class="footer"/>').appendTo(d),c=$('<div class="buttons right"/>').appendTo(p),h=new Garnish.Modal(d,{hideOnEsc:!1,hideOnShadeClick:!1,onHide:()=>{this._currentlyReviewing=!1}});a?($("<button/>",{type:"button",class:"btn",text:Craft.t("app","Keep them")}).on("click",(e=>{e.preventDefault(),this.stopIndexingSession(s),h.hide()})).appendTo(c),$("<button/>",{type:"submit",class:"btn submit",text:Craft.t("app","Delete them")}).appendTo(c)):$("<button/>",{type:"submit",class:"btn submit",text:Craft.t("app","OK")}).appendTo(c),Craft.initUiElements(g),h.updateSizeAndPosition(),d.on("submit",(e=>{e.preventDefault(),h.hide();const n=Garnish.getPostData(g),i=Craft.expandPostArray(n);i.sessionId=s.getSessionId();const o={sessionId:s.getSessionId(),action:t.FINISH,params:i};this.enqueueTask(o,!0)}))}_getMissingItemsHeading(s,e,n){let t=Craft.t("app","Missing {items}",e);return"folders"==s&&n.getListEmptyFolders()&&(t=Craft.t("app","Missing or empty {items}",e)),t}_getMissingItemsCopy(s,e,n){let t=Craft.t("app","The following {items} could not be found. Should they be deleted from the index?",e);return"folders"==s&&n.getListEmptyFolders()&&(t=Craft.t("app","The following {items} could not be found or are empty. Should they be deleted from the index?",e)),t}startIndexing(s,e){Craft.sendActionRequest("POST",t.START,{data:s}).then((s=>this.processSuccessResponse(s))).catch((({response:s})=>this.processFailureResponse(s))).finally((()=>e()))}performIndexingStep(){if(this._currentIndexingSession||this._updateCurrentIndexingSession(),!this._currentIndexingSession)return;const s=this.indexingSessions[this._currentIndexingSession],e=this._maxConcurrentConnections-this._currentConnectionCount;for(let n=0;n<Math.min(e,s.getEntriesRemaining());n++){const e={sessionId:s.getSessionId(),action:t.PROCESS,params:{sessionId:this._currentIndexingSession}};this.enqueueTask(e)}if(s.getProcessIfRootEmpty()){const e={sessionId:s.getSessionId(),action:t.PROCESS,params:{sessionId:this._currentIndexingSession}};this.enqueueTask(e)}}stopIndexingSession(s){this.pruneWaitingTasks(s.getSessionId());const e={sessionId:s.getSessionId(),action:t.STOP,params:{sessionId:s.getSessionId()}};this.enqueueTask(e,!0)}pruneWaitingTasks(s){const e=[];let n=!1;this._prunedSessionIds.push(s);for(const t of this._tasksWaiting)t.sessionId!==s?e.push(t):n=!0;n&&(this._tasksWaiting=e)}enqueueTask(s,e=!1){e?this._priorityTasks.push(s):this._tasksWaiting.push(s),this.runTasks()}runTasks(){if(!(this._tasksWaiting.length+this._priorityTasks.length===0||this._currentConnectionCount>=this._maxConcurrentConnections))for(;this._tasksWaiting.length+this._priorityTasks.length!==0&&this._currentConnectionCount<this._maxConcurrentConnections;){this._currentConnectionCount++;const s=this._priorityTasks.length>0?this._priorityTasks.shift():this._tasksWaiting.shift();Craft.sendActionRequest("POST",s.action,{data:s.params}).then((s=>this.processSuccessResponse(s))).catch((({response:s})=>this.processFailureResponse(s))).finally((()=>{s.callback&&s.callback()}))}}_updateCurrentIndexingSession(){for(const s of Object.values(this.indexingSessions))if(s.getSessionStatus()!==n.ACTIONREQUIRED)return void(this._currentIndexingSession=s.getSessionId())}createSessionFromModel(s){return new o(s,this)}}class o{constructor(s,e){this.indexingSessionData=s,this.indexer=e}getSessionId(){return this.indexingSessionData.id}getProcessIfRootEmpty(){return this.indexingSessionData.processIfRootEmpty}getListEmptyFolders(){return this.indexingSessionData.listEmptyFolders}getEntriesRemaining(){return this.indexingSessionData.totalEntries-this.indexingSessionData.processedEntries}getSessionStatus(){return this.indexingSessionData.actionRequired?n.ACTIONREQUIRED:this.indexer.currentIndexingSession===this.indexingSessionData.id?n.ACTIVE:n.WAITING}getIndexingSessionRowHtml(){const s=$('<tr class="indexingSession" data-session-id="'+this.getSessionId()+'">'),e=$("<td/>").appendTo(s),n=$("<ul/>").appendTo(e);for(const s of Object.values(this.indexingSessionData.indexedVolumes))$("<li/>",{text:s}).appendTo(n);s.append("<td>"+this.indexingSessionData.dateCreated+"</td>");const t=$('<td class="progress"><div class="progressContainer"></div></td>').css("position","relative"),i=new Craft.ProgressBar(t.find(".progressContainer"),!1);i.setItemCount(this.indexingSessionData.totalEntries),i.setProcessedItemCount(this.indexingSessionData.processedEntries),i.updateProgressBar(),i.showProgressBar(),t.data("progressBar",i),t.find(".progressContainer").append(`<div class="progressInfo">${this.indexingSessionData.processedEntries} / ${this.indexingSessionData.totalEntries}</div>`),s.append(t),s.append("<td>"+this.getSessionStatusMessage()+"</td>");const o=this.getActionButtons();return $("<td></td>").append(o).appendTo(s),s}getActionButtons(){const s=$('<div class="buttons"></div>');if(this.getSessionStatus()==n.ACTIONREQUIRED){const e=Craft.t("app","Review");s.append($("<button />",{type:"button",class:"btn submit",title:e,"aria-label":e}).text(e).on("click",(s=>{const e=$(s.target).parent();e.hasClass("disabled")||(e.addClass("disabled"),this.indexer.getReviewData(this))})))}const e=Craft.t("app","Discard");return s.append($("<button />",{type:"button",class:"btn submit",title:e,"aria-label":e}).text(e).on("click",(e=>{s.hasClass("disabled")||(s.addClass("disabled"),this.indexer.stopIndexingSession(this))}))),s}getSessionStatusMessage(){switch(this.getSessionStatus()){case n.ACTIONREQUIRED:return Craft.t("app","Waiting for review");case n.ACTIVE:return Craft.t("app","Active");case n.WAITING:return Craft.t("app","Waiting")}}getMissingEntries(){return this.indexingSessionData.missingEntries}getSkippedEntries(){return this.indexingSessionData.skippedEntries}}}();var n=Craft="undefined"==typeof Craft?{}:Craft;for(var t in e)n[t]=e[t];e.__esModule&&Object.defineProperty(n,"__esModule",{value:!0})}();
{"version":3,"file":"AssetIndexer.js","mappings":"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","sources":["webpack://Craft/webpack/bootstrap","webpack://Craft/webpack/runtime/define property getters","webpack://Craft/webpack/runtime/hasOwnProperty shorthand","webpack://Craft/webpack/runtime/make namespace object","webpack://Craft/./AssetIndexer.ts"],"sourcesContent":["// The require scope\nvar __webpack_require__ = {};\n\n","// define getter functions for harmony exports\n__webpack_require__.d = function(exports, definition) {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }","// define __esModule on exports\n__webpack_require__.r = function(exports) {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","var SessionStatus;\n(function (SessionStatus) {\n    SessionStatus[SessionStatus[\"ACTIONREQUIRED\"] = 0] = \"ACTIONREQUIRED\";\n    SessionStatus[SessionStatus[\"ACTIVE\"] = 1] = \"ACTIVE\";\n    SessionStatus[SessionStatus[\"WAITING\"] = 2] = \"WAITING\";\n})(SessionStatus || (SessionStatus = {}));\nvar IndexingActions;\n(function (IndexingActions) {\n    IndexingActions[\"START\"] = \"asset-indexes/start-indexing\";\n    IndexingActions[\"STOP\"] = \"asset-indexes/stop-indexing-session\";\n    IndexingActions[\"PROCESS\"] = \"asset-indexes/process-indexing-session\";\n    IndexingActions[\"OVERVIEW\"] = \"asset-indexes/indexing-session-overview\";\n    IndexingActions[\"FINISH\"] = \"asset-indexes/finish-indexing-session\";\n})(IndexingActions || (IndexingActions = {}));\n/**\n * Actual classes start here\n */\n// Asset Indexer\n// =====================================================================================\nexport class AssetIndexer {\n    /**\n     * @param $element The indexing session table\n     * @param sessions Existing indexing sessions\n     */\n    constructor($indexingSessionTable, sessions, maxConcurrentConnections = 3) {\n        this._currentIndexingSession = null;\n        this._currentConnectionCount = 0;\n        this._tasksWaiting = [];\n        this._priorityTasks = [];\n        this._prunedSessionIds = [];\n        this._currentlyReviewing = false;\n        this.indexingSessions = {};\n        this._maxConcurrentConnections = maxConcurrentConnections;\n        this.$indexingSessionTable = $indexingSessionTable;\n        this.indexingSessions = {};\n        let reviewSessionId = 0;\n        for (const sessionModel of sessions) {\n            let session = this.createSessionFromModel(sessionModel);\n            if (session.getSessionStatus() === SessionStatus.ACTIONREQUIRED &&\n                !reviewSessionId) {\n                reviewSessionId = session.getSessionId();\n            }\n            if (!reviewSessionId &&\n                this._currentIndexingSession == null &&\n                session.getSessionStatus() !== SessionStatus.ACTIONREQUIRED) {\n                this._currentIndexingSession = session.getSessionId();\n            }\n            this.updateIndexingSessionData(session);\n        }\n        if (this._currentIndexingSession) {\n            this.performIndexingStep();\n        }\n    }\n    get currentIndexingSession() {\n        return this._currentIndexingSession;\n    }\n    /**\n     * Update indexing session store\n     * @param session\n     */\n    updateIndexingSessionData(indexingSession) {\n        this.indexingSessions[indexingSession.getSessionId()] = indexingSession;\n        this.renderIndexingSessionRow(indexingSession);\n    }\n    /**\n     * Return a rendered indexing session row based on its id\n     * @param sessionId\n     */\n    renderIndexingSessionRow(session) {\n        let $row;\n        if (session === undefined) {\n            return;\n        }\n        if (!this.indexingSessions[session.getSessionId()] ||\n            this._prunedSessionIds.includes(session.getSessionId())) {\n            this.$indexingSessionTable\n                .find('tr[data-session-id=\"' + session.getSessionId() + '\"]')\n                .remove();\n            if (this.$indexingSessionTable.find('tbody tr').length == 0) {\n                this.$indexingSessionTable.addClass('hidden');\n            }\n            return;\n        }\n        $row = session.getIndexingSessionRowHtml();\n        const $existing = this.$indexingSessionTable.find('tr[data-session-id=\"' + session.getSessionId() + '\"]');\n        if ($existing.length > 0) {\n            $existing.replaceWith($row);\n        }\n        else {\n            this.$indexingSessionTable.find('tbody').append($row);\n        }\n        this.$indexingSessionTable.removeClass('hidden');\n    }\n    /**\n     * Remove an indexing session\n     * @param sessionId\n     * @protected\n     */\n    discardIndexingSession(sessionId) {\n        const session = this.indexingSessions[sessionId];\n        delete this.indexingSessions[sessionId];\n        if (this._currentIndexingSession === sessionId) {\n            this._currentIndexingSession = null;\n        }\n        this.renderIndexingSessionRow(session);\n        this.runTasks();\n    }\n    /**\n     * Process a failed indexing response.\n     *\n     * @param response\n     */\n    processFailureResponse(response) {\n        const responseData = response.data;\n        this._currentConnectionCount--;\n        this._updateCurrentIndexingSession();\n        Craft.cp.displayError(responseData.message);\n        if (responseData.stop) {\n            this.discardIndexingSession(responseData.stop);\n        }\n        // A mere error shall not stop the party.\n        this.runTasks();\n        return;\n    }\n    /**\n     * Process a successful indexing response.\n     *\n     * @param response\n     */\n    processSuccessResponse(response) {\n        const responseData = response.data;\n        this._currentConnectionCount--;\n        if (responseData.session) {\n            const session = this.createSessionFromModel(responseData.session);\n            this.indexingSessions[session.getSessionId()] = session;\n            this.renderIndexingSessionRow(session);\n            this._updateCurrentIndexingSession();\n            if (session.getSessionStatus() === SessionStatus.ACTIONREQUIRED &&\n                !responseData.skipDialog) {\n                if (!this._prunedSessionIds.includes(this._currentIndexingSession)) {\n                    this.reviewSession(session);\n                }\n                else {\n                    this.runTasks();\n                }\n            }\n            else if (!this._prunedSessionIds.includes(this._currentIndexingSession)) {\n                this.performIndexingStep();\n            }\n            else {\n                this.runTasks();\n            }\n        }\n        this._updateCurrentIndexingSession();\n        if (responseData.stop) {\n            this.discardIndexingSession(responseData.stop);\n        }\n    }\n    getReviewData(session) {\n        const task = {\n            sessionId: session.getSessionId(),\n            action: IndexingActions.OVERVIEW,\n            params: { sessionId: session.getSessionId() },\n            callback: () => {\n                this.renderIndexingSessionRow(session);\n            },\n        };\n        this.enqueueTask(task);\n    }\n    reviewSession(session) {\n        if (this._currentlyReviewing) {\n            return;\n        }\n        this._currentlyReviewing = true;\n        this.pruneWaitingTasks(session.getSessionId());\n        let $confirmBody = $('<div></div>');\n        const missingEntries = session.getMissingEntries();\n        const missingFiles = missingEntries.files\n            ? Object.entries(missingEntries.files)\n            : [];\n        const missingFolders = missingEntries.folders\n            ? Object.entries(missingEntries.folders)\n            : [];\n        const skippedFiles = session.getSkippedEntries();\n        if (skippedFiles.length) {\n            let skippedFilesList = '';\n            for (const skippedFile of skippedFiles) {\n                skippedFilesList += `<li>${skippedFile}</li>`;\n            }\n            $confirmBody.append(`\n                <h2>${Craft.t('app', 'Skipped files')}</h2>\n                <p>${Craft.t('app', 'The following items were not indexed.')}</p>\n                <ul>\n                    ${skippedFilesList}\n                </ul>\n            `);\n        }\n        const haveMissingItems = missingFiles.length || missingFolders.length;\n        if (haveMissingItems) {\n            if (missingFolders.length) {\n                let missingEntries = '';\n                for (const [id, uri] of missingFolders) {\n                    missingEntries += `<li><label><input type=\"checkbox\" checked=\"checked\" name=\"deleteFolder[]\" value=\"${id}\"> ${uri}</label></li>`;\n                }\n                const translationParams = { items: 'folders' };\n                let missingItemsHeading = this._getMissingItemsHeading('folders', translationParams, session);\n                let missingItemsCopy = this._getMissingItemsCopy('folders', translationParams, session);\n                $confirmBody.append($(`\n                <h2>${missingItemsHeading}</h2>\n                <p>${missingItemsCopy}</p>\n                <ul>\n                    ${missingEntries}\n                </ul>\n            `));\n            }\n            if (missingFiles.length) {\n                let missingEntries = '';\n                for (const [id, uri] of missingFiles) {\n                    missingEntries += `<li><label><input type=\"checkbox\" checked=\"checked\" name=\"deleteAsset[]\" value=\"${id}\"> ${uri}</label></li>`;\n                }\n                const translationParams = { items: 'files' };\n                let missingItemsHeading = this._getMissingItemsHeading('files', translationParams, session);\n                let missingItemsCopy = this._getMissingItemsCopy('files', translationParams, session);\n                $confirmBody.append($(`\n                <h2>${missingItemsHeading}</h2>\n                <p>${missingItemsCopy}</p>\n                <ul>\n                    ${missingEntries}\n                </ul>\n            `));\n            }\n        }\n        const $modal = $('<form class=\"modal fitted confirmmodal\"/>').appendTo(Garnish.$bod);\n        const $body = $('<div class=\"body\"/>')\n            .appendTo($modal)\n            .html($confirmBody.html());\n        const $footer = $('<footer class=\"footer\"/>').appendTo($modal);\n        const $buttons = $('<div class=\"buttons right\"/>').appendTo($footer);\n        const modal = new Garnish.Modal($modal, {\n            hideOnEsc: false,\n            hideOnShadeClick: false,\n            onHide: () => {\n                this._currentlyReviewing = false;\n            },\n        });\n        if (haveMissingItems) {\n            let $cancelBtn = $('<button/>', {\n                type: 'button',\n                class: 'btn',\n                text: Craft.t('app', 'Keep them'),\n            })\n                .on('click', (ev) => {\n                ev.preventDefault();\n                this.stopIndexingSession(session);\n                modal.hide();\n            })\n                .appendTo($buttons);\n            $('<button/>', {\n                type: 'submit',\n                class: 'btn submit',\n                text: Craft.t('app', 'Delete them'),\n            }).appendTo($buttons);\n        }\n        else {\n            $('<button/>', {\n                type: 'submit',\n                class: 'btn submit',\n                text: Craft.t('app', 'OK'),\n            }).appendTo($buttons);\n        }\n        Craft.initUiElements($body);\n        modal.updateSizeAndPosition();\n        $modal.on('submit', (ev) => {\n            ev.preventDefault();\n            modal.hide();\n            const postData = Garnish.getPostData($body);\n            const postParams = Craft.expandPostArray(postData);\n            postParams.sessionId = session.getSessionId();\n            // Make this the next task for sure?\n            const task = {\n                sessionId: session.getSessionId(),\n                action: IndexingActions.FINISH,\n                params: postParams,\n            };\n            this.enqueueTask(task, true);\n        });\n    }\n    _getMissingItemsHeading(missingType, translationParams, session) {\n        let missingItemsHeading = Craft.t('app', 'Missing {items}', translationParams);\n        if (missingType == 'folders' && session.getListEmptyFolders()) {\n            missingItemsHeading = Craft.t('app', 'Missing or empty {items}', translationParams);\n        }\n        return missingItemsHeading;\n    }\n    _getMissingItemsCopy(missingType, translationParams, session) {\n        let missingItemsCopy = Craft.t('app', 'The following {items} could not be found. Should they be deleted from the index?', translationParams);\n        if (missingType == 'folders' && session.getListEmptyFolders()) {\n            missingItemsCopy = Craft.t('app', 'The following {items} could not be found or are empty. Should they be deleted from the index?', translationParams);\n        }\n        return missingItemsCopy;\n    }\n    startIndexing(data, cb) {\n        Craft.sendActionRequest('POST', IndexingActions.START, { data })\n            .then((response) => this.processSuccessResponse(response))\n            .catch(({ response }) => this.processFailureResponse(response))\n            .finally(() => cb());\n    }\n    performIndexingStep() {\n        if (!this._currentIndexingSession) {\n            this._updateCurrentIndexingSession();\n        }\n        if (!this._currentIndexingSession) {\n            return;\n        }\n        const session = this.indexingSessions[this._currentIndexingSession];\n        const concurrentSlots = this._maxConcurrentConnections - this._currentConnectionCount;\n        // Queue up at least enough tasks to use up all the free connections of finish the session.\n        for (let i = 0; i < Math.min(concurrentSlots, session.getEntriesRemaining()); i++) {\n            const task = {\n                sessionId: session.getSessionId(),\n                action: IndexingActions.PROCESS,\n                params: { sessionId: this._currentIndexingSession },\n            };\n            this.enqueueTask(task);\n        }\n        if (session.getProcessIfRootEmpty()) {\n            const task = {\n                sessionId: session.getSessionId(),\n                action: IndexingActions.PROCESS,\n                params: { sessionId: this._currentIndexingSession },\n            };\n            this.enqueueTask(task);\n        }\n    }\n    /**\n     * Stop and discard an indexing session.\n     *\n     * @param session\n     */\n    stopIndexingSession(session) {\n        this.pruneWaitingTasks(session.getSessionId());\n        const task = {\n            sessionId: session.getSessionId(),\n            action: IndexingActions.STOP,\n            params: { sessionId: session.getSessionId() },\n        };\n        this.enqueueTask(task, true);\n    }\n    /**\n     * Pune the waiting task list by removing all tasks for a session id.\n     *\n     * @param sessionId\n     */\n    pruneWaitingTasks(sessionId) {\n        const newTaskList = [];\n        let modified = false;\n        this._prunedSessionIds.push(sessionId);\n        for (const task of this._tasksWaiting) {\n            if (task.sessionId !== sessionId) {\n                newTaskList.push(task);\n            }\n            else {\n                modified = true;\n            }\n        }\n        if (modified) {\n            this._tasksWaiting = newTaskList;\n        }\n    }\n    enqueueTask(task, prioritize = false) {\n        if (prioritize) {\n            this._priorityTasks.push(task);\n        }\n        else {\n            this._tasksWaiting.push(task);\n        }\n        this.runTasks();\n    }\n    runTasks() {\n        if (this._tasksWaiting.length + this._priorityTasks.length === 0 ||\n            this._currentConnectionCount >= this._maxConcurrentConnections) {\n            return;\n        }\n        while (this._tasksWaiting.length + this._priorityTasks.length !== 0 &&\n            this._currentConnectionCount < this._maxConcurrentConnections) {\n            this._currentConnectionCount++;\n            const task = this._priorityTasks.length > 0\n                ? this._priorityTasks.shift()\n                : this._tasksWaiting.shift();\n            Craft.sendActionRequest('POST', task.action, { data: task.params })\n                .then((response) => this.processSuccessResponse(response))\n                .catch(({ response }) => this.processFailureResponse(response))\n                .finally(() => {\n                if (task.callback) {\n                    task.callback();\n                }\n            });\n        }\n    }\n    _updateCurrentIndexingSession() {\n        for (const session of Object.values(this.indexingSessions)) {\n            if (session.getSessionStatus() !== SessionStatus.ACTIONREQUIRED) {\n                this._currentIndexingSession = session.getSessionId();\n                return;\n            }\n        }\n    }\n    /**\n     * Create a session from the data model.\n     *\n     * @param sessionData\n     * @private\n     */\n    createSessionFromModel(sessionData) {\n        return new AssetIndexingSession(sessionData, this);\n    }\n}\nclass AssetIndexingSession {\n    constructor(model, indexer) {\n        this.indexingSessionData = model;\n        this.indexer = indexer;\n    }\n    /**\n     * Get the session id\n     */\n    getSessionId() {\n        return this.indexingSessionData.id;\n    }\n    getProcessIfRootEmpty() {\n        return this.indexingSessionData.processIfRootEmpty;\n    }\n    getListEmptyFolders() {\n        return this.indexingSessionData.listEmptyFolders;\n    }\n    /**\n     * Get the remaining entry count for this sessions.\n     */\n    getEntriesRemaining() {\n        return (this.indexingSessionData.totalEntries -\n            this.indexingSessionData.processedEntries);\n    }\n    /**\n     * Get the session status.\n     */\n    getSessionStatus() {\n        if (this.indexingSessionData.actionRequired) {\n            return SessionStatus.ACTIONREQUIRED;\n        }\n        if (this.indexer.currentIndexingSession === this.indexingSessionData.id) {\n            return SessionStatus.ACTIVE;\n        }\n        return SessionStatus.WAITING;\n    }\n    /**\n     * Create row html as a JQuery object based on an indexing sessions\n     * @param session\n     * @private\n     */\n    getIndexingSessionRowHtml() {\n        const $tr = $('<tr class=\"indexingSession\" data-session-id=\"' +\n            this.getSessionId() +\n            '\">');\n        const $td = $('<td/>').appendTo($tr);\n        const $ul = $('<ul/>').appendTo($td);\n        for (const volume of Object.values(this.indexingSessionData.indexedVolumes)) {\n            $('<li/>', {\n                text: volume,\n            }).appendTo($ul);\n        }\n        $tr.append('<td>' + this.indexingSessionData.dateCreated + '</td>');\n        const $progressCell = $('<td class=\"progress\"><div class=\"progressContainer\"></div></td>').css('position', 'relative');\n        const progressBar = new Craft.ProgressBar($progressCell.find('.progressContainer'), false);\n        progressBar.setItemCount(this.indexingSessionData.totalEntries);\n        progressBar.setProcessedItemCount(this.indexingSessionData.processedEntries);\n        progressBar.updateProgressBar();\n        progressBar.showProgressBar();\n        $progressCell.data('progressBar', progressBar);\n        $progressCell\n            .find('.progressContainer')\n            .append(`<div class=\"progressInfo\">${this.indexingSessionData.processedEntries} / ${this.indexingSessionData.totalEntries}</div>`);\n        $tr.append($progressCell);\n        $tr.append('<td>' + this.getSessionStatusMessage() + '</td>');\n        const $actions = this.getActionButtons();\n        $('<td></td>').append($actions).appendTo($tr);\n        return $tr;\n    }\n    /**\n     * Get action buttons for an indexing session\n     * @param session\n     * @private\n     */\n    getActionButtons() {\n        const $buttons = $('<div class=\"buttons\"></div>');\n        if (this.getSessionStatus() == SessionStatus.ACTIONREQUIRED) {\n            const reviewMessage = Craft.t('app', 'Review');\n            $buttons.append($('<button />', {\n                type: 'button',\n                class: 'btn submit',\n                title: reviewMessage,\n                'aria-label': reviewMessage,\n            })\n                .text(reviewMessage)\n                .on('click', (ev) => {\n                const $container = $(ev.target).parent();\n                if ($container.hasClass('disabled')) {\n                    return;\n                }\n                $container.addClass('disabled');\n                this.indexer.getReviewData(this);\n            }));\n        }\n        const discardMessage = Craft.t('app', 'Discard');\n        $buttons.append($('<button />', {\n            type: 'button',\n            class: 'btn submit',\n            title: discardMessage,\n            'aria-label': discardMessage,\n        })\n            .text(discardMessage)\n            .on('click', (ev) => {\n            if ($buttons.hasClass('disabled')) {\n                return;\n            }\n            $buttons.addClass('disabled');\n            this.indexer.stopIndexingSession(this);\n        }));\n        return $buttons;\n    }\n    /**\n     * Get the session status verbose message\n     *\n     * @param status\n     */\n    getSessionStatusMessage() {\n        switch (this.getSessionStatus()) {\n            case SessionStatus.ACTIONREQUIRED:\n                return Craft.t('app', 'Waiting for review');\n                break;\n            case SessionStatus.ACTIVE:\n                return Craft.t('app', 'Active');\n                break;\n            case SessionStatus.WAITING:\n                return Craft.t('app', 'Waiting');\n                break;\n        }\n    }\n    /**\n     * Return a list of missing entries for this session\n     */\n    getMissingEntries() {\n        return this.indexingSessionData.missingEntries;\n    }\n    /**\n     * Return a list of skipped entries for this session\n     */\n    getSkippedEntries() {\n        return this.indexingSessionData.skippedEntries;\n    }\n}\n"],"names":["__webpack_require__","exports","definition","key","o","Object","defineProperty","enumerable","get","obj","prop","prototype","hasOwnProperty","call","Symbol","toStringTag","value","SessionStatus","IndexingActions","AssetIndexer","constructor","$indexingSessionTable","sessions","maxConcurrentConnections","this","_currentIndexingSession","_currentConnectionCount","_tasksWaiting","_priorityTasks","_prunedSessionIds","_currentlyReviewing","indexingSessions","_maxConcurrentConnections","reviewSessionId","sessionModel","session","createSessionFromModel","getSessionStatus","ACTIONREQUIRED","getSessionId","updateIndexingSessionData","performIndexingStep","currentIndexingSession","indexingSession","renderIndexingSessionRow","$row","undefined","includes","find","remove","length","addClass","getIndexingSessionRowHtml","$existing","replaceWith","append","removeClass","discardIndexingSession","sessionId","runTasks","processFailureResponse","response","responseData","data","_updateCurrentIndexingSession","Craft","cp","displayError","message","stop","processSuccessResponse","skipDialog","reviewSession","getReviewData","task","action","OVERVIEW","params","callback","enqueueTask","pruneWaitingTasks","$confirmBody","$","missingEntries","getMissingEntries","missingFiles","files","entries","missingFolders","folders","skippedFiles","getSkippedEntries","skippedFilesList","skippedFile","t","haveMissingItems","id","uri","translationParams","items","missingItemsHeading","_getMissingItemsHeading","missingItemsCopy","_getMissingItemsCopy","$modal","appendTo","Garnish","$bod","$body","html","$footer","$buttons","modal","Modal","hideOnEsc","hideOnShadeClick","onHide","type","class","text","on","ev","preventDefault","stopIndexingSession","hide","initUiElements","updateSizeAndPosition","postData","getPostData","postParams","expandPostArray","FINISH","missingType","getListEmptyFolders","startIndexing","cb","sendActionRequest","START","then","catch","finally","concurrentSlots","i","Math","min","getEntriesRemaining","PROCESS","getProcessIfRootEmpty","STOP","newTaskList","modified","push","prioritize","shift","values","sessionData","AssetIndexingSession","model","indexer","indexingSessionData","processIfRootEmpty","listEmptyFolders","totalEntries","processedEntries","actionRequired","ACTIVE","WAITING","$tr","$td","$ul","volume","indexedVolumes","dateCreated","$progressCell","css","progressBar","ProgressBar","setItemCount","setProcessedItemCount","updateProgressBar","showProgressBar","getSessionStatusMessage","$actions","getActionButtons","reviewMessage","title","$container","target","parent","hasClass","discardMessage","skippedEntries"],"sourceRoot":""}
        skippedFilesList += `<li>${skippedFile}</li>`;
          missingEntries += `<li><label><input type="checkbox" checked="checked" name="deleteFolder[]" value="${id}"> ${uri}</label></li>`;
          missingEntries += `<li><label><input type="checkbox" checked="checked" name="deleteAsset[]" value="${id}"> ${uri}</label></li>`;
Fixed code: - Fixed XSS vulnerabilities.
  escapeHtml(str: string);
!function(){var s={d:function(e,t){for(var n in t)s.o(t,n)&&!s.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},o:function(s,e){return Object.prototype.hasOwnProperty.call(s,e)},r:function(s){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(s,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(s,"__esModule",{value:!0})}},e={};!function(){"use strict";var t,n;s.r(e),s.d(e,{AssetIndexer:function(){return i}}),function(s){s[s.ACTIONREQUIRED=0]="ACTIONREQUIRED",s[s.ACTIVE=1]="ACTIVE",s[s.WAITING=2]="WAITING"}(t||(t={})),function(s){s.START="asset-indexes/start-indexing",s.STOP="asset-indexes/stop-indexing-session",s.PROCESS="asset-indexes/process-indexing-session",s.OVERVIEW="asset-indexes/indexing-session-overview",s.FINISH="asset-indexes/finish-indexing-session"}(n||(n={}));class i{constructor(s,e,n=3){this._currentIndexingSession=null,this._currentConnectionCount=0,this._tasksWaiting=[],this._priorityTasks=[],this._prunedSessionIds=[],this._currentlyReviewing=!1,this.indexingSessions={},this._maxConcurrentConnections=n,this.$indexingSessionTable=s,this.indexingSessions={};let i=0;for(const s of e){let e=this.createSessionFromModel(s);e.getSessionStatus()!==t.ACTIONREQUIRED||i||(i=e.getSessionId()),i||null!=this._currentIndexingSession||e.getSessionStatus()===t.ACTIONREQUIRED||(this._currentIndexingSession=e.getSessionId()),this.updateIndexingSessionData(e)}this._currentIndexingSession&&this.performIndexingStep()}get currentIndexingSession(){return this._currentIndexingSession}updateIndexingSessionData(s){this.indexingSessions[s.getSessionId()]=s,this.renderIndexingSessionRow(s)}renderIndexingSessionRow(s){let e;if(void 0===s)return;if(!this.indexingSessions[s.getSessionId()]||this._prunedSessionIds.includes(s.getSessionId()))return this.$indexingSessionTable.find('tr[data-session-id="'+s.getSessionId()+'"]').remove(),void(0==this.$indexingSessionTable.find("tbody tr").length&&this.$indexingSessionTable.addClass("hidden"));e=s.getIndexingSessionRowHtml();const t=this.$indexingSessionTable.find('tr[data-session-id="'+s.getSessionId()+'"]');t.length>0?t.replaceWith(e):this.$indexingSessionTable.find("tbody").append(e),this.$indexingSessionTable.removeClass("hidden")}discardIndexingSession(s){const e=this.indexingSessions[s];delete this.indexingSessions[s],this._currentIndexingSession===s&&(this._currentIndexingSession=null),this.renderIndexingSessionRow(e),this.runTasks()}processFailureResponse(s){const e=s.data;this._currentConnectionCount--,this._updateCurrentIndexingSession(),Craft.cp.displayError(e.message),e.stop&&this.discardIndexingSession(e.stop),this.runTasks()}processSuccessResponse(s){const e=s.data;if(this._currentConnectionCount--,e.session){const s=this.createSessionFromModel(e.session);this.indexingSessions[s.getSessionId()]=s,this.renderIndexingSessionRow(s),this._updateCurrentIndexingSession(),s.getSessionStatus()!==t.ACTIONREQUIRED||e.skipDialog?this._prunedSessionIds.includes(this._currentIndexingSession)?this.runTasks():this.performIndexingStep():this._prunedSessionIds.includes(this._currentIndexingSession)?this.runTasks():this.reviewSession(s)}this._updateCurrentIndexingSession(),e.stop&&this.discardIndexingSession(e.stop)}getReviewData(s){const e={sessionId:s.getSessionId(),action:n.OVERVIEW,params:{sessionId:s.getSessionId()},callback:()=>{this.renderIndexingSessionRow(s)}};this.enqueueTask(e)}reviewSession(s){if(this._currentlyReviewing)return;this._currentlyReviewing=!0,this.pruneWaitingTasks(s.getSessionId());let e=$("<div></div>");const t=s.getMissingEntries(),i=t.files?Object.entries(t.files):[],o=t.folders?Object.entries(t.folders):[],r=s.getSkippedEntries();if(r.length){let s="";for(const e of r)s+=`<li>${Craft.escapeHtml(e)}</li>`;e.append(`\n                <h2>${Craft.t("app","Skipped files")}</h2>\n                <p>${Craft.t("app","The following items were not indexed.")}</p>\n                <ul>\n                    ${s}\n                </ul>\n            `)}const a=i.length||o.length;if(a){if(o.length){let t="";for(const[s,e]of o)t+=`<li><label><input type="checkbox" checked="checked" name="deleteFolder[]" value="${s}"> ${Craft.escapeHtml(e)}</label></li>`;const n={items:"folders"};let i=this._getMissingItemsHeading("folders",n,s),r=this._getMissingItemsCopy("folders",n,s);e.append($(`\n                <h2>${i}</h2>\n                <p>${r}</p>\n                <ul>\n                    ${t}\n                </ul>\n            `))}if(i.length){let t="";for(const[s,e]of i)t+=`<li><label><input type="checkbox" checked="checked" name="deleteAsset[]" value="${s}"> ${Craft.escapeHtml(e)}</label></li>`;const n={items:"files"};let o=this._getMissingItemsHeading("files",n,s),r=this._getMissingItemsCopy("files",n,s);e.append($(`\n                <h2>${o}</h2>\n                <p>${r}</p>\n                <ul>\n                    ${t}\n                </ul>\n            `))}}const d=$('<form class="modal fitted confirmmodal"/>').appendTo(Garnish.$bod),g=$('<div class="body"/>').appendTo(d).html(e.html()),p=$('<footer class="footer"/>').appendTo(d),c=$('<div class="buttons right"/>').appendTo(p),h=new Garnish.Modal(d,{hideOnEsc:!1,hideOnShadeClick:!1,onHide:()=>{this._currentlyReviewing=!1}});a?($("<button/>",{type:"button",class:"btn",text:Craft.t("app","Keep them")}).on("click",(e=>{e.preventDefault(),this.stopIndexingSession(s),h.hide()})).appendTo(c),$("<button/>",{type:"submit",class:"btn submit",text:Craft.t("app","Delete them")}).appendTo(c)):$("<button/>",{type:"submit",class:"btn submit",text:Craft.t("app","OK")}).appendTo(c),Craft.initUiElements(g),h.updateSizeAndPosition(),d.on("submit",(e=>{e.preventDefault(),h.hide();const t=Garnish.getPostData(g),i=Craft.expandPostArray(t);i.sessionId=s.getSessionId();const o={sessionId:s.getSessionId(),action:n.FINISH,params:i};this.enqueueTask(o,!0)}))}_getMissingItemsHeading(s,e,t){let n=Craft.t("app","Missing {items}",e);return"folders"==s&&t.getListEmptyFolders()&&(n=Craft.t("app","Missing or empty {items}",e)),n}_getMissingItemsCopy(s,e,t){let n=Craft.t("app","The following {items} could not be found. Should they be deleted from the index?",e);return"folders"==s&&t.getListEmptyFolders()&&(n=Craft.t("app","The following {items} could not be found or are empty. Should they be deleted from the index?",e)),n}startIndexing(s,e){Craft.sendActionRequest("POST",n.START,{data:s}).then((s=>this.processSuccessResponse(s))).catch((({response:s})=>this.processFailureResponse(s))).finally((()=>e()))}performIndexingStep(){if(this._currentIndexingSession||this._updateCurrentIndexingSession(),!this._currentIndexingSession)return;const s=this.indexingSessions[this._currentIndexingSession],e=this._maxConcurrentConnections-this._currentConnectionCount;for(let t=0;t<Math.min(e,s.getEntriesRemaining());t++){const e={sessionId:s.getSessionId(),action:n.PROCESS,params:{sessionId:this._currentIndexingSession}};this.enqueueTask(e)}if(s.getProcessIfRootEmpty()){const e={sessionId:s.getSessionId(),action:n.PROCESS,params:{sessionId:this._currentIndexingSession}};this.enqueueTask(e)}}stopIndexingSession(s){this.pruneWaitingTasks(s.getSessionId());const e={sessionId:s.getSessionId(),action:n.STOP,params:{sessionId:s.getSessionId()}};this.enqueueTask(e,!0)}pruneWaitingTasks(s){const e=[];let t=!1;this._prunedSessionIds.push(s);for(const n of this._tasksWaiting)n.sessionId!==s?e.push(n):t=!0;t&&(this._tasksWaiting=e)}enqueueTask(s,e=!1){e?this._priorityTasks.push(s):this._tasksWaiting.push(s),this.runTasks()}runTasks(){if(!(this._tasksWaiting.length+this._priorityTasks.length===0||this._currentConnectionCount>=this._maxConcurrentConnections))for(;this._tasksWaiting.length+this._priorityTasks.length!==0&&this._currentConnectionCount<this._maxConcurrentConnections;){this._currentConnectionCount++;const s=this._priorityTasks.length>0?this._priorityTasks.shift():this._tasksWaiting.shift();Craft.sendActionRequest("POST",s.action,{data:s.params}).then((s=>this.processSuccessResponse(s))).catch((({response:s})=>this.processFailureResponse(s))).finally((()=>{s.callback&&s.callback()}))}}_updateCurrentIndexingSession(){for(const s of Object.values(this.indexingSessions))if(s.getSessionStatus()!==t.ACTIONREQUIRED)return void(this._currentIndexingSession=s.getSessionId())}createSessionFromModel(s){return new o(s,this)}}class o{constructor(s,e){this.indexingSessionData=s,this.indexer=e}getSessionId(){return this.indexingSessionData.id}getProcessIfRootEmpty(){return this.indexingSessionData.processIfRootEmpty}getListEmptyFolders(){return this.indexingSessionData.listEmptyFolders}getEntriesRemaining(){return this.indexingSessionData.totalEntries-this.indexingSessionData.processedEntries}getSessionStatus(){return this.indexingSessionData.actionRequired?t.ACTIONREQUIRED:this.indexer.currentIndexingSession===this.indexingSessionData.id?t.ACTIVE:t.WAITING}getIndexingSessionRowHtml(){const s=$('<tr class="indexingSession" data-session-id="'+this.getSessionId()+'">'),e=$("<td/>").appendTo(s),t=$("<ul/>").appendTo(e);for(const s of Object.values(this.indexingSessionData.indexedVolumes))$("<li/>",{text:s}).appendTo(t);s.append("<td>"+this.indexingSessionData.dateCreated+"</td>");const n=$('<td class="progress"><div class="progressContainer"></div></td>').css("position","relative"),i=new Craft.ProgressBar(n.find(".progressContainer"),!1);i.setItemCount(this.indexingSessionData.totalEntries),i.setProcessedItemCount(this.indexingSessionData.processedEntries),i.updateProgressBar(),i.showProgressBar(),n.data("progressBar",i),n.find(".progressContainer").append(`<div class="progressInfo">${this.indexingSessionData.processedEntries} / ${this.indexingSessionData.totalEntries}</div>`),s.append(n),s.append("<td>"+this.getSessionStatusMessage()+"</td>");const o=this.getActionButtons();return $("<td></td>").append(o).appendTo(s),s}getActionButtons(){const s=$('<div class="buttons"></div>');if(this.getSessionStatus()==t.ACTIONREQUIRED){const e=Craft.t("app","Review");s.append($("<button />",{type:"button",class:"btn submit",title:e,"aria-label":e}).text(e).on("click",(s=>{const e=$(s.target).parent();e.hasClass("disabled")||(e.addClass("disabled"),this.indexer.getReviewData(this))})))}const e=Craft.t("app","Discard");return s.append($("<button />",{type:"button",class:"btn submit",title:e,"aria-label":e}).text(e).on("click",(e=>{s.hasClass("disabled")||(s.addClass("disabled"),this.indexer.stopIndexingSession(this))}))),s}getSessionStatusMessage(){switch(this.getSessionStatus()){case t.ACTIONREQUIRED:return Craft.t("app","Waiting for review");case t.ACTIVE:return Craft.t("app","Active");case t.WAITING:return Craft.t("app","Waiting")}}getMissingEntries(){return this.indexingSessionData.missingEntries}getSkippedEntries(){return this.indexingSessionData.skippedEntries}}}();var t=Craft="undefined"==typeof Craft?{}:Craft;for(var n in e)t[n]=e[n];e.__esModule&&Object.defineProperty(t,"__esModule",{value:!0})}();
{"version":3,"file":"AssetIndexer.js","mappings":"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","sources":["webpack://Craft/webpack/bootstrap","webpack://Craft/webpack/runtime/define property getters","webpack://Craft/webpack/runtime/hasOwnProperty shorthand","webpack://Craft/webpack/runtime/make namespace object","webpack://Craft/./AssetIndexer.ts"],"sourcesContent":["// The require scope\nvar __webpack_require__ = {};\n\n","// define getter functions for harmony exports\n__webpack_require__.d = function(exports, definition) {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }","// define __esModule on exports\n__webpack_require__.r = function(exports) {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","var SessionStatus;\n(function (SessionStatus) {\n    SessionStatus[SessionStatus[\"ACTIONREQUIRED\"] = 0] = \"ACTIONREQUIRED\";\n    SessionStatus[SessionStatus[\"ACTIVE\"] = 1] = \"ACTIVE\";\n    SessionStatus[SessionStatus[\"WAITING\"] = 2] = \"WAITING\";\n})(SessionStatus || (SessionStatus = {}));\nvar IndexingActions;\n(function (IndexingActions) {\n    IndexingActions[\"START\"] = \"asset-indexes/start-indexing\";\n    IndexingActions[\"STOP\"] = \"asset-indexes/stop-indexing-session\";\n    IndexingActions[\"PROCESS\"] = \"asset-indexes/process-indexing-session\";\n    IndexingActions[\"OVERVIEW\"] = \"asset-indexes/indexing-session-overview\";\n    IndexingActions[\"FINISH\"] = \"asset-indexes/finish-indexing-session\";\n})(IndexingActions || (IndexingActions = {}));\n/**\n * Actual classes start here\n */\n// Asset Indexer\n// =====================================================================================\nexport class AssetIndexer {\n    /**\n     * @param $element The indexing session table\n     * @param sessions Existing indexing sessions\n     */\n    constructor($indexingSessionTable, sessions, maxConcurrentConnections = 3) {\n        this._currentIndexingSession = null;\n        this._currentConnectionCount = 0;\n        this._tasksWaiting = [];\n        this._priorityTasks = [];\n        this._prunedSessionIds = [];\n        this._currentlyReviewing = false;\n        this.indexingSessions = {};\n        this._maxConcurrentConnections = maxConcurrentConnections;\n        this.$indexingSessionTable = $indexingSessionTable;\n        this.indexingSessions = {};\n        let reviewSessionId = 0;\n        for (const sessionModel of sessions) {\n            let session = this.createSessionFromModel(sessionModel);\n            if (session.getSessionStatus() === SessionStatus.ACTIONREQUIRED &&\n                !reviewSessionId) {\n                reviewSessionId = session.getSessionId();\n            }\n            if (!reviewSessionId &&\n                this._currentIndexingSession == null &&\n                session.getSessionStatus() !== SessionStatus.ACTIONREQUIRED) {\n                this._currentIndexingSession = session.getSessionId();\n            }\n            this.updateIndexingSessionData(session);\n        }\n        if (this._currentIndexingSession) {\n            this.performIndexingStep();\n        }\n    }\n    get currentIndexingSession() {\n        return this._currentIndexingSession;\n    }\n    /**\n     * Update indexing session store\n     * @param session\n     */\n    updateIndexingSessionData(indexingSession) {\n        this.indexingSessions[indexingSession.getSessionId()] = indexingSession;\n        this.renderIndexingSessionRow(indexingSession);\n    }\n    /**\n     * Return a rendered indexing session row based on its id\n     * @param sessionId\n     */\n    renderIndexingSessionRow(session) {\n        let $row;\n        if (session === undefined) {\n            return;\n        }\n        if (!this.indexingSessions[session.getSessionId()] ||\n            this._prunedSessionIds.includes(session.getSessionId())) {\n            this.$indexingSessionTable\n                .find('tr[data-session-id=\"' + session.getSessionId() + '\"]')\n                .remove();\n            if (this.$indexingSessionTable.find('tbody tr').length == 0) {\n                this.$indexingSessionTable.addClass('hidden');\n            }\n            return;\n        }\n        $row = session.getIndexingSessionRowHtml();\n        const $existing = this.$indexingSessionTable.find('tr[data-session-id=\"' + session.getSessionId() + '\"]');\n        if ($existing.length > 0) {\n            $existing.replaceWith($row);\n        }\n        else {\n            this.$indexingSessionTable.find('tbody').append($row);\n        }\n        this.$indexingSessionTable.removeClass('hidden');\n    }\n    /**\n     * Remove an indexing session\n     * @param sessionId\n     * @protected\n     */\n    discardIndexingSession(sessionId) {\n        const session = this.indexingSessions[sessionId];\n        delete this.indexingSessions[sessionId];\n        if (this._currentIndexingSession === sessionId) {\n            this._currentIndexingSession = null;\n        }\n        this.renderIndexingSessionRow(session);\n        this.runTasks();\n    }\n    /**\n     * Process a failed indexing response.\n     *\n     * @param response\n     */\n    processFailureResponse(response) {\n        const responseData = response.data;\n        this._currentConnectionCount--;\n        this._updateCurrentIndexingSession();\n        Craft.cp.displayError(responseData.message);\n        if (responseData.stop) {\n            this.discardIndexingSession(responseData.stop);\n        }\n        // A mere error shall not stop the party.\n        this.runTasks();\n        return;\n    }\n    /**\n     * Process a successful indexing response.\n     *\n     * @param response\n     */\n    processSuccessResponse(response) {\n        const responseData = response.data;\n        this._currentConnectionCount--;\n        if (responseData.session) {\n            const session = this.createSessionFromModel(responseData.session);\n            this.indexingSessions[session.getSessionId()] = session;\n            this.renderIndexingSessionRow(session);\n            this._updateCurrentIndexingSession();\n            if (session.getSessionStatus() === SessionStatus.ACTIONREQUIRED &&\n                !responseData.skipDialog) {\n                if (!this._prunedSessionIds.includes(this._currentIndexingSession)) {\n                    this.reviewSession(session);\n                }\n                else {\n                    this.runTasks();\n                }\n            }\n            else if (!this._prunedSessionIds.includes(this._currentIndexingSession)) {\n                this.performIndexingStep();\n            }\n            else {\n                this.runTasks();\n            }\n        }\n        this._updateCurrentIndexingSession();\n        if (responseData.stop) {\n            this.discardIndexingSession(responseData.stop);\n        }\n    }\n    getReviewData(session) {\n        const task = {\n            sessionId: session.getSessionId(),\n            action: IndexingActions.OVERVIEW,\n            params: { sessionId: session.getSessionId() },\n            callback: () => {\n                this.renderIndexingSessionRow(session);\n            },\n        };\n        this.enqueueTask(task);\n    }\n    reviewSession(session) {\n        if (this._currentlyReviewing) {\n            return;\n        }\n        this._currentlyReviewing = true;\n        this.pruneWaitingTasks(session.getSessionId());\n        let $confirmBody = $('<div></div>');\n        const missingEntries = session.getMissingEntries();\n        const missingFiles = missingEntries.files\n            ? Object.entries(missingEntries.files)\n            : [];\n        const missingFolders = missingEntries.folders\n            ? Object.entries(missingEntries.folders)\n            : [];\n        const skippedFiles = session.getSkippedEntries();\n        if (skippedFiles.length) {\n            let skippedFilesList = '';\n            for (const skippedFile of skippedFiles) {\n                skippedFilesList += `<li>${Craft.escapeHtml(skippedFile)}</li>`;\n            }\n            $confirmBody.append(`\n                <h2>${Craft.t('app', 'Skipped files')}</h2>\n                <p>${Craft.t('app', 'The following items were not indexed.')}</p>\n                <ul>\n                    ${skippedFilesList}\n                </ul>\n            `);\n        }\n        const haveMissingItems = missingFiles.length || missingFolders.length;\n        if (haveMissingItems) {\n            if (missingFolders.length) {\n                let missingEntries = '';\n                for (const [id, uri] of missingFolders) {\n                    missingEntries += `<li><label><input type=\"checkbox\" checked=\"checked\" name=\"deleteFolder[]\" value=\"${id}\"> ${Craft.escapeHtml(uri)}</label></li>`;\n                }\n                const translationParams = { items: 'folders' };\n                let missingItemsHeading = this._getMissingItemsHeading('folders', translationParams, session);\n                let missingItemsCopy = this._getMissingItemsCopy('folders', translationParams, session);\n                $confirmBody.append($(`\n                <h2>${missingItemsHeading}</h2>\n                <p>${missingItemsCopy}</p>\n                <ul>\n                    ${missingEntries}\n                </ul>\n            `));\n            }\n            if (missingFiles.length) {\n                let missingEntries = '';\n                for (const [id, uri] of missingFiles) {\n                    missingEntries += `<li><label><input type=\"checkbox\" checked=\"checked\" name=\"deleteAsset[]\" value=\"${id}\"> ${Craft.escapeHtml(uri)}</label></li>`;\n                }\n                const translationParams = { items: 'files' };\n                let missingItemsHeading = this._getMissingItemsHeading('files', translationParams, session);\n                let missingItemsCopy = this._getMissingItemsCopy('files', translationParams, session);\n                $confirmBody.append($(`\n                <h2>${missingItemsHeading}</h2>\n                <p>${missingItemsCopy}</p>\n                <ul>\n                    ${missingEntries}\n                </ul>\n            `));\n            }\n        }\n        const $modal = $('<form class=\"modal fitted confirmmodal\"/>').appendTo(Garnish.$bod);\n        const $body = $('<div class=\"body\"/>')\n            .appendTo($modal)\n            .html($confirmBody.html());\n        const $footer = $('<footer class=\"footer\"/>').appendTo($modal);\n        const $buttons = $('<div class=\"buttons right\"/>').appendTo($footer);\n        const modal = new Garnish.Modal($modal, {\n            hideOnEsc: false,\n            hideOnShadeClick: false,\n            onHide: () => {\n                this._currentlyReviewing = false;\n            },\n        });\n        if (haveMissingItems) {\n            let $cancelBtn = $('<button/>', {\n                type: 'button',\n                class: 'btn',\n                text: Craft.t('app', 'Keep them'),\n            })\n                .on('click', (ev) => {\n                ev.preventDefault();\n                this.stopIndexingSession(session);\n                modal.hide();\n            })\n                .appendTo($buttons);\n            $('<button/>', {\n                type: 'submit',\n                class: 'btn submit',\n                text: Craft.t('app', 'Delete them'),\n            }).appendTo($buttons);\n        }\n        else {\n            $('<button/>', {\n                type: 'submit',\n                class: 'btn submit',\n                text: Craft.t('app', 'OK'),\n            }).appendTo($buttons);\n        }\n        Craft.initUiElements($body);\n        modal.updateSizeAndPosition();\n        $modal.on('submit', (ev) => {\n            ev.preventDefault();\n            modal.hide();\n            const postData = Garnish.getPostData($body);\n            const postParams = Craft.expandPostArray(postData);\n            postParams.sessionId = session.getSessionId();\n            // Make this the next task for sure?\n            const task = {\n                sessionId: session.getSessionId(),\n                action: IndexingActions.FINISH,\n                params: postParams,\n            };\n            this.enqueueTask(task, true);\n        });\n    }\n    _getMissingItemsHeading(missingType, translationParams, session) {\n        let missingItemsHeading = Craft.t('app', 'Missing {items}', translationParams);\n        if (missingType == 'folders' && session.getListEmptyFolders()) {\n            missingItemsHeading = Craft.t('app', 'Missing or empty {items}', translationParams);\n        }\n        return missingItemsHeading;\n    }\n    _getMissingItemsCopy(missingType, translationParams, session) {\n        let missingItemsCopy = Craft.t('app', 'The following {items} could not be found. Should they be deleted from the index?', translationParams);\n        if (missingType == 'folders' && session.getListEmptyFolders()) {\n            missingItemsCopy = Craft.t('app', 'The following {items} could not be found or are empty. Should they be deleted from the index?', translationParams);\n        }\n        return missingItemsCopy;\n    }\n    startIndexing(data, cb) {\n        Craft.sendActionRequest('POST', IndexingActions.START, { data })\n            .then((response) => this.processSuccessResponse(response))\n            .catch(({ response }) => this.processFailureResponse(response))\n            .finally(() => cb());\n    }\n    performIndexingStep() {\n        if (!this._currentIndexingSession) {\n            this._updateCurrentIndexingSession();\n        }\n        if (!this._currentIndexingSession) {\n            return;\n        }\n        const session = this.indexingSessions[this._currentIndexingSession];\n        const concurrentSlots = this._maxConcurrentConnections - this._currentConnectionCount;\n        // Queue up at least enough tasks to use up all the free connections of finish the session.\n        for (let i = 0; i < Math.min(concurrentSlots, session.getEntriesRemaining()); i++) {\n            const task = {\n                sessionId: session.getSessionId(),\n                action: IndexingActions.PROCESS,\n                params: { sessionId: this._currentIndexingSession },\n            };\n            this.enqueueTask(task);\n        }\n        if (session.getProcessIfRootEmpty()) {\n            const task = {\n                sessionId: session.getSessionId(),\n                action: IndexingActions.PROCESS,\n                params: { sessionId: this._currentIndexingSession },\n            };\n            this.enqueueTask(task);\n        }\n    }\n    /**\n     * Stop and discard an indexing session.\n     *\n     * @param session\n     */\n    stopIndexingSession(session) {\n        this.pruneWaitingTasks(session.getSessionId());\n        const task = {\n            sessionId: session.getSessionId(),\n            action: IndexingActions.STOP,\n            params: { sessionId: session.getSessionId() },\n        };\n        this.enqueueTask(task, true);\n    }\n    /**\n     * Pune the waiting task list by removing all tasks for a session id.\n     *\n     * @param sessionId\n     */\n    pruneWaitingTasks(sessionId) {\n        const newTaskList = [];\n        let modified = false;\n        this._prunedSessionIds.push(sessionId);\n        for (const task of this._tasksWaiting) {\n            if (task.sessionId !== sessionId) {\n                newTaskList.push(task);\n            }\n            else {\n                modified = true;\n            }\n        }\n        if (modified) {\n            this._tasksWaiting = newTaskList;\n        }\n    }\n    enqueueTask(task, prioritize = false) {\n        if (prioritize) {\n            this._priorityTasks.push(task);\n        }\n        else {\n            this._tasksWaiting.push(task);\n        }\n        this.runTasks();\n    }\n    runTasks() {\n        if (this._tasksWaiting.length + this._priorityTasks.length === 0 ||\n            this._currentConnectionCount >= this._maxConcurrentConnections) {\n            return;\n        }\n        while (this._tasksWaiting.length + this._priorityTasks.length !== 0 &&\n            this._currentConnectionCount < this._maxConcurrentConnections) {\n            this._currentConnectionCount++;\n            const task = this._priorityTasks.length > 0\n                ? this._priorityTasks.shift()\n                : this._tasksWaiting.shift();\n            Craft.sendActionRequest('POST', task.action, { data: task.params })\n                .then((response) => this.processSuccessResponse(response))\n                .catch(({ response }) => this.processFailureResponse(response))\n                .finally(() => {\n                if (task.callback) {\n                    task.callback();\n                }\n            });\n        }\n    }\n    _updateCurrentIndexingSession() {\n        for (const session of Object.values(this.indexingSessions)) {\n            if (session.getSessionStatus() !== SessionStatus.ACTIONREQUIRED) {\n                this._currentIndexingSession = session.getSessionId();\n                return;\n            }\n        }\n    }\n    /**\n     * Create a session from the data model.\n     *\n     * @param sessionData\n     * @private\n     */\n    createSessionFromModel(sessionData) {\n        return new AssetIndexingSession(sessionData, this);\n    }\n}\nclass AssetIndexingSession {\n    constructor(model, indexer) {\n        this.indexingSessionData = model;\n        this.indexer = indexer;\n    }\n    /**\n     * Get the session id\n     */\n    getSessionId() {\n        return this.indexingSessionData.id;\n    }\n    getProcessIfRootEmpty() {\n        return this.indexingSessionData.processIfRootEmpty;\n    }\n    getListEmptyFolders() {\n        return this.indexingSessionData.listEmptyFolders;\n    }\n    /**\n     * Get the remaining entry count for this sessions.\n     */\n    getEntriesRemaining() {\n        return (this.indexingSessionData.totalEntries -\n            this.indexingSessionData.processedEntries);\n    }\n    /**\n     * Get the session status.\n     */\n    getSessionStatus() {\n        if (this.indexingSessionData.actionRequired) {\n            return SessionStatus.ACTIONREQUIRED;\n        }\n        if (this.indexer.currentIndexingSession === this.indexingSessionData.id) {\n            return SessionStatus.ACTIVE;\n        }\n        return SessionStatus.WAITING;\n    }\n    /**\n     * Create row html as a JQuery object based on an indexing sessions\n     * @param session\n     * @private\n     */\n    getIndexingSessionRowHtml() {\n        const $tr = $('<tr class=\"indexingSession\" data-session-id=\"' +\n            this.getSessionId() +\n            '\">');\n        const $td = $('<td/>').appendTo($tr);\n        const $ul = $('<ul/>').appendTo($td);\n        for (const volume of Object.values(this.indexingSessionData.indexedVolumes)) {\n            $('<li/>', {\n                text: volume,\n            }).appendTo($ul);\n        }\n        $tr.append('<td>' + this.indexingSessionData.dateCreated + '</td>');\n        const $progressCell = $('<td class=\"progress\"><div class=\"progressContainer\"></div></td>').css('position', 'relative');\n        const progressBar = new Craft.ProgressBar($progressCell.find('.progressContainer'), false);\n        progressBar.setItemCount(this.indexingSessionData.totalEntries);\n        progressBar.setProcessedItemCount(this.indexingSessionData.processedEntries);\n        progressBar.updateProgressBar();\n        progressBar.showProgressBar();\n        $progressCell.data('progressBar', progressBar);\n        $progressCell\n            .find('.progressContainer')\n            .append(`<div class=\"progressInfo\">${this.indexingSessionData.processedEntries} / ${this.indexingSessionData.totalEntries}</div>`);\n        $tr.append($progressCell);\n        $tr.append('<td>' + this.getSessionStatusMessage() + '</td>');\n        const $actions = this.getActionButtons();\n        $('<td></td>').append($actions).appendTo($tr);\n        return $tr;\n    }\n    /**\n     * Get action buttons for an indexing session\n     * @param session\n     * @private\n     */\n    getActionButtons() {\n        const $buttons = $('<div class=\"buttons\"></div>');\n        if (this.getSessionStatus() == SessionStatus.ACTIONREQUIRED) {\n            const reviewMessage = Craft.t('app', 'Review');\n            $buttons.append($('<button />', {\n                type: 'button',\n                class: 'btn submit',\n                title: reviewMessage,\n                'aria-label': reviewMessage,\n            })\n                .text(reviewMessage)\n                .on('click', (ev) => {\n                const $container = $(ev.target).parent();\n                if ($container.hasClass('disabled')) {\n                    return;\n                }\n                $container.addClass('disabled');\n                this.indexer.getReviewData(this);\n            }));\n        }\n        const discardMessage = Craft.t('app', 'Discard');\n        $buttons.append($('<button />', {\n            type: 'button',\n            class: 'btn submit',\n            title: discardMessage,\n            'aria-label': discardMessage,\n        })\n            .text(discardMessage)\n            .on('click', (ev) => {\n            if ($buttons.hasClass('disabled')) {\n                return;\n            }\n            $buttons.addClass('disabled');\n            this.indexer.stopIndexingSession(this);\n        }));\n        return $buttons;\n    }\n    /**\n     * Get the session status verbose message\n     *\n     * @param status\n     */\n    getSessionStatusMessage() {\n        switch (this.getSessionStatus()) {\n            case SessionStatus.ACTIONREQUIRED:\n                return Craft.t('app', 'Waiting for review');\n                break;\n            case SessionStatus.ACTIVE:\n                return Craft.t('app', 'Active');\n                break;\n            case SessionStatus.WAITING:\n                return Craft.t('app', 'Waiting');\n                break;\n        }\n    }\n    /**\n     * Return a list of missing entries for this session\n     */\n    getMissingEntries() {\n        return this.indexingSessionData.missingEntries;\n    }\n    /**\n     * Return a list of skipped entries for this session\n     */\n    getSkippedEntries() {\n        return this.indexingSessionData.skippedEntries;\n    }\n}\n"],"names":["__webpack_require__","exports","definition","key","o","Object","defineProperty","enumerable","get","obj","prop","prototype","hasOwnProperty","call","Symbol","toStringTag","value","SessionStatus","IndexingActions","AssetIndexer","constructor","$indexingSessionTable","sessions","maxConcurrentConnections","this","_currentIndexingSession","_currentConnectionCount","_tasksWaiting","_priorityTasks","_prunedSessionIds","_currentlyReviewing","indexingSessions","_maxConcurrentConnections","reviewSessionId","sessionModel","session","createSessionFromModel","getSessionStatus","ACTIONREQUIRED","getSessionId","updateIndexingSessionData","performIndexingStep","currentIndexingSession","indexingSession","renderIndexingSessionRow","$row","undefined","includes","find","remove","length","addClass","getIndexingSessionRowHtml","$existing","replaceWith","append","removeClass","discardIndexingSession","sessionId","runTasks","processFailureResponse","response","responseData","data","_updateCurrentIndexingSession","Craft","cp","displayError","message","stop","processSuccessResponse","skipDialog","reviewSession","getReviewData","task","action","OVERVIEW","params","callback","enqueueTask","pruneWaitingTasks","$confirmBody","$","missingEntries","getMissingEntries","missingFiles","files","entries","missingFolders","folders","skippedFiles","getSkippedEntries","skippedFilesList","skippedFile","escapeHtml","t","haveMissingItems","id","uri","translationParams","items","missingItemsHeading","_getMissingItemsHeading","missingItemsCopy","_getMissingItemsCopy","$modal","appendTo","Garnish","$bod","$body","html","$footer","$buttons","modal","Modal","hideOnEsc","hideOnShadeClick","onHide","type","class","text","on","ev","preventDefault","stopIndexingSession","hide","initUiElements","updateSizeAndPosition","postData","getPostData","postParams","expandPostArray","FINISH","missingType","getListEmptyFolders","startIndexing","cb","sendActionRequest","START","then","catch","finally","concurrentSlots","i","Math","min","getEntriesRemaining","PROCESS","getProcessIfRootEmpty","STOP","newTaskList","modified","push","prioritize","shift","values","sessionData","AssetIndexingSession","model","indexer","indexingSessionData","processIfRootEmpty","listEmptyFolders","totalEntries","processedEntries","actionRequired","ACTIVE","WAITING","$tr","$td","$ul","volume","indexedVolumes","dateCreated","$progressCell","css","progressBar","ProgressBar","setItemCount","setProcessedItemCount","updateProgressBar","showProgressBar","getSessionStatusMessage","$actions","getActionButtons","reviewMessage","title","$container","target","parent","hasClass","discardMessage","skippedEntries"],"sourceRoot":""}
        skippedFilesList += `<li>${Craft.escapeHtml(skippedFile)}</li>`;
          missingEntries += `<li><label><input type="checkbox" checked="checked" name="deleteFolder[]" value="${id}"> ${Craft.escapeHtml(
            uri
          )}</label></li>`;
          missingEntries += `<li><label><input type="checkbox" checked="checked" name="deleteAsset[]" value="${id}"> ${Craft.escapeHtml(
            uri
          )}</label></li>`;