diff --git a/minify.sh b/minify.sh new file mode 100755 index 0000000..d63362f --- /dev/null +++ b/minify.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +# Build minified copies of uproot's own scripts. uproot serves X.min.js instead +# of X.js only while the SHA-256 in its first line matches X.js (see minified() +# in src/uproot/pages.py). Otherwise, it serves X.js. + +set -euo pipefail + +cd "$(dirname "$0")/src/uproot/_static" + +source="uproot.js" + +build="${source%.js}.min.js" +sha256="$(python3 -c 'import hashlib, sys; print(hashlib.file_digest(open(sys.argv[1], "rb"), "sha256").hexdigest())' "$source")" + +{ + echo "/* Minified from source with SHA-256 $sha256 */" + npx --yes terser@5 "$source" --compress --mangle --ecma 2020 +} > "$build.tmp" + +mv "$build.tmp" "$build" diff --git a/pyproject.toml b/pyproject.toml index 89a45a7..6f0e255 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -39,6 +39,7 @@ dependencies = [ "python-multipart>=0.0.32,<0.1", "setproctitle>=1.3.7,<2", "sortedcontainers>=2.4.0,<3", + "starlette-compress>=1.8.0,<2", "uvicorn>=0.49.0,<1", "watchdog>=6.0.0,<7", "websockets>=16.0,<18", diff --git a/release.sh b/release.sh index 2f5811b..37254a8 100755 --- a/release.sh +++ b/release.sh @@ -5,6 +5,9 @@ set -euxo pipefail # Sync dependencies uv sync --extra dev --upgrade +# Build minified scripts +./minify.sh + # Run formatters and autofixes uv run ruff check --fix src/uproot/ uv run black src/uproot/ diff --git a/src/uproot/_static/favicon.svg b/src/uproot/_static/favicon.svg index bbfbedb..97907d4 100644 --- a/src/uproot/_static/favicon.svg +++ b/src/uproot/_static/favicon.svg @@ -1,33 +1 @@ - - - - - - - - U - - +U diff --git a/src/uproot/_static/uproot.js b/src/uproot/_static/uproot.js index 934a146..bdd1e7f 100644 --- a/src/uproot/_static/uproot.js +++ b/src/uproot/_static/uproot.js @@ -102,9 +102,11 @@ window.uproot = { dirty: false, form: null, futStore: {}, + futureCounter: 0, I: (id_) => document.getElementById(id_), isInitialized: false, keepAliveInterval: null, + keepAliveSeconds: null, key: null, lastTimeoutLevel: null, missing: new Set(), @@ -301,7 +303,7 @@ window.uproot = { api(endpoint, data = null) { return new Promise((resolve, reject) => { - const futid = this.uuid(); + const futid = ++this.futureCounter; // short ids save bandwidth this.futStore[futid] = { resolve, reject }; const message = JSON.stringify({ @@ -479,12 +481,19 @@ window.uproot = { }, wsstart() { + if (!(this.keepAliveSeconds > 0)) { + throw new Error("uproot.keepAliveSeconds must be set before wsstart()"); + } + this.ws = new RobustWebSocket(this.wsurl(), { onOpen: (ws) => { if (this.keepAliveInterval !== null) { window.clearInterval(this.keepAliveInterval); } - this.keepAliveInterval = window.setInterval(this.hello, 9000); + this.keepAliveInterval = window.setInterval( + this.hello, + 1000 * this.keepAliveSeconds, + ); if (this.isInitialized) { this.replaySubscriptions(); diff --git a/src/uproot/_static/uproot.min.js b/src/uproot/_static/uproot.min.js new file mode 100644 index 0000000..249b2f3 --- /dev/null +++ b/src/uproot/_static/uproot.min.js @@ -0,0 +1,2 @@ +/* Minified from source with SHA-256 44eb3304c686923d30bf44e1579600b7ab32f93beef5d087e702b7f06416a85f */ +class RobustWebSocket{constructor(e,t={}){this.url=e,this.options=t,this.ws=null,this.messageQueue=[],this.reconnectInterval=t.reconnectInterval||1e3,this.isConnected=!1,this.shouldReconnect=!0,this.connect()}connect(){try{this.ws=new WebSocket(this.url),this.ws.onopen=()=>{this.isConnected=!0,this.processQueue(),this.options.onOpen?.(this)},this.ws.onmessage=e=>{this.options.onMessage?.(e,this)},this.ws.onclose=e=>{this.isConnected=!1,this.options.onClose?.(e,this),this.shouldReconnect&&this.scheduleReconnect()},this.ws.onerror=e=>{this.isConnected=!1,this.options.onError?.(e,this)}}catch(e){this.scheduleReconnect()}}scheduleReconnect(){setTimeout(()=>{this.connect()},this.reconnectInterval)}async send(e){const t={message:e,timestamp:Date.now()};return this.messageQueue.push(t),this.isConnected&&this.ws.readyState===WebSocket.OPEN&&this.processQueue(),t}processQueue(){if(this.isConnected&&this.ws.readyState===WebSocket.OPEN)for(;this.messageQueue.length>0;){const e=this.messageQueue.shift();try{this.ws.send(e.message),this.options.onMessageSent?.(e,this)}catch(t){this.messageQueue.unshift(e),this.options.onSendError?.(t,e,this);break}}}close(){this.shouldReconnect=!1,this.ws?.close()}getQueueLength(){return this.messageQueue.length}clearQueue(){this.messageQueue=[]}}window.uproot={_domContentLoaded:!1,dirty:!1,form:null,futStore:{},futureCounter:0,I:e=>document.getElementById(e),isInitialized:!1,keepAliveInterval:null,keepAliveSeconds:null,key:null,lastTimeoutLevel:null,missing:new Set,receive:null,root:null,serverThere:null,sname:null,subscriptions:new Map,terms:{},testing:!1,pageTimeoutDangerSeconds:15,pageTimeoutDurationMs:0,pageTimeoutStartMs:null,pageTimeoutSubmitTimer:null,pageTimeoutTickTimer:null,pageTimeoutWarningSeconds:60,uname:null,vars:{},verbose:!1,ws:null,aer1945(){document.location="https://www.econlib.org/library/Essays/hykKnw.html"},setPageTimeout(e){const t=Number(e);if(!Number.isFinite(t))throw new TypeError(`Invalid page timeout: ${e}`);if(this.stopPageTimeoutTimers(),t<=0)return this.pageTimeoutStartMs=null,this.pageTimeoutDurationMs=0,this.syncTimeout(),void this.submit();this.pageTimeoutStartMs=performance.now(),this.pageTimeoutDurationMs=Math.max(0,1e3*t);const n=this.syncTimeout();this.pageTimeoutSubmitTimer=window.setTimeout(()=>this.submit(),this.pageTimeoutDurationMs),this.pageTimeoutTickTimer=window.setInterval(()=>this.syncTimeout(),1e3),window.dispatchEvent(new CustomEvent("UprootInternalPageTimeoutSet",{detail:n}))},stopPageTimeoutTimers(){null!==this.pageTimeoutSubmitTimer&&(window.clearTimeout(this.pageTimeoutSubmitTimer),this.pageTimeoutSubmitTimer=null),null!==this.pageTimeoutTickTimer&&(window.clearInterval(this.pageTimeoutTickTimer),this.pageTimeoutTickTimer=null)},pageTimeoutRemainingMs(){return null===this.pageTimeoutStartMs?0:Math.max(0,this.pageTimeoutDurationMs-(performance.now()-this.pageTimeoutStartMs))},syncTimeout(){const e=null!==this.pageTimeoutStartMs,t=this.pageTimeoutRemainingMs()/1e3;let n="light";e&&t<=this.pageTimeoutDangerSeconds?n="danger":e&&t<=this.pageTimeoutWarningSeconds&&(n="warning");const o={active:e,level:n,text:e?this.formatDuration(t):"__:__",compact:e?this.formatDurationCompact(t):"__:__"},s=document.querySelectorAll("[data-uproot-timeout-box]");if(s.forEach(t=>{t.hidden=!e,t.classList.remove("uproot-timeout-light","uproot-timeout-warning","uproot-timeout-danger"),e&&t.classList.add(`uproot-timeout-${n}`)}),document.querySelectorAll("[data-uproot-timeout]").forEach(e=>{e.textContent=o.compact}),e&&n!==this.lastTimeoutLevel&&("warning"===n||"danger"===n)){const e=document.getElementById("uproot-timeout-announcer"),t=s[0]?.getAttribute("aria-label");e&&(e.textContent=t?`${t}: ${o.text}`:o.text)}return this.lastTimeoutLevel=e?n:null,window.dispatchEvent(new CustomEvent("UprootInternalPageTimeout",{detail:o})),o},formatDuration(e){const t=Math.max(0,Math.ceil(e)),n=Math.floor(t/86400),o=Math.floor(t%86400/3600),s=Math.floor(t%3600/60),r=t%60,a=[];return n>0&&a.push(_(1===n?"#x# day":"#x# days").replace("#x#",n)),o>0&&a.push(_(1===o?"#x# hour":"#x# hours").replace("#x#",o)),s>0&&a.push(_(1===s?"#x# minute":"#x# minutes").replace("#x#",s)),(r>0||0===a.length)&&a.push(_(1===r?"#x# second":"#x# seconds").replace("#x#",r)),a.length>1?a.slice(0,-1).join(_(", "))+_(" and ")+a[a.length-1]:a[0]},formatDurationCompact(e){const t=Math.max(0,Math.ceil(e));let n=Math.floor(t/86400),o=Math.floor(t%86400/3600),s=Math.floor(t%3600/60),r=t%60;const a=e=>String(e).padStart(2,"0");return t>=86400?`${n}:${a(o)}:${a(s)}:${a(r)} ${_("days")}`:t>=3600?(o=Math.floor(t/3600),s=Math.floor(t%3600/60),r=t%60,`${o}:${a(s)}:${a(r)} ${_("hours")}`):t>=60?(s=Math.floor(t/60),r=t%60,`${s}:${a(r)} ${_("minutes")}`):`${t} ${t<=1?_("second"):_("seconds")}`},fmtnum(e,{pre:t="",post:n="",places:o=2,use_nbsp:s=!0,sep:r=",",decsep:a="."}={}){const i=Number(e);if(!Number.isFinite(i))return`${t}${e}${n}`;const[l,c="0"]=String(Math.abs(i)).split("e"),[d,u=""]=l.split(".");let m=d+u,h=d.length+Number(c);h<=0&&(m="0".repeat(1-h)+m,h=1),m=m.padEnd(h+o+1,"0");let p=BigInt(m.slice(0,h+o));m[h+o]>="5"&&(p+=1n);const g=p.toString().padStart(o+1,"0");let v=g.slice(0,g.length-o);const w=g.slice(g.length-o);r&&(v=v.replace(/\B(?=(\d{3})+(?!\d))/g,r));let f=`${t}${v}${o>0?a+w:""}${n}`;return i<0&&p>0n&&(f="−"+f),s?f.replaceAll(" "," "):f},getParam:e=>new URLSearchParams(location.search).get(e),api(e,t=null){return new Promise((n,o)=>{const s=++this.futureCounter;this.futStore[s]={resolve:n,reject:o};const r=JSON.stringify({endpoint:e,payload:t,future:s});this.ws.send(r)})},api2(e,t={}){if(null===this.root||!this.sname||!this.uname||!this.key)throw new Error("uproot must be initialized before api2() is used");const n=new Headers(t.headers);n.set("X-Uproot-CSRF",this.csrf()),n.set("X-Uproot-Player",this.uname);const o=encodeURIComponent(e),s=encodeURIComponent(this.sname);return fetch(`${this.root}/api2/${o}/${s}/`,{...t,credentials:t.credentials||"same-origin",headers:n})},invoke(e,...t){const n=t[t.length-1],o=t.length>0&&null!=n&&"object"==typeof n&&!Array.isArray(n),s=o?t.slice(0,-1):t,r=o?n:{};return uproot.api("invoke",{mname:e,args:s,kwargs:r})},subscribe(e,...t){const n=JSON.stringify([e,t]);this.subscriptions.set(n,{mname:e,args:t}),this.sendSubscription(e,t)},sendSubscription(e,t){const n=JSON.stringify({endpoint:"invoke",payload:{mname:e,args:t,kwargs:{}}});this.ws.send(n)},replaySubscriptions(){for(const{mname:e,args:t}of this.subscriptions.values())this.sendSubscription(e,t)},queueDispatch(e,t){if(void 0!==t.event)if(t.event.startsWith("_uproot")){const e=t.event.substring(8),n=new CustomEvent(`UprootInternal${e}`,{detail:t});window.dispatchEvent(n)}else{const e=t.event,n=new CustomEvent(`UprootCustom${e}`,{detail:t});window.dispatchEvent(n)}else{const e=new CustomEvent("UprootQueue",{detail:t});window.dispatchEvent(e)}},fromServer(e,t){const n=e=>{const t=Object.assign({received:Date.now()},JSON.parse(e)),n=t.kind,o=t.payload,s=this.vars?._uproot_internal?.thisis||null;if(void 0!==n&&void 0!==o)if(this.verbose&&console.log(t),this.serverThere=Date.now(),"invoke"===n&&"future"in o){const e=this.futStore[o.future];o.error?e.reject(new Error("Server-side Exception occurred")):e.resolve(o.data),delete this.futStore[o.future]}else if("action"===n&&"action"in o){const e=o.action;"reload"===e?this.reload():"redirect"===e&&"url"in o?this.redirect(o.url):"submit"===e&&this.submit()}else if("event"===n&&"event"in o){const e=o.event,t=new CustomEvent(`UprootCustom${e}`,{detail:o.detail});window.dispatchEvent(t)}else"queue"===n&&("constraint"in o.entry&&null!==o.entry.constraint&&o.entry.constraint!==s||this.queueDispatch(o.u,o.entry))};"string"==typeof e.data?n(e.data):e.data.text().then(n).catch(()=>{})},isValidToken:e=>"string"==typeof e&&/^[a-zA-Z0-9._-]+$/.test(e),hello:()=>window.uproot.api("hello"),wsurl(){return`${"https:"===window.location.protocol?"wss:":"ws:"}//${window.location.host}${this.root}/ws/${this.sname}/${this.uname}/`},wsstart(){if(!(this.keepAliveSeconds>0))throw new Error("uproot.keepAliveSeconds must be set before wsstart()");this.ws=new RobustWebSocket(this.wsurl(),{onOpen:e=>{null!==this.keepAliveInterval&&window.clearInterval(this.keepAliveInterval),this.keepAliveInterval=window.setInterval(this.hello,1e3*this.keepAliveSeconds),this.isInitialized&&this.replaySubscriptions(),this.hello().then(()=>{this.isInitialized?window.dispatchEvent(new Event("UprootReconnect")):(this.isInitialized=!0,window.dispatchEvent(new Event("UprootReady")))})},onMessage:(e,t)=>{this.fromServer(e,t)},onClose:(e,t)=>{window.dispatchEvent(new Event("UprootDisconnect"))}})},csrf(){return`${this.sname}+${this.uname}+${this.key}`},init(){this.sname=uproot.vars._uproot_internal.sname,this.uname=uproot.vars._uproot_internal.uname,this.root=uproot.vars._uproot_internal.root,this.key=uproot.vars._uproot_internal.key,this.form=document.forms[0],this.I("_uproot_from").value=uproot.vars._uproot_internal.thisis,this.I("_uproot_csrf").value=this.csrf()},testingButton(e){this.testing?(document.querySelectorAll(".uproot-testing-only").forEach(e=>{e.style.display="none"}),e.innerText=_("Show testing elements")):(document.querySelectorAll(".uproot-testing-only").forEach(e=>{e.style.display="block"}),e.innerText=_("Hide testing elements")),this.testing=!this.testing},uuid:()=>"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,e=>{const t=16*Math.random()|0;return("x"===e?t:3&t|8).toString(16)}),reload(){document.location=document.location},redirect(e){(e.startsWith("http://")||e.startsWith("https://"))&&(document.location=e)},beginSubmit(e){return"true"!==e.dataset.uprootSubmitting&&(e.dataset.uprootSubmitting="true",this.stopPageTimeoutTimers(),!0)},submitForm(e){return!!this.beginSubmit(e)&&(HTMLFormElement.prototype.submit.call(e),!0)},submit(e=null){const t=uproot.vars?._uproot_internal?.thisis;return(null===e||t===e)&&this.submitForm(I("uproot-form"))},goBack(){return this.I("_uproot_from").value="back-"+uproot.vars._uproot_internal.thisis,this.submit()},onStart(e){this._domContentLoaded?e():window.addEventListener("DOMContentLoaded",e)},onReady(e){this.isInitialized?e():window.addEventListener("UprootReady",e)},onReconnect(e){window.addEventListener("UprootReconnect",e)},onDisconnect(e){window.addEventListener("UprootDisconnect",e)},onCustomEvent(e,t){window.addEventListener(`UprootCustom${e}`,t)},onInternalEvent(e,t){window.addEventListener(`UprootInternal${e}`,t)},onQueue(e){window.addEventListener("UprootQueue",e)},selectedValue(e){const t=document.querySelector(`input[name="${e}"]:checked`);if(t)return t.value;const n=document.querySelector(`select[name="${e}"]`);return n?n.value:null},error(e){const t=Array.from(document.querySelectorAll(".modal.show")).map(e=>{const t=bootstrap.Modal.getOrCreateInstance(e);return t.hide(),t});let n=bootstrap.Modal.getOrCreateInstance(this.I("error-modal"),{backdrop:"static"});this.I("error-modal-body").innerHTML=e,n.show(),n._backdrop._element.style.backgroundColor="red",this.I("error-modal").addEventListener("hidden.bs.modal",()=>{t.forEach(e=>e.show())},{once:!0})},skipFromTesting(){const e=parseInt(this.selectedValue("uproot-skip"));this.api("skip",e).then(this.reload)},alert(e){const t=Array.from(document.querySelectorAll(".modal.show")).map(e=>{const t=bootstrap.Modal.getOrCreateInstance(e);return t.hide(),t}),n=this.I("alert-modal"),o=bootstrap.Modal.getInstance(n);o&&!n.classList.contains("show")&&o.dispose();let s=bootstrap.Modal.getOrCreateInstance(n,{backdrop:!0});this.I("alert-modal-body").innerHTML=e,s.show(),this.I("alert-modal").addEventListener("hidden.bs.modal",()=>{t.forEach(e=>e.show())},{once:!0})},prompt(e,t=""){return new Promise(n=>{const o=Array.from(document.querySelectorAll(".modal.show")).map(e=>{const t=bootstrap.Modal.getOrCreateInstance(e);return t.hide(),t});let s=bootstrap.Modal.getOrCreateInstance(this.I("alert-modal"),{backdrop:"static"});const r="uproot-prompt-input-"+this.uuid();this.I("alert-modal-body").innerHTML=`${e}`;const a=this.I(r),i=this.I("alert-modal").querySelector(".modal-footer button");i&&(i.className="btn btn-uproot",i.textContent=_("OK"),i.removeAttribute("data-bs-dismiss"));const l=e=>{s.hide(),n(e)},c=()=>{const e=a.value;l(e)},d=e=>{if("Enter"===e.key){e.preventDefault();const t=a.value;l(t)}else"Escape"===e.key&&(e.preventDefault(),l(null))};a.addEventListener("keydown",d),i&&i.addEventListener("click",c),I(r).value=t,s.show(),this.I("alert-modal").addEventListener("shown.bs.modal",()=>{a.focus()},{once:!0}),this.I("alert-modal").addEventListener("hidden.bs.modal",()=>{a.removeEventListener("keydown",d),i&&(i.removeEventListener("click",c),i.className="btn btn-outline-uproot",i.textContent=_("Close"),i.setAttribute("data-bs-dismiss","modal")),o.forEach(e=>e.show())},{once:!0})})},sum:e=>e.reduce((e,t)=>e+t,0),mean(e){return this.sum(e)/e.length},sd(e){const t=this.mean(e),n=e.reduce((e,n)=>e+Math.pow(n-t,2),0)/(e.length-1);return Math.sqrt(n)},sort(e,t=null){if("function"==typeof t&&t.length>=2)return e.sort(t);const n=null===t?e=>e:t;return e.sort((e,t)=>String(n(e)).localeCompare(String(n(t)),void 0,{numeric:!0,sensitivity:"base"}))},escape(e){const t=document.createElement("span");return t.textContent=e,t.innerHTML},looseJSONtoJSON(input){const obj=eval("("+input+")");return JSON.stringify(obj)},ensureBuddy(){this.I("uproot-buddy").hidden=!1},adminMessage(e){const t=this.I("adminmessage-modal");if(void 0===e&&t.classList.contains("show"))return;const n=Array.from(document.querySelectorAll(".modal.show")).map(e=>{const t=bootstrap.Modal.getOrCreateInstance(e);return t.hide(),t});let o=bootstrap.Modal.getOrCreateInstance(t,{backdrop:"static"});if(void 0!==e){const t=this.I("adminmessage-modal-body");if(t.querySelector(".uproot-chat")){let n=t.querySelector(".uproot-adminmessage-notice");n||(n=document.createElement("div"),n.className="alert alert-warning uproot-adminmessage-notice",t.prepend(n)),n.innerText=e}else t.innerText=e}o.show(),t.addEventListener("hidden.bs.modal",()=>{n.forEach(e=>e.show())},{once:!0}),this.ensureBuddy()},slidersWithoutAnchoring(){document.querySelectorAll("input[type='range'].without-anchoring").forEach(e=>{const t=e.name;e.removeAttribute("name");const n=document.createElement("input");n.type="hidden",n.name=t,n.value="",e.insertAdjacentElement("afterend",n),e.dataset.interacted="false";const o=(t,n)=>{const o=parseFloat(e.min)||0,s=parseFloat(e.max)||100;let r=parseFloat(e.step);if((isNaN(r)||r<=0||"any"===e.step)&&(r=1),s<=o)return o;if(n.width<=0)return o;const a=e=>{const t=String(e),n=t.match(/e-(\d+)$/);return n?Number(n[1]):t.includes(".")?t.split(".")[1].length:0},i=10**Math.max(a(o),a(s),a(r)),l=e=>Math.round((e+Number.EPSILON)*i)/i,c=o+Math.max(0,Math.min(1,t/n.width))*(s-o),d=isNaN(parseFloat(e.min))?0:o;let u=l(d+Math.round((c-d)/r)*r);u=Math.max(o,Math.min(s,u));const m=l(d+Math.round((u-d)/r)*r);return Math.max(o,Math.min(s,m))},s=e.getBoundingClientRect(),r=window.getComputedStyle(e);let a=s.height>0?`${s.height}px`:r.height;a&&"0px"!==a&&"auto"!==a||(a="1.5rem");const i=document.createElement("div");i.style.cssText=`\n position: relative;\n display: inline-block;\n width: 100%;\n height: ${a};\n `,e.parentNode.insertBefore(i,e),i.appendChild(e);const l=document.createElement("div");l.classList.add("without-anchoring-overlay"),l.setAttribute("tabindex","0"),l.setAttribute("role","button");const c=e.labels?.[0];c&&l.setAttribute("aria-label",c.textContent.trim());const d=e.getAttribute("aria-describedby");d&&l.setAttribute("aria-describedby",d);const u=t=>{e.value=t,e.dataset.interacted="true",n.value=t,l.remove(),e.style.display="block",e.focus(),e.dispatchEvent(new Event("input",{bubbles:!0})),e.dispatchEvent(new Event("change",{bubbles:!0}))};e.style.display="none",e.classList.remove("w-50"),l.addEventListener("click",e=>{const t=l.getBoundingClientRect(),n=e.clientX-t.left;u(o(n,t))}),l.addEventListener("keydown",e=>{if("Enter"===e.key||" "===e.key){e.preventDefault();const t=l.getBoundingClientRect();u(o(t.width/2,t))}}),i.appendChild(l);const m=()=>{e.dataset.interacted="true",n.value=e.value};e.addEventListener("input",m),e.addEventListener("change",m)})},paginateBlocks(){document.querySelectorAll(".uproot-pagination").forEach(e=>{const t=parseInt(e.dataset.itemsPerPage)||8,n=Array.from(e.querySelectorAll(":scope > .uproot-pagination-item"));if(n.length<=t)return;const o=Math.ceil(n.length/t);let s=1;const r=document.createElement("nav");r.setAttribute("aria-label","Pagination");const a=document.createElement("ul");a.className="pagination pagination-sm mb-3",r.appendChild(a),e.insertBefore(r,e.firstChild);const i=e=>{s=e;const o=(e-1)*t,r=o+t;n.forEach((e,t)=>{e.classList.toggle("d-none",t=r)}),l()},l=()=>{a.innerHTML="";const e=document.createElement("li");e.className="page-item"+(1===s?" disabled":"");const r=document.createElement("a");r.className="page-link",r.href="#",r.setAttribute("aria-label","Previous"),r.innerHTML="«",r.addEventListener("click",e=>{e.preventDefault(),s>1&&i(s-1)}),e.appendChild(r),a.appendChild(e);const l=[];if(o<=8)for(let e=1;e<=o;e++)l.push(e);else{const e=6;let t=Math.max(2,s-Math.floor((e-1)/2)),n=t+e-1;n>o-1&&(n=o-1,t=n-e+1),l.push(1),t>2&&l.push("⋯");for(let e=t;e<=n;e++)l.push(e);n{const o=document.createElement("li");if("⋯"===e){o.className="page-item disabled";const e=document.createElement("span");e.className="page-link",e.textContent="⋯",o.appendChild(e)}else{const r=e===s;o.className="page-item"+(r?" active":"");const a=document.createElement("a");a.className="page-link",a.href="#",a.textContent=n[(e-1)*t].dataset.paginationLabel||e,r&&a.setAttribute("aria-current","page"),a.addEventListener("click",t=>{t.preventDefault(),i(e)}),o.appendChild(a)}a.appendChild(o)});const c=document.createElement("li");c.className="page-item"+(s===o?" disabled":"");const d=document.createElement("a");d.className="page-link",d.href="#",d.setAttribute("aria-label","Next"),d.innerHTML="»",d.addEventListener("click",e=>{e.preventDefault(),se<0?"−"+-e:`${e}`,formatForInterface(e,t){const n=window[`${e}Formatter`];return"function"==typeof n?n(t):this.defaultFormatter(t)},slidersWithPopovers(){const e=parseFloat(getComputedStyle(document.documentElement).fontSize);document.querySelectorAll(".uproot-slider-popover-wrapper").forEach(t=>{const n=t.querySelector("input[type='range']");if(!n)return;const o=t.querySelector(".uproot-slider-popover-container");if(!o)return;const s=n.name||n.id,r=t.parentElement,a=r?.querySelector(".uproot-slider-label-min"),i=r?.querySelector(".uproot-slider-label-max");a?.dataset.raw&&(a.textContent=this.formatForInterface(s,Number(a.dataset.raw))),i?.dataset.raw&&(i.textContent=this.formatForInterface(s,Number(i.dataset.raw)));let l=!1,c=null;const d=`popover-content-${n.id}`;c=new bootstrap.Popover(o,{animation:!1,content:``,html:!0,offset:[0,-2.75*e],placement:"top",trigger:"manual"});const u=()=>{const t=""!==n.min?Number(n.min):0,s=""!==n.max?Number(n.max):100,r=Number(n.value),a=(r-t)/(s-t),i=n.getBoundingClientRect().width,u=e/2,m=u+a*(i-2*u);o.style.marginLeft=`${m}px`;const h=this.formatForInterface(n.name||n.id,r);l?c.update():(c.show(),l=!0);const p=document.getElementById(d);p&&(p.textContent=h)};n.addEventListener("input",u),window.addEventListener("resize",()=>{l&&u()})})},nonRequiredRadios(){const e="radio-toggle-clear-hover",t="toggleClearBound",n="preChecked",o=_("Click again to unselect.");document.documentElement.classList.add("radio-toggle-enabled");const s=e=>{e.checked=!1,e.dispatchEvent(new Event("change",{bubbles:!0}))};document.querySelectorAll("input[type='radio'][data-uproot-radio]:not([required])").forEach(r=>{if("1"===r.dataset[t])return;r.dataset[t]="1";const a=r.closest(".form-check"),i=((e,t)=>{if(e.id){const n=CSS.escape(e.id),o=t?.querySelector(`label.form-check-label[for="${n}"]`);if(o)return o;const s=document.querySelector(`label[for="${n}"]`);if(s)return s}return e.closest("label")})(r,a);a&&((t,n)=>{n.addEventListener("mouseenter",()=>{t.checked&&(n.classList.add(e),n.title=o)}),n.addEventListener("mouseleave",()=>{n.classList.remove(e),n.removeAttribute("title")}),t.addEventListener("change",()=>{n.classList.remove(e),n.removeAttribute("title")})})(r,a),i&&((e,t)=>{t.addEventListener("pointerdown",()=>{e.dataset[n]=e.checked?"1":"0"},!0),t.addEventListener("click",t=>{const o="1"===e.dataset[n];e.dataset[n]="0",o&&(t.preventDefault(),t.stopPropagation(),s(e))},!0)})(r,i),(e=>{e.addEventListener("pointerdown",t=>{e.dataset[n]=e.checked?"1":"0",e.checked&&(t.preventDefault(),t.stopPropagation(),s(e))},!0),e.addEventListener("click",t=>{"1"===e.dataset[n]&&(t.preventDefault(),t.stopPropagation()),e.dataset[n]="0"},!0),e.addEventListener("keydown",t=>{" "!==t.key&&"Spacebar"!==t.key||!e.checked||(t.preventDefault(),s(e))})})(r)})},boundedChoiceFields(){document.querySelectorAll(".uproot-bounded-choice").forEach(e=>{const t=e.dataset.boundedMin,n=e.dataset.boundedMax,o=(""!==t&&parseInt(t,10),""!==n?parseInt(n,10):null),s=e.querySelectorAll("input[type='checkbox']"),r=()=>{const e=Array.from(s).filter(e=>e.checked).length;null!==o&&e>=o?s.forEach(e=>{e.checked||(e.disabled=!0)}):s.forEach(e=>{e.disabled=!1})};s.forEach(e=>{e.addEventListener("change",r)}),r()})},enableConnectionLostModal(){let e=null,t=null,n=null,o=!1;window.addEventListener("beforeunload",()=>{o=!0});const s=()=>{if(o)return;const t=(e||(e=bootstrap.Modal.getOrCreateInstance(I("connection-timeout-modal"),{backdrop:"static",keyboard:!1})),e),n=I("connection-modal-reload-btn");n&&(n.hidden=this.dirty),t.show(),t._backdrop._element.style.backgroundColor="red"};n=setTimeout(()=>{this.isInitialized||s()},1e4);const r=this.hello.bind(this);this.hello=function(){const e=Date.now();return clearTimeout(t),t=setTimeout(()=>{(!uproot.serverThere||uproot.serverThere{o||s()})},chat:{messageStore:{},ignoreChatted:!0,normalizeOptions:(e=!0)=>"boolean"==typeof e?{canReply:e}:{canReply:e.canReply??!0},composerHtml:e=>`\n
\n \n \n \n
\n `,bindComposer(e){const t=document.querySelector(`#message-input-chat-${e}`);t&&"1"!==t.dataset.uprootBound&&(t.dataset.uprootBound="1",t.addEventListener("keydown",t=>this.handleChatKeydown(t,e)))},setReplyState(e,t){const n=document.querySelector(`#chat-${e}`);if(!n)return;n.dataset.canReply=t?"1":"0";const o=n.querySelector(".uproot-chat-composer");t&&!o?(n.insertAdjacentHTML("beforeend",this.composerHtml(e)),this.bindComposer(e)):!t&&o&&o.remove()},handleChatKeydown(e,t){"Enter"!==e.key||e.shiftKey||(e.preventDefault(),this.sendMessage(t))},create(e,t,n=!0){t=uproot.escape(t);const{canReply:o}=this.normalizeOptions(n),s=o?this.composerHtml(t):"";e.innerHTML=``;const r=e.children[0];r.dataset.canReply=o?"1":"0",this.bindComposer(t),this.messageStore[t]||(this.messageStore[t]=new Set),uproot.api("chat_get",t).then(uproot.chat.messagesFromServer).then(()=>{r.classList.remove("hidden"),r.children[0].scroll(0,1e6)})},addMessage(e,t,n,o,s,r="text-primary"){this.messageStore[e]||(this.messageStore[e]=new Set);const a=uproot.escape(n),i=uproot.escape(o);if(this.messageStore[e].has(t))return;this.messageStore[e].add(t);const l=document.querySelector(`#messages-chat-${e}`);if(!l)return void console.error(`Chat messages list not found for chatId: ${e}`);const c=new Date(1e3*s),d=c.toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"}),u=c.toISOString(),m=document.createElement("li");m.className="px-3 py-2 message-hover",m.innerHTML=`\n
\n ${a}\n \n
\n
${i}
\n `,l.appendChild(m),l.parentElement&&(l.parentElement.scrollTop=l.parentElement.scrollHeight)},sendMessage(e){const t=document.querySelector(`#message-input-chat-${e}`);if(!t)return void console.error(`Chat input not found for chatId: ${e}`);const n=t.value.trim();n&&(t.value="",window.uproot.api("chat_add",[e,n]))},messagesFromServer(e){e&&e.forEach(e=>{if(e.cname&&I(`chat-${e.cname}`)){let t=e.sender[1],n="text-success";"self"===e.sender[0]?(t=`${e.sender[1]} (${_("You")})`,n="text-primary"):"admin"===e.sender[0]&&(t=_("Research Coordinator"),n="text-danger"),window.uproot.chat.addMessage(e.cname,e.id,t,e.text,e.time,n)}})}}},"loading"===document.readyState?window.addEventListener("DOMContentLoaded",()=>{uproot._domContentLoaded=!0}):uproot._domContentLoaded=!0,window._=e=>{if(e in window.uproot.terms)return window.uproot.terms[e];const t=e.replace(/\s+/g," ").trim();return t in window.uproot.terms?window.uproot.terms[t]:(window.uproot.missing.add(e),window.uproot.verbose&&console.log(`Missing translation for: "${e}"`),e)},window.alert=e=>{window.uproot.alert(uproot.escape(e))},window.prompt=e=>window.uproot.prompt(uproot.escape(e)),window.I=e=>document.getElementById(e),uproot.onInternalEvent("Received",e=>{const t=e.detail;if(null===uproot.receive)throw new Error(`Please define uproot.receive(). Ignored data received from server: ${JSON.stringify(t.data)}`);uproot.receive(t.data)}),uproot.onInternalEvent("Chatted",e=>{if(!window.uproot.chat.ignoreChatted){const t=e.detail.data;if(t.sender&&"admin"===t.sender[0]){if(!I(`chat-${t.cname}`))return uproot.chat.create(I("adminmessage-modal-body"),t.cname,{canReply:!!t.can_reply}),uproot.ensureBuddy(),void uproot.adminMessage();uproot.chat.setReplyState(t.cname,!!t.can_reply),uproot.ensureBuddy(),uproot.adminMessage()}window.uproot.chat.messagesFromServer([t])}}),uproot.onInternalEvent("AdminMessaged",e=>{const t=e.detail;uproot.adminMessage(t.data)}),uproot.onInternalEvent("AdminTimeoutChanged",e=>{uproot.setPageTimeout(e.detail.data)}),uproot.onInternalEvent("AdminchatStateChanged",e=>{const t=e.detail.data;if(!t?.cname)return;let n=I(`chat-${t.cname}`);if(!n&&(t.canReply||t.hasMessages))return uproot.chat.create(I("adminmessage-modal-body"),t.cname,{canReply:!!t.canReply}),void uproot.ensureBuddy();if(n&&(uproot.chat.setReplyState(t.cname,!!t.canReply),!t.hasMessages&&!t.canReply&&(n.remove(),!document.querySelector(".uproot-chat")))){const e=I("uproot-buddy");e&&(e.hidden=!0)}}); diff --git a/src/uproot/default/Bare.html b/src/uproot/default/Bare.html index ef0e51b..f5c9856 100644 --- a/src/uproot/default/Bare.html +++ b/src/uproot/default/Bare.html @@ -15,31 +15,32 @@ -{% if not (disable_bootstrap | default(false)) %} +{% if not disable_bootstrap %} {% endif %} -{% if not (disable_uproot_fonts | default(false)) %} +{% if not disable_uproot_fonts %} -{% if not (disable_tabular_numbers | default(false)) %} +{% if not disable_tabular_numbers %} {% endif %} {% endif %} -{% if not (disable_terms | default(false)) %} +{% if not disable_terms %} {% endif %} {% endif %} -{% if not (disable_alpinejs | default(false)) %} +{% if not disable_alpinejs %} {% endif %} @@ -140,7 +141,7 @@