From 02af9e04077ff210b81768aa175e9a5bd01399b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Diemer?= Date: Fri, 1 Mar 2024 09:17:04 +0100 Subject: [PATCH] Add polling function management | refs #35772 --- dist/chunked-upload.min.js | 1 - dist/jsu.min.js | 2 +- dist/jsu.min.mjs | 2 +- gulpfile.js | 2 +- package.json | 2 +- src/jsu.js | 2 +- src/lib/polling-manager.js | 77 ++++++++++++++++++++++++++++++++++++++ tests/index.html | 7 ++++ tests/manual_test.js | 24 +++++++++++- tests/test_jsu.spec.js | 50 ++++++++++++------------- tests/test_polling.spec.js | 50 +++++++++++++++++++++++++ tests/test_upload.spec.js | 10 ++--- tests/test_xhr.spec.js | 7 ++-- 13 files changed, 195 insertions(+), 41 deletions(-) delete mode 100644 dist/chunked-upload.min.js create mode 100644 src/lib/polling-manager.js create mode 100644 tests/test_polling.spec.js diff --git a/dist/chunked-upload.min.js b/dist/chunked-upload.min.js deleted file mode 100644 index 1c0d87d..0000000 --- a/dist/chunked-upload.min.js +++ /dev/null @@ -1 +0,0 @@ -class ChunkedUploader{constructor(e){jsu.onDOMLoad(this.init.bind(this,e))}init(e){const t=["inputSelector","formSelector","uploadURL","completeURL"],i={debugMode:null,headers:{},maxRetry:5,retryDelay:1e4,chunkSize:2e7,uploadIdFieldName:"upload_id",fileNameSuffix:"",onProgress:null,onRetry:null,onSuccess:null,onFailure:null};for(const i of t){if(!e[i])throw new Error('A mandatory argument is missing: "'+i+'".');this[i]=e[i]}for(const t in i)this[t]=void 0!==e[t]?e[t]:i[t];if(null===this.debugMode&&-1!==window.location.hash.indexOf("debug")&&(this.debugMode=!0),this.fileInput=document.querySelector(this.inputSelector),!this.fileInput)throw new Error('Cannot find element in the document: "'+this.inputSelector+'".');if(this.fileForm=document.querySelector(this.formSelector),!this.fileForm)throw new Error('Cannot find element in the document: "'+this.formSelector+'".');const s=this;this.fileForm.addEventListener("submit",function(e){s.log("Number of files:",s.fileInput.files.length),s.fileInput.files.length>0&&(e.preventDefault(),setTimeout(s.sendFile.bind(s),1))})}log(){this.debugMode&&console.log.apply(null,arguments)}setProgress(e){this.onProgress&&this.onProgress(e)}retry(e,t){let i;if(this.onRetry&&(i=this.onRetry(e)),void 0===i){const e=this;i=new Promise(function(t){e.log("Retrying in "+e.retryDelay+" ms..."),setTimeout(t,e.retryDelay)})}i.then(t)}uploadSuccess(){this.log("Submitting form..."),this.fileForm.submit(),this.onSuccess&&this.onSuccess(this.uploadId)}uploadFailure(e){this.fileInput.disabled=!1,this.onFailure&&this.onFailure(e)}sendFile(){this.setProgress(0),this.uploadIdInput=document.createElement("input"),this.uploadIdInput.setAttribute("type","hidden"),this.uploadIdInput.setAttribute("name",this.uploadIdFieldName),this.uploadIdInput.setAttribute("id",this.fileInput.id+"_"+this.uploadIdFieldName),this.fileInput.after(this.uploadIdInput),this.fileInput.disabled=!0,this.uploadId=null;const e=this.fileInput.files[0];if(this.fileName=e.name,this.fileNameSuffix){const e=this.fileName.lastIndexOf(".");this.fileName=e>0?this.fileName.substring(0,e)+this.fileNameSuffix+this.fileName.substring(e):"file"+this.fileNameSuffix+".tmp"}this.log("Number of chunk to send:",Math.ceil(e.size/this.chunkSize),this.chunkSize,e.size),this.sendNextChunk(e,0,0)}sendNextChunk(e,t,i){this.log("Sending chunk:","start:",t,"total size:",e.size,"retries:",i);const s=Math.min(t+this.chunkSize,e.size),o=new FormData;o.append("file",e.slice(t,s),this.fileName),o.append("retries",i),this.uploadId&&o.append("upload_id",this.uploadId);const l=(s-t)/e.size,n=Object.assign(this.headers,{"Content-Range":"bytes "+t+"-"+(s-1)+"/"+e.size});this.log("Content-Range",n["Content-Range"]);const u=this;jsu.httpRequest({method:"POST",url:this.uploadURL,headers:n,data:o,json:!0,progress:function(i){if(i.lengthComputable){let s=t/e.size;i.total&&(s+=l*(i.loaded/i.total)),s=Math.floor(95*s),u.log("Progress:",s,l,i.loaded,i.total),u.setProgress(s)}},callback:function(o,l){if(200==o.status&&l.upload_id){u.log("Chunk sent",l),u.uploadId=l.upload_id;const t=s;t>=e.size?u.completeUpload(e,0):u.sendNextChunk(e,t,0)}else u.log("Failed to send chunk:",l),i0){let t=document.cookie.indexOf(e+"=");if(-1!=t){t=t+e.length+1;let s=document.cookie.indexOf(";",t);return-1==s&&(s=document.cookie.length),window.decodeURIComponent(document.cookie.substring(t,s))}}return t}setCookie(e,t,s=360){const n=new Date;n.setDate(n.getDate()+s);const i=0===window.location.href.indexOf("https://")?"; secure; samesite=none":"";document.cookie=e+"="+window.decodeURIComponent(t)+"; expires="+n.toUTCString()+"; path=/"+i}strip(e,t=""){if(!e)return e;const s=""!==t?t:" \n\r\t ";let n=0;for(;n=0&&-1!=s.indexOf(e[i]);)i--;return e.substring(n,i+1)}slugify(e){return e.toString().toLowerCase().replace(/\s+/g,"-").replace(/[^-\w]+/g,"").replace(/-+/g,"-").replace(/^-+/,"").replace(/-+$/,"")}stripHTML(e){if(!e)return e;const t=document.createElement("div");return t.innerHTML=e,t.textContent}decodeHTML(e){if(!e)return"";const t=document.createElement("div");return t.innerHTML=e,0===t.childNodes.length?"":t.childNodes[0].nodeValue}escapeHTML(e){if(!e)return e;let t=e.toString();return t=(t=(t=(t=t.replace(/(&)/g,"&")).replace(/(<)/g,"<")).replace(/(>)/g,">")).replace(/(\n)/g,"
")}escapeAttribute(e){if(!e)return e;let t=e.toString();return t=(t=(t=t.replace(/(")/g,""")).replace(/(')/g,"'")).replace(/(\n)/g," ")}getClickPosition(e,t){let s=t,n=0,i=0;for(;null!=s;)n+=s.offsetLeft,i+=s.offsetTop,s=s.offsetParent;return{x:e.pageX-n,y:e.pageY-i}}onDOMLoad(e){"complete"===document.readyState||"interactive"===document.readyState?setTimeout(e,1):document.addEventListener("DOMContentLoaded",e)}httpRequest(e){const t=e.params?e.params:{};e.cache||(t._=(new Date).getTime());const s=e.method?e.method.toUpperCase():"GET";let n=e.url?e.url:"";const i=e.headers?e.headers:{};if(!/^(GET|HEAD|OPTIONS|TRACE)$/.test(s)){const e=this.getCookie("csrftoken");e&&(i["X-CSRFToken"]=e)}const r=[];for(const e in t)if(t[e]instanceof Array)for(const s of t[e])r.push(encodeURIComponent(e)+"="+encodeURIComponent(s));else r.push(encodeURIComponent(e)+"="+encodeURIComponent(t[e]));let o;if(r.length>0&&(n+=(-1===n.indexOf("?")?"?":"&")+r.join("&")),e.jsonData)i["Content-Type"]="application/json; charset=UTF-8",o=e.data;else if(e.data instanceof FormData)o=e.data;else if(e.data){o=new FormData;for(const t in e.data)if(e.data[t]instanceof Array)for(const s of e.data[t])o.append(t+"[]",s);else o.append(t,e.data[t])}else o=null;const a=new XMLHttpRequest;e.progress&&a.upload&&a.upload.addEventListener("progress",e.progress,!1),e.callback&&(a.addEventListener("readystatechange",function(){if(this.readyState!==XMLHttpRequest.DONE)return;if(a._callbackCalled)return;let t;if(a._callbackCalled=!0,e.json)if(""===this.responseText)t={error:"No response.",empty:!0,raw:this.responseText};else try{t=JSON.parse(this.responseText)}catch(e){t={error:"Failed to parse json response: "+e,raw:this.responseText}}else t=this.responseText;e.callback(this,t)}),a.addEventListener("error",function(t){if(a._callbackCalled)return;a._callbackCalled=!0;const s=t.error||t.message||(t.detail?t.detail.error||t.detail.message:"Unknown error");e.callback(this,{error:s})})),a.open(s,n,!e.synchronous);for(const e in i)if(i[e]instanceof Array)for(const t of i[e])a.setRequestHeader(e,t);else a.setRequestHeader(e,i[e]);return a.send(o),a}compareVersions(e,t,s){t="="==t?"==":t;const n=e.split("."),i=s.split("."),r=Math.max(n.length,i.length);for(let e=0;es)return-1}return 0}setObjectAttributes(e,t,s=null){if(t){"translations"in t&&(this.addTranslations(t.translations),delete t.translations);for(const n in t)s&&-1==s.indexOf(n)||(e[n]=t[n])}}getWebglContext(e,t={},s=""){if(window.WebGLRenderingContext)try{let n;return(n="safari"===s?e.getContext("webgl",t)||e.getContext("experimental-webgl",t):e.getContext("webgl2",t)||e.getContext("webgl",t)||e.getContext("experimental-webgl",t))||(console.log("Failed to initialize WebGL context. Your browser does not support Webgl context."),null)}catch(e){return console.log("WebGL context is supported but may be disable, please check your browser configuration."),null}return console.log("Your browser does not support Webgl context"),null}isInIframe(){return!(!window.frameElement||"IFRAME"!=window.frameElement.nodeName)}attemptFocus(e){if(!this.isFocusable(e))return!1;this.ignoreUntilFocusChanges=!0;try{e.focus()}catch(t){console.log("Failed to focus element.",e,t)}return this.ignoreUntilFocusChanges=!1,document.activeElement===e}isFocusable(e){if(e.tabIndex>0||0===e.tabIndex&&null!==e.getAttribute("tabIndex"))return!0;if(e.disabled)return!1;switch(e.nodeName){case"A":return!!e.href&&"ignore"!=e.rel;case"INPUT":return"hidden"!=e.type&&"file"!=e.type;case"BUTTON":case"SELECT":case"TEXTAREA":return!0;default:return!1}}focusFirstDescendant(e){for(let t=0;t=0;t--){const s=e.childNodes[t];if(this.attemptFocus(s)||this.focusLastDescendant(s))return!0}return!1}_getOSInfo(){let e,t;if(!e&&window.navigator&&window.navigator.platform){const s=window.navigator.platform.toLowerCase();-1==s.indexOf("ipad")&&-1==s.indexOf("iphone")&&-1==s.indexOf("ipod")||(e="ios",t=parseFloat((""+(/CPU.*OS ([0-9_]{1,5})|(CPU like).*AppleWebKit.*Mobile/i.exec(navigator.userAgent)||[0,""])[1]).replace("undefined","3_2").replace("_",".").replace("_",""))||!1)}if(!e&&window.navigator&&window.navigator.appVersion){const t=window.navigator.appVersion.toLowerCase();-1!=t.indexOf("win")?e="windows":-1!=t.indexOf("mac")?e="macos":-1==t.indexOf("x11")&&-1==t.indexOf("linux")||(e="linux")}this.osName=e||"unknown",this.osVersion=t||0}_getBrowserInfo(){let e,t=0;if(window.navigator&&window.navigator.userAgentData&&window.navigator.userAgentData.brands)for(let s=0;s=45,t=("chrome"===this.browserName||"chromium"===this.browserName)&&this.browserVersion>=57,s="safari"===this.browserName&&this.browserVersion>=16,n="edge"===this.browserName&&this.browserVersion>=79;return e||t||n||s}isLivestreamingAvailable(){const e=("chrome"===this.browserName||"chromium"===this.browserName)&&this.browserVersion>=57,t="edge"===this.browserName&&this.browserVersion>=79,s="safari"===this.browserName&&this.browserVersion>=16;return e||t||s}useLang(e){this._currentLang=e,this._translations[e]||(this._translations[e]={}),this._currentCatalog=this._translations[e]}getCurrentLang(){return this._currentLang}getCurrentCatalog(){return this._currentCatalog}addTranslations(e,t=""){let s;t?(this._translations[t]||(this._translations[t]={}),s=this._translations[t]):s=this._currentCatalog;for(const t of Object.keys(e))e[t]&&(s[t]=e[t])}translate(e,t=""){const s=(t?t+"":"")+e;return s in this._currentCatalog?this._currentCatalog[s]:"en"!=this._currentLang&&s in this._translations.en?this._translations.en[s]:e}translateHTML(e,t=""){const s=this.translate(e,t);return this.escapeHTML(s)}translateAttribute(e,t=""){const s=this.translate(e,t);return this.escapeAttribute(s)}getDateDisplay(e){if(!e)return"";const t=/^(\d+)-(\d+)-(\d+)(?: |T)(\d+):(\d+):(\d+)$/.exec(e);if(!t)return e;const s=t[1];let n=null;switch(t[2]){case"01":n=this.translate("January");break;case"02":n=this.translate("February");break;case"03":n=this.translate("March");break;case"04":n=this.translate("April");break;case"05":n=this.translate("May");break;case"06":n=this.translate("June");break;case"07":n=this.translate("July");break;case"08":n=this.translate("August");break;case"09":n=this.translate("September");break;case"10":n=this.translate("October");break;case"11":n=this.translate("November");break;case"12":n=this.translate("December")}const i=t[3];let r,o=parseInt(t[4],10),a=parseInt(t[5],10);if(!n||isNaN(o)||isNaN(a))return e;if(a<10&&(a="0"+a),"en"!==this._currentLang)o<10&&(o="0"+o),r=o+":"+a;else{let e;o<12?(e="AM",o||(o=12)):(e="PM",o>12&&(o-=12)),r=o+":"+a+" "+e}return i+" "+n+" "+s+" "+this.translate("at")+" "+r}getSizeDisplay(e){if(!e||isNaN(e))return"0 "+this.translate("B");let t="";return e>1e3&&(t="k",(e/=1e3)>1e3&&(t="M",(e/=1e3)>1e3&&(t="G",(e/=1e3)>1e3&&(e/=1e3,t="T")))),e.toFixed(1)+" "+t+this.translate("B")}getHashFromRequest(e,t,s,n={}){let i=e+t;if(i&&i.includes("_=")&&((i=i.replace(/_=[0-9]+&?/g,"")).endsWith("?")||i.endsWith("&"))&&(i=i.substring(0,i.length-1)),s instanceof FormData||s instanceof URLSearchParams)i+=JSON.stringify(Object.fromEntries(s));else if(s instanceof Blob)i+="blob-"+s.size;else if(s instanceof ArrayBuffer)i+="arraybuffer-"+s.byteLength;else if(s)try{i+=JSON.stringify(s)}catch(e){i+=JSON.stringify(new Date)}return Object.keys(n).length&&(i+=JSON.stringify(n)),i}_overrideHttpRequest(){window.xhrOverride=!0;const e=[];XMLHttpRequest.noIntercept=!1;const t=XMLHttpRequest.prototype.open;XMLHttpRequest.prototype.open=function(e,s){return this._method=e,this._url=s,t.apply(this,arguments)};const s=XMLHttpRequest.prototype.setRequestHeader;XMLHttpRequest.prototype.setRequestHeader=function(e,t){const n=s.apply(this,arguments);return this._headers||(this._headers={}),this._headers[e]||(this._headers[e]=[]),this._headers[e].push(t),n};const n=XMLHttpRequest.prototype.send;XMLHttpRequest.prototype.send=function(t){const s=window.jsu.getHashFromRequest(this._method,this._url,t,this._headers);if(e.includes(s)){const e="Duplicated request aborted";return Object.defineProperty(this,"statusText",{value:e,writable:!1}),this.dispatchEvent(new CustomEvent("error",{detail:{error:e,message:e}}))}return e.push(s),this.noIntercept||this.addEventListener&&this.addEventListener("readystatechange",function(){this.readyState===XMLHttpRequest.DONE&&e.splice(e.indexOf(s),1)},!1),n.apply(this,arguments)}}}class ChunkedUpload{constructor(e){const t=["file","uploadURL","completeURL"],s={debugMode:null,extraHeaders:{},extraData:{},maxRetry:30,retryDelay:1e4,chunkSize:2e7,fileNameSuffix:"",progressCallback:null,retryCallback:null,successCallback:null,failureCallback:null,inTest:!1};for(const s of t){if(!e[s])throw new Error('A mandatory argument is missing: "'+s+'".');this[s]=e[s]}for(const t in s)this[t]=void 0!==e[t]?e[t]:s[t];null===this.debugMode&&-1!==window.location.hash.indexOf("debug")&&(this.debugMode=!0),this.sendFile()}logDebug(){this.debugMode&&!this.inTest&&console.log.apply(null,arguments)}logError(){this.inTest||console.error.apply(null,arguments)}logWarn(){this.inTest||console.warn.apply(null,arguments)}onProgress(e){this.progressCallback&&this.progressCallback(e)}onRetry(e,t){let s;if(this.retryCallback&&(s=this.retryCallback(e)),void 0===s){const e=this;s=new Promise(function(t){e.logDebug("Retrying in "+e.retryDelay+" ms..."),setTimeout(t,e.retryDelay)})}s.then(t)}onSuccess(){this.successCallback&&this.successCallback(this.uploadId)}onFailure(e){this.failureCallback&&this.failureCallback(e)}sendFile(){if(this.onProgress(0),this.uploadId=null,this.fileName=this.file.name,this.fileNameSuffix){const e=this.fileName.lastIndexOf(".");this.fileName=e>0?this.fileName.substring(0,e)+this.fileNameSuffix+this.fileName.substring(e):"file"+this.fileNameSuffix+".tmp"}this.logDebug("Number of chunk to send:",Math.ceil(this.file.size/this.chunkSize),this.chunkSize,this.file.size),this.sendNextChunk(0,0)}sendNextChunk(e,t){this.logDebug("Sending chunk:","start:",e,"total size:",this.file.size,"retries:",t);const s=Math.min(e+this.chunkSize,this.file.size),n=new FormData;n.append("file",this.file.slice(e,s),this.fileName),n.append("retries",t),this.uploadId&&n.append("upload_id",this.uploadId);for(const e in this.extraData)n.append(e,this.extraData[e]);const i=(s-e)/this.file.size,r=Object.assign(this.extraHeaders,{"Content-Range":"bytes "+e+"-"+(s-1)+"/"+this.file.size});this.logDebug("Content-Range",r["Content-Range"]);const o=this;jsu.httpRequest({method:"POST",url:this.uploadURL,headers:r,data:n,json:!0,progress:function(t){if(t.lengthComputable){let s=e/o.file.size;t.total&&(s+=i*(t.loaded/t.total)),s=Math.floor(95*s),o.logDebug("Progress:",s,i,t.loaded,t.total),o.onProgress(s)}},callback:function(n,i){if(200==n.status&&i.upload_id){o.logDebug("Chunk sent",i),o.uploadId=i.upload_id;const e=s;e>=o.file.size?o.completeUpload(0):o.sendNextChunk(e,0)}else o.logError("Failed to send chunk:",i),t0){let t=document.cookie.indexOf(e+"=");if(-1!=t){t=t+e.length+1;let s=document.cookie.indexOf(";",t);return-1==s&&(s=document.cookie.length),window.decodeURIComponent(document.cookie.substring(t,s))}}return t}setCookie(e,t,s=360){const n=new Date;n.setDate(n.getDate()+s);const i=0===window.location.href.indexOf("https://")?"; secure; samesite=none":"";document.cookie=e+"="+window.decodeURIComponent(t)+"; expires="+n.toUTCString()+"; path=/"+i}strip(e,t=""){if(!e)return e;const s=""!==t?t:" \n\r\t ";let n=0;for(;n=0&&-1!=s.indexOf(e[i]);)i--;return e.substring(n,i+1)}slugify(e){return e.toString().toLowerCase().replace(/\s+/g,"-").replace(/[^-\w]+/g,"").replace(/-+/g,"-").replace(/^-+/,"").replace(/-+$/,"")}stripHTML(e){if(!e)return e;const t=document.createElement("div");return t.innerHTML=e,t.textContent}decodeHTML(e){if(!e)return"";const t=document.createElement("div");return t.innerHTML=e,0===t.childNodes.length?"":t.childNodes[0].nodeValue}escapeHTML(e){if(!e)return e;let t=e.toString();return t=(t=(t=(t=t.replace(/(&)/g,"&")).replace(/(<)/g,"<")).replace(/(>)/g,">")).replace(/(\n)/g,"
")}escapeAttribute(e){if(!e)return e;let t=e.toString();return t=(t=(t=t.replace(/(")/g,""")).replace(/(')/g,"'")).replace(/(\n)/g," ")}getClickPosition(e,t){let s=t,n=0,i=0;for(;null!=s;)n+=s.offsetLeft,i+=s.offsetTop,s=s.offsetParent;return{x:e.pageX-n,y:e.pageY-i}}onDOMLoad(e){"complete"===document.readyState||"interactive"===document.readyState?setTimeout(e,1):document.addEventListener("DOMContentLoaded",e)}httpRequest(e){const t=e.params?e.params:{};e.cache||(t._=(new Date).getTime());const s=e.method?e.method.toUpperCase():"GET";let n=e.url?e.url:"";const i=e.headers?e.headers:{};if(!/^(GET|HEAD|OPTIONS|TRACE)$/.test(s)){const e=this.getCookie("csrftoken");e&&(i["X-CSRFToken"]=e)}const r=[];for(const e in t)if(t[e]instanceof Array)for(const s of t[e])r.push(encodeURIComponent(e)+"="+encodeURIComponent(s));else r.push(encodeURIComponent(e)+"="+encodeURIComponent(t[e]));let o;if(r.length>0&&(n+=(-1===n.indexOf("?")?"?":"&")+r.join("&")),e.jsonData)i["Content-Type"]="application/json; charset=UTF-8",o=e.data;else if(e.data instanceof FormData)o=e.data;else if(e.data){o=new FormData;for(const t in e.data)if(e.data[t]instanceof Array)for(const s of e.data[t])o.append(t+"[]",s);else o.append(t,e.data[t])}else o=null;const a=new XMLHttpRequest;e.progress&&a.upload&&a.upload.addEventListener("progress",e.progress,!1),e.callback&&(a.addEventListener("readystatechange",function(){if(this.readyState!==XMLHttpRequest.DONE)return;if(a._callbackCalled)return;let t;if(a._callbackCalled=!0,e.json)if(""===this.responseText)t={error:"No response.",empty:!0,raw:this.responseText};else try{t=JSON.parse(this.responseText)}catch(e){t={error:"Failed to parse json response: "+e,raw:this.responseText}}else t=this.responseText;e.callback(this,t)}),a.addEventListener("error",function(t){if(a._callbackCalled)return;a._callbackCalled=!0;const s=t.error||t.message||(t.detail?t.detail.error||t.detail.message:"Unknown error");e.callback(this,{error:s})})),a.open(s,n,!e.synchronous);for(const e in i)if(i[e]instanceof Array)for(const t of i[e])a.setRequestHeader(e,t);else a.setRequestHeader(e,i[e]);return a.send(o),a}compareVersions(e,t,s){t="="==t?"==":t;const n=e.split("."),i=s.split("."),r=Math.max(n.length,i.length);for(let e=0;es)return-1}return 0}setObjectAttributes(e,t,s=null){if(t){"translations"in t&&(this.addTranslations(t.translations),delete t.translations);for(const n in t)s&&-1==s.indexOf(n)||(e[n]=t[n])}}getWebglContext(e,t={},s=""){if(window.WebGLRenderingContext)try{let n;return(n="safari"===s?e.getContext("webgl",t)||e.getContext("experimental-webgl",t):e.getContext("webgl2",t)||e.getContext("webgl",t)||e.getContext("experimental-webgl",t))||(console.log("Failed to initialize WebGL context. Your browser does not support Webgl context."),null)}catch(e){return console.log("WebGL context is supported but may be disable, please check your browser configuration."),null}return console.log("Your browser does not support Webgl context"),null}isInIframe(){return!(!window.frameElement||"IFRAME"!=window.frameElement.nodeName)}attemptFocus(e){if(!this.isFocusable(e))return!1;this.ignoreUntilFocusChanges=!0;try{e.focus()}catch(t){console.log("Failed to focus element.",e,t)}return this.ignoreUntilFocusChanges=!1,document.activeElement===e}isFocusable(e){if(e.tabIndex>0||0===e.tabIndex&&null!==e.getAttribute("tabIndex"))return!0;if(e.disabled)return!1;switch(e.nodeName){case"A":return!!e.href&&"ignore"!=e.rel;case"INPUT":return"hidden"!=e.type&&"file"!=e.type;case"BUTTON":case"SELECT":case"TEXTAREA":return!0;default:return!1}}focusFirstDescendant(e){for(let t=0;t=0;t--){const s=e.childNodes[t];if(this.attemptFocus(s)||this.focusLastDescendant(s))return!0}return!1}_getOSInfo(){let e,t;if(!e&&window.navigator&&window.navigator.platform){const s=window.navigator.platform.toLowerCase();-1==s.indexOf("ipad")&&-1==s.indexOf("iphone")&&-1==s.indexOf("ipod")||(e="ios",t=parseFloat((""+(/CPU.*OS ([0-9_]{1,5})|(CPU like).*AppleWebKit.*Mobile/i.exec(navigator.userAgent)||[0,""])[1]).replace("undefined","3_2").replace("_",".").replace("_",""))||!1)}if(!e&&window.navigator&&window.navigator.appVersion){const t=window.navigator.appVersion.toLowerCase();-1!=t.indexOf("win")?e="windows":-1!=t.indexOf("mac")?e="macos":-1==t.indexOf("x11")&&-1==t.indexOf("linux")||(e="linux")}this.osName=e||"unknown",this.osVersion=t||0}_getBrowserInfo(){let e,t=0;if(window.navigator&&window.navigator.userAgentData&&window.navigator.userAgentData.brands)for(let s=0;s=45,t=("chrome"===this.browserName||"chromium"===this.browserName)&&this.browserVersion>=57,s="safari"===this.browserName&&this.browserVersion>=16,n="edge"===this.browserName&&this.browserVersion>=79;return e||t||n||s}isLivestreamingAvailable(){const e=("chrome"===this.browserName||"chromium"===this.browserName)&&this.browserVersion>=57,t="edge"===this.browserName&&this.browserVersion>=79,s="safari"===this.browserName&&this.browserVersion>=16;return e||t||s}useLang(e){this._currentLang=e,this._translations[e]||(this._translations[e]={}),this._currentCatalog=this._translations[e]}getCurrentLang(){return this._currentLang}getCurrentCatalog(){return this._currentCatalog}addTranslations(e,t=""){let s;t?(this._translations[t]||(this._translations[t]={}),s=this._translations[t]):s=this._currentCatalog;for(const t of Object.keys(e))e[t]&&(s[t]=e[t])}translate(e,t=""){const s=(t?t+"":"")+e;return s in this._currentCatalog?this._currentCatalog[s]:"en"!=this._currentLang&&s in this._translations.en?this._translations.en[s]:e}translateHTML(e,t=""){const s=this.translate(e,t);return this.escapeHTML(s)}translateAttribute(e,t=""){const s=this.translate(e,t);return this.escapeAttribute(s)}getDateDisplay(e){if(!e)return"";const t=/^(\d+)-(\d+)-(\d+)(?: |T)(\d+):(\d+):(\d+)$/.exec(e);if(!t)return e;const s=t[1];let n=null;switch(t[2]){case"01":n=this.translate("January");break;case"02":n=this.translate("February");break;case"03":n=this.translate("March");break;case"04":n=this.translate("April");break;case"05":n=this.translate("May");break;case"06":n=this.translate("June");break;case"07":n=this.translate("July");break;case"08":n=this.translate("August");break;case"09":n=this.translate("September");break;case"10":n=this.translate("October");break;case"11":n=this.translate("November");break;case"12":n=this.translate("December")}const i=t[3];let r,o=parseInt(t[4],10),a=parseInt(t[5],10);if(!n||isNaN(o)||isNaN(a))return e;if(a<10&&(a="0"+a),"en"!==this._currentLang)o<10&&(o="0"+o),r=o+":"+a;else{let e;o<12?(e="AM",o||(o=12)):(e="PM",o>12&&(o-=12)),r=o+":"+a+" "+e}return i+" "+n+" "+s+" "+this.translate("at")+" "+r}getSizeDisplay(e){if(!e||isNaN(e))return"0 "+this.translate("B");let t="";return e>1e3&&(t="k",(e/=1e3)>1e3&&(t="M",(e/=1e3)>1e3&&(t="G",(e/=1e3)>1e3&&(e/=1e3,t="T")))),e.toFixed(1)+" "+t+this.translate("B")}getHashFromRequest(e,t,s,n={}){let i=e+t;if(i&&i.includes("_=")&&((i=i.replace(/_=[0-9]+&?/g,"")).endsWith("?")||i.endsWith("&"))&&(i=i.substring(0,i.length-1)),s instanceof FormData||s instanceof URLSearchParams)i+=JSON.stringify(Object.fromEntries(s));else if(s instanceof Blob)i+="blob-"+s.size;else if(s instanceof ArrayBuffer)i+="arraybuffer-"+s.byteLength;else if(s)try{i+=JSON.stringify(s)}catch(e){i+=JSON.stringify(new Date)}return Object.keys(n).length&&(i+=JSON.stringify(n)),i}_overrideHttpRequest(){window.xhrOverride=!0;const e=[];XMLHttpRequest.noIntercept=!1;const t=XMLHttpRequest.prototype.open;XMLHttpRequest.prototype.open=function(e,s){return this._method=e,this._url=s,t.apply(this,arguments)};const s=XMLHttpRequest.prototype.setRequestHeader;XMLHttpRequest.prototype.setRequestHeader=function(e,t){const n=s.apply(this,arguments);return this._headers||(this._headers={}),this._headers[e]||(this._headers[e]=[]),this._headers[e].push(t),n};const n=XMLHttpRequest.prototype.send;XMLHttpRequest.prototype.send=function(t){const s=window.jsu.getHashFromRequest(this._method,this._url,t,this._headers);if(e.includes(s)){const e="Duplicated request aborted";return Object.defineProperty(this,"statusText",{value:e,writable:!1}),this.dispatchEvent(new CustomEvent("error",{detail:{error:e,message:e}}))}return e.push(s),this.noIntercept||this.addEventListener&&this.addEventListener("readystatechange",function(){this.readyState===XMLHttpRequest.DONE&&e.splice(e.indexOf(s),1)},!1),n.apply(this,arguments)}}}class ChunkedUpload{constructor(e){const t=["file","uploadURL","completeURL"],s={debugMode:null,extraHeaders:{},extraData:{},maxRetry:30,retryDelay:1e4,chunkSize:2e7,fileNameSuffix:"",progressCallback:null,retryCallback:null,successCallback:null,failureCallback:null,inTest:!1};for(const s of t){if(!e[s])throw new Error('A mandatory argument is missing: "'+s+'".');this[s]=e[s]}for(const t in s)this[t]=void 0!==e[t]?e[t]:s[t];null===this.debugMode&&-1!==window.location.hash.indexOf("debug")&&(this.debugMode=!0),this.sendFile()}logDebug(){this.debugMode&&!this.inTest&&console.log.apply(null,arguments)}logError(){this.inTest||console.error.apply(null,arguments)}logWarn(){this.inTest||console.warn.apply(null,arguments)}onProgress(e){this.progressCallback&&this.progressCallback(e)}onRetry(e,t){let s;if(this.retryCallback&&(s=this.retryCallback(e)),void 0===s){const e=this;s=new Promise(function(t){e.logDebug("Retrying in "+e.retryDelay+" ms..."),setTimeout(t,e.retryDelay)})}s.then(t)}onSuccess(){this.successCallback&&this.successCallback(this.uploadId)}onFailure(e){this.failureCallback&&this.failureCallback(e)}sendFile(){if(this.onProgress(0),this.uploadId=null,this.fileName=this.file.name,this.fileNameSuffix){const e=this.fileName.lastIndexOf(".");this.fileName=e>0?this.fileName.substring(0,e)+this.fileNameSuffix+this.fileName.substring(e):"file"+this.fileNameSuffix+".tmp"}this.logDebug("Number of chunk to send:",Math.ceil(this.file.size/this.chunkSize),this.chunkSize,this.file.size),this.sendNextChunk(0,0)}sendNextChunk(e,t){this.logDebug("Sending chunk:","start:",e,"total size:",this.file.size,"retries:",t);const s=Math.min(e+this.chunkSize,this.file.size),n=new FormData;n.append("file",this.file.slice(e,s),this.fileName),n.append("retries",t),this.uploadId&&n.append("upload_id",this.uploadId);for(const e in this.extraData)n.append(e,this.extraData[e]);const i=(s-e)/this.file.size,r=Object.assign(this.extraHeaders,{"Content-Range":"bytes "+e+"-"+(s-1)+"/"+this.file.size});this.logDebug("Content-Range",r["Content-Range"]);const o=this;jsu.httpRequest({method:"POST",url:this.uploadURL,headers:r,data:n,json:!0,progress:function(t){if(t.lengthComputable){let s=e/o.file.size;t.total&&(s+=i*(t.loaded/t.total)),s=Math.floor(95*s),o.logDebug("Progress:",s,i,t.loaded,t.total),o.onProgress(s)}},callback:function(n,i){if(200==n.status&&i.upload_id){o.logDebug("Chunk sent",i),o.uploadId=i.upload_id;const e=s;e>=o.file.size?o.completeUpload(0):o.sendNextChunk(e,0)}else o.logError("Failed to send chunk:",i),t0){let t=document.cookie.indexOf(e+"=");if(-1!=t){t=t+e.length+1;let s=document.cookie.indexOf(";",t);return-1==s&&(s=document.cookie.length),window.decodeURIComponent(document.cookie.substring(t,s))}}return t}setCookie(e,t,s=360){const n=new Date;n.setDate(n.getDate()+s);const r=0===window.location.href.indexOf("https://")?"; secure; samesite=none":"";document.cookie=e+"="+window.decodeURIComponent(t)+"; expires="+n.toUTCString()+"; path=/"+r}strip(e,t=""){if(!e)return e;const s=""!==t?t:" \n\r\t ";let n=0;for(;n=0&&-1!=s.indexOf(e[r]);)r--;return e.substring(n,r+1)}slugify(e){return e.toString().toLowerCase().replace(/\s+/g,"-").replace(/[^-\w]+/g,"").replace(/-+/g,"-").replace(/^-+/,"").replace(/-+$/,"")}stripHTML(e){if(!e)return e;const t=document.createElement("div");return t.innerHTML=e,t.textContent}decodeHTML(e){if(!e)return"";const t=document.createElement("div");return t.innerHTML=e,0===t.childNodes.length?"":t.childNodes[0].nodeValue}escapeHTML(e){if(!e)return e;let t=e.toString();return t=(t=(t=(t=t.replace(/(&)/g,"&")).replace(/(<)/g,"<")).replace(/(>)/g,">")).replace(/(\n)/g,"
")}escapeAttribute(e){if(!e)return e;let t=e.toString();return t=(t=(t=t.replace(/(")/g,""")).replace(/(')/g,"'")).replace(/(\n)/g," ")}getClickPosition(e,t){let s=t,n=0,r=0;for(;null!=s;)n+=s.offsetLeft,r+=s.offsetTop,s=s.offsetParent;return{x:e.pageX-n,y:e.pageY-r}}onDOMLoad(e){"complete"===document.readyState||"interactive"===document.readyState?setTimeout(e,1):document.addEventListener("DOMContentLoaded",e)}httpRequest(e){const t=e.params?e.params:{};e.cache||(t._=(new Date).getTime());const s=e.method?e.method.toUpperCase():"GET";let n=e.url?e.url:"";const r=e.headers?e.headers:{};if(!/^(GET|HEAD|OPTIONS|TRACE)$/.test(s)){const e=this.getCookie("csrftoken");e&&(r["X-CSRFToken"]=e)}const i=[];for(const e in t)if(t[e]instanceof Array)for(const s of t[e])i.push(encodeURIComponent(e)+"="+encodeURIComponent(s));else i.push(encodeURIComponent(e)+"="+encodeURIComponent(t[e]));let o;if(i.length>0&&(n+=(-1===n.indexOf("?")?"?":"&")+i.join("&")),e.jsonData)r["Content-Type"]="application/json; charset=UTF-8",o=e.data;else if(e.data instanceof FormData)o=e.data;else if(e.data){o=new FormData;for(const t in e.data)if(e.data[t]instanceof Array)for(const s of e.data[t])o.append(t+"[]",s);else o.append(t,e.data[t])}else o=null;const a=new XMLHttpRequest;e.progress&&a.upload&&a.upload.addEventListener("progress",e.progress,!1),e.callback&&(a.addEventListener("readystatechange",function(){if(this.readyState!==XMLHttpRequest.DONE)return;if(a._callbackCalled)return;let t;if(a._callbackCalled=!0,e.json)if(""===this.responseText)t={error:"No response.",empty:!0,raw:this.responseText};else try{t=JSON.parse(this.responseText)}catch(e){t={error:"Failed to parse json response: "+e,raw:this.responseText}}else t=this.responseText;e.callback(this,t)}),a.addEventListener("error",function(t){if(a._callbackCalled)return;a._callbackCalled=!0;const s=t.error||t.message||(t.detail?t.detail.error||t.detail.message:"Unknown error");e.callback(this,{error:s})})),a.open(s,n,!e.synchronous);for(const e in r)if(r[e]instanceof Array)for(const t of r[e])a.setRequestHeader(e,t);else a.setRequestHeader(e,r[e]);return a.send(o),a}compareVersions(e,t,s){t="="==t?"==":t;const n=e.split("."),r=s.split("."),i=Math.max(n.length,r.length);for(let e=0;es)return-1}return 0}setObjectAttributes(e,t,s=null){if(t){"translations"in t&&(this.addTranslations(t.translations),delete t.translations);for(const n in t)s&&-1==s.indexOf(n)||(e[n]=t[n])}}getWebglContext(e,t={},s=""){if(window.WebGLRenderingContext)try{let n;return(n="safari"===s?e.getContext("webgl",t)||e.getContext("experimental-webgl",t):e.getContext("webgl2",t)||e.getContext("webgl",t)||e.getContext("experimental-webgl",t))||(console.log("Failed to initialize WebGL context. Your browser does not support Webgl context."),null)}catch(e){return console.log("WebGL context is supported but may be disable, please check your browser configuration."),null}return console.log("Your browser does not support Webgl context"),null}isInIframe(){return!(!window.frameElement||"IFRAME"!=window.frameElement.nodeName)}attemptFocus(e){if(!this.isFocusable(e))return!1;this.ignoreUntilFocusChanges=!0;try{e.focus()}catch(t){console.log("Failed to focus element.",e,t)}return this.ignoreUntilFocusChanges=!1,document.activeElement===e}isFocusable(e){if(e.tabIndex>0||0===e.tabIndex&&null!==e.getAttribute("tabIndex"))return!0;if(e.disabled)return!1;switch(e.nodeName){case"A":return!!e.href&&"ignore"!=e.rel;case"INPUT":return"hidden"!=e.type&&"file"!=e.type;case"BUTTON":case"SELECT":case"TEXTAREA":return!0;default:return!1}}focusFirstDescendant(e){for(let t=0;t=0;t--){const s=e.childNodes[t];if(this.attemptFocus(s)||this.focusLastDescendant(s))return!0}return!1}_getOSInfo(){let e,t;if(!e&&window.navigator&&window.navigator.platform){const s=window.navigator.platform.toLowerCase();-1==s.indexOf("ipad")&&-1==s.indexOf("iphone")&&-1==s.indexOf("ipod")||(e="ios",t=parseFloat((""+(/CPU.*OS ([0-9_]{1,5})|(CPU like).*AppleWebKit.*Mobile/i.exec(navigator.userAgent)||[0,""])[1]).replace("undefined","3_2").replace("_",".").replace("_",""))||!1)}if(!e&&window.navigator&&window.navigator.appVersion){const t=window.navigator.appVersion.toLowerCase();-1!=t.indexOf("win")?e="windows":-1!=t.indexOf("mac")?e="macos":-1==t.indexOf("x11")&&-1==t.indexOf("linux")||(e="linux")}this.osName=e||"unknown",this.osVersion=t||0}_getBrowserInfo(){let e,t=0;if(window.navigator&&window.navigator.userAgentData&&window.navigator.userAgentData.brands)for(let s=0;s=45,t=("chrome"===this.browserName||"chromium"===this.browserName)&&this.browserVersion>=57,s="safari"===this.browserName&&this.browserVersion>=16,n="edge"===this.browserName&&this.browserVersion>=79;return e||t||n||s}isLivestreamingAvailable(){const e=("chrome"===this.browserName||"chromium"===this.browserName)&&this.browserVersion>=57,t="edge"===this.browserName&&this.browserVersion>=79,s="safari"===this.browserName&&this.browserVersion>=16;return e||t||s}useLang(e){this._currentLang=e,this._translations[e]||(this._translations[e]={}),this._currentCatalog=this._translations[e]}getCurrentLang(){return this._currentLang}getCurrentCatalog(){return this._currentCatalog}addTranslations(e,t=""){let s;t?(this._translations[t]||(this._translations[t]={}),s=this._translations[t]):s=this._currentCatalog;for(const t of Object.keys(e))e[t]&&(s[t]=e[t])}translate(e,t=""){const s=(t?t+"":"")+e;return s in this._currentCatalog?this._currentCatalog[s]:"en"!=this._currentLang&&s in this._translations.en?this._translations.en[s]:e}translateHTML(e,t=""){const s=this.translate(e,t);return this.escapeHTML(s)}translateAttribute(e,t=""){const s=this.translate(e,t);return this.escapeAttribute(s)}getDateDisplay(e){if(!e)return"";const t=/^(\d+)-(\d+)-(\d+)(?: |T)(\d+):(\d+):(\d+)$/.exec(e);if(!t)return e;const s=t[1];let n=null;switch(t[2]){case"01":n=this.translate("January");break;case"02":n=this.translate("February");break;case"03":n=this.translate("March");break;case"04":n=this.translate("April");break;case"05":n=this.translate("May");break;case"06":n=this.translate("June");break;case"07":n=this.translate("July");break;case"08":n=this.translate("August");break;case"09":n=this.translate("September");break;case"10":n=this.translate("October");break;case"11":n=this.translate("November");break;case"12":n=this.translate("December")}const r=t[3];let i,o=parseInt(t[4],10),a=parseInt(t[5],10);if(!n||isNaN(o)||isNaN(a))return e;if(a<10&&(a="0"+a),"en"!==this._currentLang)o<10&&(o="0"+o),i=o+":"+a;else{let e;o<12?(e="AM",o||(o=12)):(e="PM",o>12&&(o-=12)),i=o+":"+a+" "+e}return r+" "+n+" "+s+" "+this.translate("at")+" "+i}getSizeDisplay(e){if(!e||isNaN(e))return"0 "+this.translate("B");let t="";return e>1e3&&(t="k",(e/=1e3)>1e3&&(t="M",(e/=1e3)>1e3&&(t="G",(e/=1e3)>1e3&&(e/=1e3,t="T")))),e.toFixed(1)+" "+t+this.translate("B")}getHashFromRequest(e,t,s,n={}){let r=e+t;if(r&&r.includes("_=")&&((r=r.replace(/_=[0-9]+&?/g,"")).endsWith("?")||r.endsWith("&"))&&(r=r.substring(0,r.length-1)),s instanceof FormData||s instanceof URLSearchParams)r+=JSON.stringify(Object.fromEntries(s));else if(s instanceof Blob)r+="blob-"+s.size;else if(s instanceof ArrayBuffer)r+="arraybuffer-"+s.byteLength;else if(s)try{r+=JSON.stringify(s)}catch(e){r+=JSON.stringify(new Date)}return Object.keys(n).length&&(r+=JSON.stringify(n)),r}_overrideHttpRequest(){window.xhrOverride=!0;const e=[];XMLHttpRequest.noIntercept=!1;const t=XMLHttpRequest.prototype.open;XMLHttpRequest.prototype.open=function(e,s){return this._method=e,this._url=s,t.apply(this,arguments)};const s=XMLHttpRequest.prototype.setRequestHeader;XMLHttpRequest.prototype.setRequestHeader=function(e,t){const n=s.apply(this,arguments);return this._headers||(this._headers={}),this._headers[e]||(this._headers[e]=[]),this._headers[e].push(t),n};const n=XMLHttpRequest.prototype.send;XMLHttpRequest.prototype.send=function(t){const s=window.jsu.getHashFromRequest(this._method,this._url,t,this._headers);if(e.includes(s)){const e="Duplicated request aborted";return Object.defineProperty(this,"statusText",{value:e,writable:!1}),this.dispatchEvent(new CustomEvent("error",{detail:{error:e,message:e}}))}return e.push(s),this.noIntercept||this.addEventListener&&this.addEventListener("readystatechange",function(){this.readyState===XMLHttpRequest.DONE&&e.splice(e.indexOf(s),1)},!1),n.apply(this,arguments)}}}export class ChunkedUpload{constructor(e){const t=["file","uploadURL","completeURL"],s={debugMode:null,extraHeaders:{},extraData:{},maxRetry:30,retryDelay:1e4,chunkSize:2e7,fileNameSuffix:"",progressCallback:null,retryCallback:null,successCallback:null,failureCallback:null,inTest:!1};for(const s of t){if(!e[s])throw new Error('A mandatory argument is missing: "'+s+'".');this[s]=e[s]}for(const t in s)this[t]=void 0!==e[t]?e[t]:s[t];null===this.debugMode&&-1!==window.location.hash.indexOf("debug")&&(this.debugMode=!0),this.sendFile()}logDebug(){this.debugMode&&!this.inTest&&console.log.apply(null,arguments)}logError(){this.inTest||console.error.apply(null,arguments)}logWarn(){this.inTest||console.warn.apply(null,arguments)}onProgress(e){this.progressCallback&&this.progressCallback(e)}onRetry(e,t){let s;if(this.retryCallback&&(s=this.retryCallback(e)),void 0===s){const e=this;s=new Promise(function(t){e.logDebug("Retrying in "+e.retryDelay+" ms..."),setTimeout(t,e.retryDelay)})}s.then(t)}onSuccess(){this.successCallback&&this.successCallback(this.uploadId)}onFailure(e){this.failureCallback&&this.failureCallback(e)}sendFile(){if(this.onProgress(0),this.uploadId=null,this.fileName=this.file.name,this.fileNameSuffix){const e=this.fileName.lastIndexOf(".");this.fileName=e>0?this.fileName.substring(0,e)+this.fileNameSuffix+this.fileName.substring(e):"file"+this.fileNameSuffix+".tmp"}this.logDebug("Number of chunk to send:",Math.ceil(this.file.size/this.chunkSize),this.chunkSize,this.file.size),this.sendNextChunk(0,0)}sendNextChunk(e,t){this.logDebug("Sending chunk:","start:",e,"total size:",this.file.size,"retries:",t);const s=Math.min(e+this.chunkSize,this.file.size),n=new FormData;n.append("file",this.file.slice(e,s),this.fileName),n.append("retries",t),this.uploadId&&n.append("upload_id",this.uploadId);for(const e in this.extraData)n.append(e,this.extraData[e]);const r=(s-e)/this.file.size,i=Object.assign(this.extraHeaders,{"Content-Range":"bytes "+e+"-"+(s-1)+"/"+this.file.size});this.logDebug("Content-Range",i["Content-Range"]);const o=this;jsu.httpRequest({method:"POST",url:this.uploadURL,headers:i,data:n,json:!0,progress:function(t){if(t.lengthComputable){let s=e/o.file.size;t.total&&(s+=r*(t.loaded/t.total)),s=Math.floor(95*s),o.logDebug("Progress:",s,r,t.loaded,t.total),o.onProgress(s)}},callback:function(n,r){if(200==n.status&&r.upload_id){o.logDebug("Chunk sent",r),o.uploadId=r.upload_id;const e=s;e>=o.file.size?o.completeUpload(0):o.sendNextChunk(e,0)}else o.logError("Failed to send chunk:",r),t0){let t=document.cookie.indexOf(e+"=");if(-1!=t){t=t+e.length+1;let s=document.cookie.indexOf(";",t);return-1==s&&(s=document.cookie.length),window.decodeURIComponent(document.cookie.substring(t,s))}}return t}setCookie(e,t,s=360){const n=new Date;n.setDate(n.getDate()+s);const i=0===window.location.href.indexOf("https://")?"; secure; samesite=none":"";document.cookie=e+"="+window.decodeURIComponent(t)+"; expires="+n.toUTCString()+"; path=/"+i}strip(e,t=""){if(!e)return e;const s=""!==t?t:" \n\r\t ";let n=0;for(;n=0&&-1!=s.indexOf(e[i]);)i--;return e.substring(n,i+1)}slugify(e){return e.toString().toLowerCase().replace(/\s+/g,"-").replace(/[^-\w]+/g,"").replace(/-+/g,"-").replace(/^-+/,"").replace(/-+$/,"")}stripHTML(e){if(!e)return e;const t=document.createElement("div");return t.innerHTML=e,t.textContent}decodeHTML(e){if(!e)return"";const t=document.createElement("div");return t.innerHTML=e,0===t.childNodes.length?"":t.childNodes[0].nodeValue}escapeHTML(e){if(!e)return e;let t=e.toString();return t=(t=(t=(t=t.replace(/(&)/g,"&")).replace(/(<)/g,"<")).replace(/(>)/g,">")).replace(/(\n)/g,"
")}escapeAttribute(e){if(!e)return e;let t=e.toString();return t=(t=(t=t.replace(/(")/g,""")).replace(/(')/g,"'")).replace(/(\n)/g," ")}getClickPosition(e,t){let s=t,n=0,i=0;for(;null!=s;)n+=s.offsetLeft,i+=s.offsetTop,s=s.offsetParent;return{x:e.pageX-n,y:e.pageY-i}}onDOMLoad(e){"complete"===document.readyState||"interactive"===document.readyState?setTimeout(e,1):document.addEventListener("DOMContentLoaded",e)}httpRequest(e){const t=e.params?e.params:{};e.cache||(t._=(new Date).getTime());const s=e.method?e.method.toUpperCase():"GET";let n=e.url?e.url:"";const i=e.headers?e.headers:{};if(!/^(GET|HEAD|OPTIONS|TRACE)$/.test(s)){const e=this.getCookie("csrftoken");e&&(i["X-CSRFToken"]=e)}const r=[];for(const e in t)if(t[e]instanceof Array)for(const s of t[e])r.push(encodeURIComponent(e)+"="+encodeURIComponent(s));else r.push(encodeURIComponent(e)+"="+encodeURIComponent(t[e]));let o;if(r.length>0&&(n+=(-1===n.indexOf("?")?"?":"&")+r.join("&")),e.jsonData)i["Content-Type"]="application/json; charset=UTF-8",o=e.data;else if(e.data instanceof FormData)o=e.data;else if(e.data){o=new FormData;for(const t in e.data)if(e.data[t]instanceof Array)for(const s of e.data[t])o.append(t+"[]",s);else o.append(t,e.data[t])}else o=null;const a=new XMLHttpRequest;e.progress&&a.upload&&a.upload.addEventListener("progress",e.progress,!1),e.callback&&(a.addEventListener("readystatechange",function(){if(this.readyState!==XMLHttpRequest.DONE)return;if(a._callbackCalled)return;let t;if(a._callbackCalled=!0,e.json)if(""===this.responseText)t={error:"No response.",empty:!0,raw:this.responseText};else try{t=JSON.parse(this.responseText)}catch(e){t={error:"Failed to parse json response: "+e,raw:this.responseText}}else t=this.responseText;e.callback(this,t)}),a.addEventListener("error",function(t){if(a._callbackCalled)return;a._callbackCalled=!0;const s=t.error||t.message||(t.detail?t.detail.error||t.detail.message:"Unknown error");e.callback(this,{error:s})})),a.open(s,n,!e.synchronous);for(const e in i)if(i[e]instanceof Array)for(const t of i[e])a.setRequestHeader(e,t);else a.setRequestHeader(e,i[e]);return a.send(o),a}compareVersions(e,t,s){t="="==t?"==":t;const n=e.split("."),i=s.split("."),r=Math.max(n.length,i.length);for(let e=0;es)return-1}return 0}setObjectAttributes(e,t,s=null){if(t){"translations"in t&&(this.addTranslations(t.translations),delete t.translations);for(const n in t)s&&-1==s.indexOf(n)||(e[n]=t[n])}}getWebglContext(e,t={},s=""){if(window.WebGLRenderingContext)try{let n;return(n="safari"===s?e.getContext("webgl",t)||e.getContext("experimental-webgl",t):e.getContext("webgl2",t)||e.getContext("webgl",t)||e.getContext("experimental-webgl",t))||(console.log("Failed to initialize WebGL context. Your browser does not support Webgl context."),null)}catch(e){return console.log("WebGL context is supported but may be disable, please check your browser configuration."),null}return console.log("Your browser does not support Webgl context"),null}isInIframe(){return!(!window.frameElement||"IFRAME"!=window.frameElement.nodeName)}attemptFocus(e){if(!this.isFocusable(e))return!1;this.ignoreUntilFocusChanges=!0;try{e.focus()}catch(t){console.log("Failed to focus element.",e,t)}return this.ignoreUntilFocusChanges=!1,document.activeElement===e}isFocusable(e){if(e.tabIndex>0||0===e.tabIndex&&null!==e.getAttribute("tabIndex"))return!0;if(e.disabled)return!1;switch(e.nodeName){case"A":return!!e.href&&"ignore"!=e.rel;case"INPUT":return"hidden"!=e.type&&"file"!=e.type;case"BUTTON":case"SELECT":case"TEXTAREA":return!0;default:return!1}}focusFirstDescendant(e){for(let t=0;t=0;t--){const s=e.childNodes[t];if(this.attemptFocus(s)||this.focusLastDescendant(s))return!0}return!1}_getOSInfo(){let e,t;if(!e&&window.navigator&&window.navigator.platform){const s=window.navigator.platform.toLowerCase();-1==s.indexOf("ipad")&&-1==s.indexOf("iphone")&&-1==s.indexOf("ipod")||(e="ios",t=parseFloat((""+(/CPU.*OS ([0-9_]{1,5})|(CPU like).*AppleWebKit.*Mobile/i.exec(navigator.userAgent)||[0,""])[1]).replace("undefined","3_2").replace("_",".").replace("_",""))||!1)}if(!e&&window.navigator&&window.navigator.appVersion){const t=window.navigator.appVersion.toLowerCase();-1!=t.indexOf("win")?e="windows":-1!=t.indexOf("mac")?e="macos":-1==t.indexOf("x11")&&-1==t.indexOf("linux")||(e="linux")}this.osName=e||"unknown",this.osVersion=t||0}_getBrowserInfo(){let e,t=0;if(window.navigator&&window.navigator.userAgentData&&window.navigator.userAgentData.brands)for(let s=0;s=45,t=("chrome"===this.browserName||"chromium"===this.browserName)&&this.browserVersion>=57,s="safari"===this.browserName&&this.browserVersion>=16,n="edge"===this.browserName&&this.browserVersion>=79;return e||t||n||s}isLivestreamingAvailable(){const e=("chrome"===this.browserName||"chromium"===this.browserName)&&this.browserVersion>=57,t="edge"===this.browserName&&this.browserVersion>=79,s="safari"===this.browserName&&this.browserVersion>=16;return e||t||s}useLang(e){this._currentLang=e,this._translations[e]||(this._translations[e]={}),this._currentCatalog=this._translations[e]}getCurrentLang(){return this._currentLang}getCurrentCatalog(){return this._currentCatalog}addTranslations(e,t=""){let s;t?(this._translations[t]||(this._translations[t]={}),s=this._translations[t]):s=this._currentCatalog;for(const t of Object.keys(e))e[t]&&(s[t]=e[t])}translate(e,t=""){const s=(t?t+"":"")+e;return s in this._currentCatalog?this._currentCatalog[s]:"en"!=this._currentLang&&s in this._translations.en?this._translations.en[s]:e}translateHTML(e,t=""){const s=this.translate(e,t);return this.escapeHTML(s)}translateAttribute(e,t=""){const s=this.translate(e,t);return this.escapeAttribute(s)}getDateDisplay(e){if(!e)return"";const t=/^(\d+)-(\d+)-(\d+)(?: |T)(\d+):(\d+):(\d+)$/.exec(e);if(!t)return e;const s=t[1];let n=null;switch(t[2]){case"01":n=this.translate("January");break;case"02":n=this.translate("February");break;case"03":n=this.translate("March");break;case"04":n=this.translate("April");break;case"05":n=this.translate("May");break;case"06":n=this.translate("June");break;case"07":n=this.translate("July");break;case"08":n=this.translate("August");break;case"09":n=this.translate("September");break;case"10":n=this.translate("October");break;case"11":n=this.translate("November");break;case"12":n=this.translate("December")}const i=t[3];let r,o=parseInt(t[4],10),a=parseInt(t[5],10);if(!n||isNaN(o)||isNaN(a))return e;if(a<10&&(a="0"+a),"en"!==this._currentLang)o<10&&(o="0"+o),r=o+":"+a;else{let e;o<12?(e="AM",o||(o=12)):(e="PM",o>12&&(o-=12)),r=o+":"+a+" "+e}return i+" "+n+" "+s+" "+this.translate("at")+" "+r}getSizeDisplay(e){if(!e||isNaN(e))return"0 "+this.translate("B");let t="";return e>1e3&&(t="k",(e/=1e3)>1e3&&(t="M",(e/=1e3)>1e3&&(t="G",(e/=1e3)>1e3&&(e/=1e3,t="T")))),e.toFixed(1)+" "+t+this.translate("B")}getHashFromRequest(e,t,s,n={}){let i=e+t;if(i&&i.includes("_=")&&((i=i.replace(/_=[0-9]+&?/g,"")).endsWith("?")||i.endsWith("&"))&&(i=i.substring(0,i.length-1)),s instanceof FormData||s instanceof URLSearchParams)i+=JSON.stringify(Object.fromEntries(s));else if(s instanceof Blob)i+="blob-"+s.size;else if(s instanceof ArrayBuffer)i+="arraybuffer-"+s.byteLength;else if(s)try{i+=JSON.stringify(s)}catch(e){i+=JSON.stringify(new Date)}return Object.keys(n).length&&(i+=JSON.stringify(n)),i}_overrideHttpRequest(){window.xhrOverride=!0;const e=[];XMLHttpRequest.noIntercept=!1;const t=XMLHttpRequest.prototype.open;XMLHttpRequest.prototype.open=function(e,s){return this._method=e,this._url=s,t.apply(this,arguments)};const s=XMLHttpRequest.prototype.setRequestHeader;XMLHttpRequest.prototype.setRequestHeader=function(e,t){const n=s.apply(this,arguments);return this._headers||(this._headers={}),this._headers[e]||(this._headers[e]=[]),this._headers[e].push(t),n};const n=XMLHttpRequest.prototype.send;XMLHttpRequest.prototype.send=function(t){const s=window.jsu.getHashFromRequest(this._method,this._url,t,this._headers);if(e.includes(s)){const e="Duplicated request aborted";return Object.defineProperty(this,"statusText",{value:e,writable:!1}),this.dispatchEvent(new CustomEvent("error",{detail:{error:e,message:e}}))}return e.push(s),this.noIntercept||this.addEventListener&&this.addEventListener("readystatechange",function(){this.readyState===XMLHttpRequest.DONE&&e.splice(e.indexOf(s),1)},!1),n.apply(this,arguments)}}}export class ChunkedUpload{constructor(e){const t=["file","uploadURL","completeURL"],s={debugMode:null,extraHeaders:{},extraData:{},maxRetry:30,retryDelay:1e4,chunkSize:2e7,fileNameSuffix:"",progressCallback:null,retryCallback:null,successCallback:null,failureCallback:null,inTest:!1};for(const s of t){if(!e[s])throw new Error('A mandatory argument is missing: "'+s+'".');this[s]=e[s]}for(const t in s)this[t]=void 0!==e[t]?e[t]:s[t];null===this.debugMode&&-1!==window.location.hash.indexOf("debug")&&(this.debugMode=!0),this.sendFile()}logDebug(){this.debugMode&&!this.inTest&&console.log.apply(null,arguments)}logError(){this.inTest||console.error.apply(null,arguments)}logWarn(){this.inTest||console.warn.apply(null,arguments)}onProgress(e){this.progressCallback&&this.progressCallback(e)}onRetry(e,t){let s;if(this.retryCallback&&(s=this.retryCallback(e)),void 0===s){const e=this;s=new Promise(function(t){e.logDebug("Retrying in "+e.retryDelay+" ms..."),setTimeout(t,e.retryDelay)})}s.then(t)}onSuccess(){this.successCallback&&this.successCallback(this.uploadId)}onFailure(e){this.failureCallback&&this.failureCallback(e)}sendFile(){if(this.onProgress(0),this.uploadId=null,this.fileName=this.file.name,this.fileNameSuffix){const e=this.fileName.lastIndexOf(".");this.fileName=e>0?this.fileName.substring(0,e)+this.fileNameSuffix+this.fileName.substring(e):"file"+this.fileNameSuffix+".tmp"}this.logDebug("Number of chunk to send:",Math.ceil(this.file.size/this.chunkSize),this.chunkSize,this.file.size),this.sendNextChunk(0,0)}sendNextChunk(e,t){this.logDebug("Sending chunk:","start:",e,"total size:",this.file.size,"retries:",t);const s=Math.min(e+this.chunkSize,this.file.size),n=new FormData;n.append("file",this.file.slice(e,s),this.fileName),n.append("retries",t),this.uploadId&&n.append("upload_id",this.uploadId);for(const e in this.extraData)n.append(e,this.extraData[e]);const i=(s-e)/this.file.size,r=Object.assign(this.extraHeaders,{"Content-Range":"bytes "+e+"-"+(s-1)+"/"+this.file.size});this.logDebug("Content-Range",r["Content-Range"]);const o=this;jsu.httpRequest({method:"POST",url:this.uploadURL,headers:r,data:n,json:!0,progress:function(t){if(t.lengthComputable){let s=e/o.file.size;t.total&&(s+=i*(t.loaded/t.total)),s=Math.floor(95*s),o.logDebug("Progress:",s,i,t.loaded,t.total),o.onProgress(s)}},callback:function(n,i){if(200==n.status&&i.upload_id){o.logDebug("Chunk sent",i),o.uploadId=i.upload_id;const e=s;e>=o.file.size?o.completeUpload(0):o.sendNextChunk(e,0)}else o.logError("Failed to send chunk:",i),t