diff --git a/platform/CTFd/themes/ddps/assets/js/sql_challenge.js b/platform/CTFd/themes/ddps/assets/js/sql_challenge.js
index a84a477d0..2fed8f521 100644
--- a/platform/CTFd/themes/ddps/assets/js/sql_challenge.js
+++ b/platform/CTFd/themes/ddps/assets/js/sql_challenge.js
@@ -273,6 +273,13 @@ function resetSQLEditor() {
// Execute SQL Query (test mode - checks correctness but doesn't record submission)
async function executeSQLQuery() {
+ const testButton = document.getElementById('challenge-execute');
+
+ // Prevent duplicate clicks
+ if (testButton.disabled) {
+ return;
+ }
+
const challengeId = document.getElementById('challenge-id').value;
const submission = sqlEditor ? sqlEditor.getValue() : document.getElementById('challenge-input').value;
// Get user information from CTFd object
@@ -284,6 +291,11 @@ async function executeSQLQuery() {
return;
}
+ // Disable button and show loading state
+ testButton.disabled = true;
+ const originalHTML = testButton.innerHTML;
+ testButton.innerHTML = ' Testing...';
+
try {
const requestBody = {
challenge_id: parseInt(challengeId),
@@ -360,11 +372,22 @@ async function executeSQLQuery() {
console.error('Error details:', error.message, error.stack);
showErrorToast('Error executing query. Please try again.');
}
+ } finally {
+ // Re-enable button and restore original text
+ testButton.disabled = false;
+ testButton.innerHTML = originalHTML;
}
}
// Submit SQL Challenge
async function submitSQLChallenge() {
+ const submitButton = document.getElementById('challenge-submit');
+
+ // Prevent duplicate clicks
+ if (submitButton.disabled) {
+ return;
+ }
+
const challengeId = document.getElementById('challenge-id').value;
const submission = sqlEditor ? sqlEditor.getValue() : document.getElementById('challenge-input').value;
// Get user information from CTFd object
@@ -376,6 +399,11 @@ async function submitSQLChallenge() {
return;
}
+ // Disable button and show loading state
+ submitButton.disabled = true;
+ const originalHTML = submitButton.innerHTML;
+ submitButton.innerHTML = ' Submitting...';
+
// Check deadline before submitting
const deadlineElement = document.getElementById('deadline-time');
if (deadlineElement) {
@@ -393,6 +421,9 @@ async function submitSQLChallenge() {
}
};
displayResult(deadlineResult, false);
+ // Re-enable button
+ submitButton.disabled = false;
+ submitButton.innerHTML = originalHTML;
return;
}
}
@@ -459,6 +490,10 @@ async function submitSQLChallenge() {
console.error('Error submitting challenge:', error);
showErrorToast('Error submitting challenge. Please try again.');
}
+ } finally {
+ // Re-enable button and restore original text
+ submitButton.disabled = false;
+ submitButton.innerHTML = originalHTML;
}
}
diff --git a/platform/CTFd/themes/ddps/static/assets/sql_challenge.1f1e9271.js b/platform/CTFd/themes/ddps/static/assets/sql_challenge.1f1e9271.js
new file mode 100644
index 000000000..7bcc438c8
--- /dev/null
+++ b/platform/CTFd/themes/ddps/static/assets/sql_challenge.1f1e9271.js
@@ -0,0 +1,58 @@
+let l=null;function b(){const e=document.getElementById("challenge-input"),t=document.getElementById("sql-editor-container");if(!e||!t)return!1;if(l){try{l.toTextArea()}catch{}l=null}if(t.innerHTML="",typeof CodeMirror>"u"){if(!document.querySelector('link[href*="codemirror"]')){const d=document.createElement("link");d.rel="stylesheet",d.href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/codemirror.min.css",document.head.appendChild(d);const u=document.createElement("link");u.rel="stylesheet",u.href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/theme/monokai.min.css",document.head.appendChild(u)}const i=document.createElement("script");return i.src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/codemirror.min.js",i.onload=function(){const d=document.createElement("script");d.src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/mode/sql/sql.min.js",d.onload=function(){b()},document.head.appendChild(d)},document.head.appendChild(i),!1}if(typeof CTFd>"u"||!CTFd.user)return setTimeout(()=>b(),100),!1;const s=document.getElementById("challenge-id").value,r=`sql-challenge-user${CTFd.user.id||"anonymous"}-ch${s}-code`,m=localStorage.getItem(r),n=m||e.value||"";l=CodeMirror(t,{value:"",mode:"text/x-sql",theme:"monokai",lineNumbers:!0,indentUnit:4,lineWrapping:!0,autofocus:!0,scrollbarStyle:"native",viewportMargin:1/0,readOnly:!1,inputStyle:"contenteditable",extraKeys:{"Ctrl-Enter":function(){document.getElementById("challenge-submit").click()},Tab:"indentMore"}}),setTimeout(()=>{l.refresh(),n&&l.setValue(n),l.focus(),l.setCursor(l.lineCount(),0)},200),t.addEventListener("click",function(i){(i.target===t||i.target.classList.contains("CodeMirror-scroll"))&&l.focus()});const a=function(){const i=l.getValue();if(e.value=i,i.trim())try{localStorage.setItem(r,i)}catch(d){console.error("Failed to save:",d)}else localStorage.removeItem(r)};let c=!0;return l.on("beforeChange",function(){if(c){c=!1;return}setTimeout(a,0)}),l.on("inputRead",a),l.on("keyHandled",a),l.on("blur",a),setInterval(()=>{l&&document.querySelector(".CodeMirror-focused")&&a()},1e3),m&&x("Previous code restored from auto-save"),typeof setupBehaviorTracking=="function"&&l&&(setupBehaviorTracking(l),console.log("[SQL Challenge] Behavior tracking initialized")),!0}function x(e){const t=document.createElement("div");t.className="alert alert-info fade show position-fixed",t.style.cssText="top: 70px; right: 20px; z-index: 1050; max-width: 350px; padding: 0.75rem 2.5rem 0.75rem 1rem; position: relative;";const s=document.createElement("div");s.style.cssText="display: flex; align-items: center;",s.innerHTML=`${e}`;const o=document.createElement("button");o.type="button",o.className="btn-close btn-sm",o.style.cssText="position: absolute; top: 50%; right: 0.5rem; transform: translateY(-50%); padding: 0.25rem; font-size: 0.875rem;",o.onclick=function(){t.remove()},t.appendChild(s),t.appendChild(o),document.body.appendChild(t),setTimeout(()=>{t.parentNode&&t.remove()},3e3)}function y(e){const t=document.createElement("div");t.className="alert alert-danger fade show position-fixed",t.style.cssText="top: 70px; right: 20px; z-index: 1050; max-width: 400px; padding: 0.75rem 2.5rem 0.75rem 1rem; position: relative;";const s=document.createElement("div");s.style.cssText="display: flex; align-items: center;",s.innerHTML=`${I(e)}`;const o=document.createElement("button");o.type="button",o.className="btn-close btn-sm",o.style.cssText="position: absolute; top: 50%; right: 0.5rem; transform: translateY(-50%); padding: 0.25rem; font-size: 0.875rem;",o.onclick=function(){t.remove()},t.appendChild(s),t.appendChild(o),document.body.appendChild(t),setTimeout(()=>{t.parentNode&&t.remove()},5e3)}function w(){const e=document.getElementById("challenge-id").value,s=`sql-challenge-user${CTFd&&CTFd.user?CTFd.user.id:"anonymous"}-ch${e}-code`;localStorage.removeItem(s)}function B(){if(!confirm("Are you sure you want to reset the editor? This will clear all your current code."))return;if(l)l.setValue(""),l.focus();else{const r=document.getElementById("challenge-input");r&&(r.value="")}const e=document.getElementById("challenge-id").value,s=`sql-challenge-user${CTFd&&CTFd.user?CTFd.user.id:"anonymous"}-ch${e}-code`;localStorage.removeItem(s);const o=document.getElementById("query-result-container");o&&(o.innerHTML=`
+
+
+
Execute a query to see results
+
+ `),x("Editor has been reset")}async function $(){const e=document.getElementById("challenge-execute");if(e.disabled)return;const t=document.getElementById("challenge-id").value,s=l?l.getValue():document.getElementById("challenge-input").value,o=CTFd&&CTFd.user?CTFd.user.id:null,r=CTFd&&CTFd.user?CTFd.user.name:"anonymous";if(!s.trim()){alert("Please enter a SQL query");return}e.disabled=!0;const m=e.innerHTML;e.innerHTML=' Testing...';try{const n={challenge_id:parseInt(t),submission:s,test:!0,user_id:o,user_name:r},a=new AbortController,c=setTimeout(()=>a.abort(),2e4),i=await fetch("/api/v1/challenges/attempt",{method:"POST",headers:{"Content-Type":"application/json","CSRF-Token":init.csrfNonce},body:JSON.stringify(n),signal:a.signal});clearTimeout(c),i.ok||console.error("Response not OK:",i.statusText);const d=await i.text();let u;try{u=JSON.parse(d)}catch(p){console.error("Failed to parse response as JSON:",p),console.error("Response was:",d),y("Server returned invalid JSON response");return}if(!u||typeof u!="object"){console.error("Invalid result structure:",u),y("Invalid response from server");return}u.hasOwnProperty("data")?T(u,!0):T({success:!0,data:u},!0)}catch(n){n.name==="AbortError"?(console.error("Request timed out after 20 seconds"),y("Request timed out. The server took too long to respond. Please try again.")):(console.error("Error executing query:",n),console.error("Error details:",n.message,n.stack),y("Error executing query. Please try again."))}finally{e.disabled=!1,e.innerHTML=m}}async function M(){const e=document.getElementById("challenge-submit");if(e.disabled)return;const t=document.getElementById("challenge-id").value,s=l?l.getValue():document.getElementById("challenge-input").value,o=CTFd&&CTFd.user?CTFd.user.id:null,r=CTFd&&CTFd.user?CTFd.user.name:"anonymous";if(!s.trim()){alert("Please enter a SQL query");return}e.disabled=!0;const m=e.innerHTML;e.innerHTML=' Submitting...';const n=document.getElementById("deadline-time");if(n){const a=n.getAttribute("data-deadline");if(a){const c=new Date(a);if(new Date>c){T({success:!0,data:{status:"incorrect",message:"Submission deadline has passed"}},!1),e.disabled=!1,e.innerHTML=m;return}}}try{const a=new AbortController,c=setTimeout(()=>a.abort(),2e4),i=await fetch("/api/v1/challenges/attempt",{method:"POST",headers:{"Content-Type":"application/json","CSRF-Token":init.csrfNonce},body:JSON.stringify({challenge_id:parseInt(t),submission:s,user_id:o,user_name:r}),signal:a.signal});clearTimeout(c);const d=await i.json();if(!d||typeof d!="object"){console.error("Invalid result structure:",d),y("Invalid response from server");return}if(behaviorLogger){const u=d.data.status||"unknown";behaviorLogger.logEvent("submit",{query_text:s,query_length:s.length,submit_status:u})}d.hasOwnProperty("data")?T(d,!1):T({success:!0,data:d},!1)}catch(a){a.name==="AbortError"?(console.error("Request timed out after 20 seconds"),y("Request timed out. The server took too long to respond. Please try again.")):(console.error("Error submitting challenge:",a),y("Error submitting challenge. Please try again."))}finally{e.disabled=!1,e.innerHTML=m}}function T(e,t=!1){const s=document.getElementById("query-result-container");s.innerHTML="";let o=e.data.message||"";const r=o.startsWith("[TEST]"),m=document.createElement("div"),n=e.data.status==="already_solved";m.className=e.data.status==="correct"||n?"alert alert-success":e.data.status==="incorrect"&&!r?"alert alert-danger":r?"alert alert-info":"alert alert-warning",m.innerHTML='',r&&(o=o.replace(`[TEST]
+`,"")),n&&(o=o.replace(" but you already solved this",""));const a=o.split(`
+`);let c="",i=null,d=null,u=!1,p=!1,h=[];for(const f of a)if(f==="[USER_RESULT]")u=!0,h=[];else if(f==="[/USER_RESULT]"){u=!1;try{const g=h.join("");i=JSON.parse(g)}catch(g){console.error("Failed to parse user result:",g,"JSON string:",h.join(""))}}else if(f==="[EXPECTED_RESULT]")p=!0,h=[];else if(f==="[/EXPECTED_RESULT]"){p=!1;try{const g=h.join("");d=JSON.parse(g)}catch(g){console.error("Failed to parse expected result:",g,"JSON string:",h.join(""))}}else u||p?h.push(f):c+=f+`
+`;s.appendChild(m);let v=c.trim();if(n&&(v+=" (Already solved)"),document.getElementById("status-text").innerHTML=v,i){const f=document.createElement("div");f.className="card mb-2";const g=k(i),E=document.createElement("div");E.className="card-header bg-primary text-white",E.innerHTML=' Your Query Result
';const C=document.createElement("div");C.className="card-body p-0";const S=document.createElement("div");S.className="table-responsive",S.innerHTML=g,C.appendChild(S),f.appendChild(E),f.appendChild(C),s.appendChild(f)}e.data.status==="correct"&&!t&&(w(),e.data.points&&(document.getElementById("earned-points").textContent=e.data.points),setTimeout(()=>{try{const f=document.getElementById("successModal");f?(f.classList.add("show"),f.style.display="block",f.setAttribute("aria-hidden","false"),document.body.classList.add("modal-open")):console.error("Modal element not found!")}catch(f){console.error("Error showing modal:",f)}},1e3))}function k(e){if(!e||!e.columns||!e.rows)return'No data to display
';if(e.rows.length===0)return'Empty result set
';let t='';t+='';for(const s of e.columns)t+=`| ${I(s)} | `;t+="
",t+="";for(const s of e.rows){t+="";for(const o of s)t+=`| ${I(o)} | `;t+="
"}return t+="",t+="
",t+=``,t}function I(e){return e==null?"":String(e).replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")}function H(){const e=document.getElementById("resize-handle"),t=document.getElementById("left-panel"),s=document.getElementById("right-panel"),o=document.getElementById("resize-container");let r=!1,m=0,n=0;e.addEventListener("mousedown",function(c){r=!0,m=c.clientX,n=t.offsetWidth,document.body.style.cursor="col-resize",document.body.style.userSelect="none",c.preventDefault()}),document.addEventListener("mousemove",function(c){if(!r)return;const i=o.offsetWidth,u=(n+(c.clientX-m))/i*100;u>=20&&u<=70&&(t.style.width=u+"%",s.style.width=100-u+"%",l&&setTimeout(()=>{l.refresh(),document.activeElement&&document.activeElement.classList.contains("CodeMirror")&&l.focus()},10))}),document.addEventListener("mouseup",function(){if(r){r=!1,document.body.style.cursor="",document.body.style.userSelect="";const c=t.style.width;c&&localStorage.setItem("sql-challenge-left-width",c)}});const a=localStorage.getItem("sql-challenge-left-width");a&&(t.style.width=a,s.style.width=100-parseFloat(a)+"%")}function q(){const e=document.getElementById("vertical-resize-handle"),t=document.getElementById("editor-section"),s=document.getElementById("result-section"),o=document.querySelector(".sql-editor-section");if(!e||!t||!s)return;let r=!1,m=0,n=0;e.addEventListener("mousedown",function(c){r=!0,m=c.clientY,n=t.offsetHeight,document.body.style.cursor="row-resize",document.body.style.userSelect="none",c.preventDefault()}),document.addEventListener("mousemove",function(c){if(!r)return;const i=o.offsetHeight,u=(n+(c.clientY-m))/i*100;u>=20&&u<=80&&(t.style.height=u+"%",s.style.height=100-u-2+"%",l&&setTimeout(()=>{l.refresh(),document.activeElement&&document.activeElement.classList.contains("CodeMirror")&&l.focus()},10))}),document.addEventListener("mouseup",function(){if(r){r=!1,document.body.style.cursor="",document.body.style.userSelect="";const c=t.style.height;c&&localStorage.setItem("sql-challenge-editor-height",c)}});const a=localStorage.getItem("sql-challenge-editor-height");a&&(t.style.height=a,s.style.height=100-parseFloat(a)-2+"%")}async function F(){const e=document.getElementById("challenge-id").value,t=document.getElementById("historyModal"),s=document.getElementById("submission-history-content");if(!(!t||!s)){t.classList.add("show"),t.style.display="block",t.setAttribute("aria-hidden","false"),document.body.classList.add("modal-open"),s.innerHTML=`
+
+
+
Loading submission history...
+
+ `;try{const r=await(await fetch(`/api/v1/challenges/${e}/sql-submissions`)).json();if(r.success&&r.data)if(r.data.length===0)s.innerHTML=`
+
+
+
No Submissions Yet
+
You haven't submitted any solutions for this challenge yet.
+
+ `;else{let m='';r.data.forEach((n,a)=>{const i=new Date(n.date).toLocaleString("ko-KR",{year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",second:"2-digit",timeZone:"Asia/Seoul",hour12:!1}),d=n.type==="correct"?'
':'
',u=n.type==="correct"?"correct":"incorrect",p=n.type==="correct"?"Correct":"Incorrect",h=r.data.length-a,v=`collapse${n.id}`,f=`heading${n.id}`,g=a===0,E=g?"show":"";m+=`
+
+
+
+
+
+ ${I(n.submission||"")}
+
+
+
+
+ `}),m+="
",s.innerHTML=m}else s.innerHTML=`
+
+
+ Failed to load submission history. Please try again.
+
+ `}catch(o){console.error("Error loading submission history:",o),s.innerHTML=`
+
+
+ An error occurred while loading submission history.
+
+ `}}}function L(){const e=document.getElementById("deadline-time");if(!e)return;const t=e.getAttribute("data-deadline");if(!t)return;const s=new Date(t),o=new Date,r=s-o;if(r<=0){e.textContent="Expired",e.closest(".deadline-display").classList.add("deadline-danger");return}const m=Math.floor(r/(1e3*60*60*24)),n=Math.floor(r%(1e3*60*60*24)/(1e3*60*60)),a=Math.floor(r%(1e3*60*60)/(1e3*60)),c=Math.floor(r%(1e3*60)/1e3);let i="";m>0?i=`${m}d ${n}h ${a}m`:n>0?i=`${n}h ${a}m ${c}s`:a>0?i=`${a}m ${c}s`:i=`${c}s`,e.textContent=i+" remaining";const d=e.closest(".deadline-display");d.classList.remove("deadline-warning","deadline-danger"),m===0&&n<1?d.classList.add("deadline-danger"):m===0&&n<6&&d.classList.add("deadline-warning")}function R(){console.log("=== LocalStorage Debug ==="),console.log("All localStorage keys:",Object.keys(localStorage)),console.log("SQL related keys:");for(let e in localStorage)e.includes("sql-challenge")&&console.log(` ${e}: ${localStorage[e].substring(0,50)}...`);console.log("========================")}window.testSaveCode=function(e){const t=document.getElementById("challenge-id").value,o=`sql-challenge-user${CTFd&&CTFd.user?CTFd.user.id:"test"}-ch${t}-code`;localStorage.setItem(o,e||"SELECT * FROM test;"),console.log(`Saved to ${o}`),R()};window.testLoadCode=function(){const e=document.getElementById("challenge-id").value,s=`sql-challenge-user${CTFd&&CTFd.user?CTFd.user.id:"test"}-ch${e}-code`,o=localStorage.getItem(s);return console.log(`Loading from ${s}:`,o),o&&l&&(l.setValue(o),console.log("Code loaded into editor")),o};document.addEventListener("DOMContentLoaded",function(){const e=document.querySelector(".challenge-panel");e&&(e.addEventListener("contextmenu",function(n){return n.preventDefault(),!1}),e.addEventListener("selectstart",function(n){return n.preventDefault(),!1}),e.addEventListener("keydown",function(n){if((n.ctrlKey||n.metaKey)&&(n.keyCode===67||n.keyCode===65||n.keyCode===88))return n.preventDefault(),!1}),e.addEventListener("dragstart",function(n){return n.preventDefault(),!1}),e.addEventListener("mousedown",function(n){if(n.detail>1)return n.preventDefault(),!1})),b(),setTimeout(()=>{l||b()},500),setTimeout(()=>{l||b()},1e3),H(),q(),L(),setInterval(L,1e3);const t=document.getElementById("challenge-reset"),s=document.getElementById("challenge-execute"),o=document.getElementById("challenge-submit"),r=document.getElementById("challenge-history");t&&t.addEventListener("click",B),s&&s.addEventListener("click",$),o&&o.addEventListener("click",M),r&&r.addEventListener("click",F),document.querySelectorAll('[data-bs-dismiss="modal"]').forEach(n=>{n.addEventListener("click",function(){const a=this.closest(".modal");a&&(a.classList.remove("show"),a.style.display="none",a.setAttribute("aria-hidden","true"),document.body.classList.remove("modal-open"))})})});window.addEventListener("load",function(){l||b()});
diff --git a/platform/CTFd/themes/ddps/static/assets/sql_challenge.d0862715.js b/platform/CTFd/themes/ddps/static/assets/sql_challenge.d0862715.js
deleted file mode 100644
index d080f5f57..000000000
--- a/platform/CTFd/themes/ddps/static/assets/sql_challenge.d0862715.js
+++ /dev/null
@@ -1,58 +0,0 @@
-let i=null;function v(){const t=document.getElementById("challenge-input"),e=document.getElementById("sql-editor-container");if(!t||!e)return!1;if(i){try{i.toTextArea()}catch{}i=null}if(e.innerHTML="",typeof CodeMirror>"u"){if(!document.querySelector('link[href*="codemirror"]')){const u=document.createElement("link");u.rel="stylesheet",u.href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/codemirror.min.css",document.head.appendChild(u);const f=document.createElement("link");f.rel="stylesheet",f.href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/theme/monokai.min.css",document.head.appendChild(f)}const l=document.createElement("script");return l.src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/codemirror.min.js",l.onload=function(){const u=document.createElement("script");u.src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/mode/sql/sql.min.js",u.onload=function(){v()},document.head.appendChild(u)},document.head.appendChild(l),!1}if(typeof CTFd>"u"||!CTFd.user)return setTimeout(()=>v(),100),!1;const s=document.getElementById("challenge-id").value,a=`sql-challenge-user${CTFd.user.id||"anonymous"}-ch${s}-code`,d=localStorage.getItem(a),n=d||t.value||"";i=CodeMirror(e,{value:"",mode:"text/x-sql",theme:"monokai",lineNumbers:!0,indentUnit:4,lineWrapping:!0,autofocus:!0,scrollbarStyle:"native",viewportMargin:1/0,readOnly:!1,inputStyle:"contenteditable",extraKeys:{"Ctrl-Enter":function(){document.getElementById("challenge-submit").click()},Tab:"indentMore"}}),setTimeout(()=>{i.refresh(),n&&i.setValue(n),i.focus(),i.setCursor(i.lineCount(),0)},200),e.addEventListener("click",function(l){(l.target===e||l.target.classList.contains("CodeMirror-scroll"))&&i.focus()});const c=function(){const l=i.getValue();if(t.value=l,l.trim())try{localStorage.setItem(a,l)}catch(u){console.error("Failed to save:",u)}else localStorage.removeItem(a)};let r=!0;return i.on("beforeChange",function(){if(r){r=!1;return}setTimeout(c,0)}),i.on("inputRead",c),i.on("keyHandled",c),i.on("blur",c),setInterval(()=>{i&&document.querySelector(".CodeMirror-focused")&&c()},1e3),d&&w("Previous code restored from auto-save"),typeof setupBehaviorTracking=="function"&&i&&(setupBehaviorTracking(i),console.log("[SQL Challenge] Behavior tracking initialized")),!0}function w(t){const e=document.createElement("div");e.className="alert alert-info fade show position-fixed",e.style.cssText="top: 70px; right: 20px; z-index: 1050; max-width: 350px; padding: 0.75rem 2.5rem 0.75rem 1rem; position: relative;";const s=document.createElement("div");s.style.cssText="display: flex; align-items: center;",s.innerHTML=`${t}`;const o=document.createElement("button");o.type="button",o.className="btn-close btn-sm",o.style.cssText="position: absolute; top: 50%; right: 0.5rem; transform: translateY(-50%); padding: 0.25rem; font-size: 0.875rem;",o.onclick=function(){e.remove()},e.appendChild(s),e.appendChild(o),document.body.appendChild(e),setTimeout(()=>{e.parentNode&&e.remove()},3e3)}function p(t){const e=document.createElement("div");e.className="alert alert-danger fade show position-fixed",e.style.cssText="top: 70px; right: 20px; z-index: 1050; max-width: 400px; padding: 0.75rem 2.5rem 0.75rem 1rem; position: relative;";const s=document.createElement("div");s.style.cssText="display: flex; align-items: center;",s.innerHTML=`${I(t)}`;const o=document.createElement("button");o.type="button",o.className="btn-close btn-sm",o.style.cssText="position: absolute; top: 50%; right: 0.5rem; transform: translateY(-50%); padding: 0.25rem; font-size: 0.875rem;",o.onclick=function(){e.remove()},e.appendChild(s),e.appendChild(o),document.body.appendChild(e),setTimeout(()=>{e.parentNode&&e.remove()},5e3)}function L(){const t=document.getElementById("challenge-id").value,s=`sql-challenge-user${CTFd&&CTFd.user?CTFd.user.id:"anonymous"}-ch${t}-code`;localStorage.removeItem(s)}function B(){if(!confirm("Are you sure you want to reset the editor? This will clear all your current code."))return;if(i)i.setValue(""),i.focus();else{const a=document.getElementById("challenge-input");a&&(a.value="")}const t=document.getElementById("challenge-id").value,s=`sql-challenge-user${CTFd&&CTFd.user?CTFd.user.id:"anonymous"}-ch${t}-code`;localStorage.removeItem(s);const o=document.getElementById("query-result-container");o&&(o.innerHTML=`
-
-
-
Execute a query to see results
-
- `),w("Editor has been reset")}async function $(){const t=document.getElementById("challenge-id").value,e=i?i.getValue():document.getElementById("challenge-input").value,s=CTFd&&CTFd.user?CTFd.user.id:null,o=CTFd&&CTFd.user?CTFd.user.name:"anonymous";if(!e.trim()){alert("Please enter a SQL query");return}try{const a={challenge_id:parseInt(t),submission:e,test:!0,user_id:s,user_name:o},d=new AbortController,n=setTimeout(()=>d.abort(),2e4),c=await fetch("/api/v1/challenges/attempt",{method:"POST",headers:{"Content-Type":"application/json","CSRF-Token":init.csrfNonce},body:JSON.stringify(a),signal:d.signal});clearTimeout(n),c.ok||console.error("Response not OK:",c.statusText);const r=await c.text();let l;try{l=JSON.parse(r)}catch(u){console.error("Failed to parse response as JSON:",u),console.error("Response was:",r),p("Server returned invalid JSON response");return}if(!l||typeof l!="object"){console.error("Invalid result structure:",l),p("Invalid response from server");return}l.hasOwnProperty("data")?T(l,!0):T({success:!0,data:l},!0)}catch(a){a.name==="AbortError"?(console.error("Request timed out after 20 seconds"),p("Request timed out. The server took too long to respond. Please try again.")):(console.error("Error executing query:",a),console.error("Error details:",a.message,a.stack),p("Error executing query. Please try again."))}}async function k(){const t=document.getElementById("challenge-id").value,e=i?i.getValue():document.getElementById("challenge-input").value,s=CTFd&&CTFd.user?CTFd.user.id:null,o=CTFd&&CTFd.user?CTFd.user.name:"anonymous";if(!e.trim()){alert("Please enter a SQL query");return}const a=document.getElementById("deadline-time");if(a){const d=a.getAttribute("data-deadline");if(d){const n=new Date(d);if(new Date>n){T({success:!0,data:{status:"incorrect",message:"Submission deadline has passed"}},!1);return}}}try{const d=new AbortController,n=setTimeout(()=>d.abort(),2e4),c=await fetch("/api/v1/challenges/attempt",{method:"POST",headers:{"Content-Type":"application/json","CSRF-Token":init.csrfNonce},body:JSON.stringify({challenge_id:parseInt(t),submission:e,user_id:s,user_name:o}),signal:d.signal});clearTimeout(n);const r=await c.json();if(!r||typeof r!="object"){console.error("Invalid result structure:",r),p("Invalid response from server");return}if(behaviorLogger){const l=r.data.status||"unknown";behaviorLogger.logEvent("submit",{query_text:e,query_length:e.length,submit_status:l})}r.hasOwnProperty("data")?T(r,!1):T({success:!0,data:r},!1)}catch(d){d.name==="AbortError"?(console.error("Request timed out after 20 seconds"),p("Request timed out. The server took too long to respond. Please try again.")):(console.error("Error submitting challenge:",d),p("Error submitting challenge. Please try again."))}}function T(t,e=!1){const s=document.getElementById("query-result-container");s.innerHTML="";let o=t.data.message||"";const a=o.startsWith("[TEST]"),d=document.createElement("div"),n=t.data.status==="already_solved";d.className=t.data.status==="correct"||n?"alert alert-success":t.data.status==="incorrect"&&!a?"alert alert-danger":a?"alert alert-info":"alert alert-warning",d.innerHTML='',a&&(o=o.replace(`[TEST]
-`,"")),n&&(o=o.replace(" but you already solved this",""));const c=o.split(`
-`);let r="",l=null,u=null,f=!1,b=!1,h=[];for(const m of c)if(m==="[USER_RESULT]")f=!0,h=[];else if(m==="[/USER_RESULT]"){f=!1;try{const g=h.join("");l=JSON.parse(g)}catch(g){console.error("Failed to parse user result:",g,"JSON string:",h.join(""))}}else if(m==="[EXPECTED_RESULT]")b=!0,h=[];else if(m==="[/EXPECTED_RESULT]"){b=!1;try{const g=h.join("");u=JSON.parse(g)}catch(g){console.error("Failed to parse expected result:",g,"JSON string:",h.join(""))}}else f||b?h.push(m):r+=m+`
-`;s.appendChild(d);let y=r.trim();if(n&&(y+=" (Already solved)"),document.getElementById("status-text").innerHTML=y,l){const m=document.createElement("div");m.className="card mb-2";const g=q(l),E=document.createElement("div");E.className="card-header bg-primary text-white",E.innerHTML=' Your Query Result
';const C=document.createElement("div");C.className="card-body p-0";const S=document.createElement("div");S.className="table-responsive",S.innerHTML=g,C.appendChild(S),m.appendChild(E),m.appendChild(C),s.appendChild(m)}t.data.status==="correct"&&!e&&(L(),t.data.points&&(document.getElementById("earned-points").textContent=t.data.points),setTimeout(()=>{try{const m=document.getElementById("successModal");m?(m.classList.add("show"),m.style.display="block",m.setAttribute("aria-hidden","false"),document.body.classList.add("modal-open")):console.error("Modal element not found!")}catch(m){console.error("Error showing modal:",m)}},1e3))}function q(t){if(!t||!t.columns||!t.rows)return'No data to display
';if(t.rows.length===0)return'Empty result set
';let e='';e+='';for(const s of t.columns)e+=`| ${I(s)} | `;e+="
",e+="";for(const s of t.rows){e+="";for(const o of s)e+=`| ${I(o)} | `;e+="
"}return e+="",e+="
",e+=``,e}function I(t){return t==null?"":String(t).replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")}function F(){const t=document.getElementById("resize-handle"),e=document.getElementById("left-panel"),s=document.getElementById("right-panel"),o=document.getElementById("resize-container");let a=!1,d=0,n=0;t.addEventListener("mousedown",function(r){a=!0,d=r.clientX,n=e.offsetWidth,document.body.style.cursor="col-resize",document.body.style.userSelect="none",r.preventDefault()}),document.addEventListener("mousemove",function(r){if(!a)return;const l=o.offsetWidth,f=(n+(r.clientX-d))/l*100;f>=20&&f<=70&&(e.style.width=f+"%",s.style.width=100-f+"%",i&&setTimeout(()=>{i.refresh(),document.activeElement&&document.activeElement.classList.contains("CodeMirror")&&i.focus()},10))}),document.addEventListener("mouseup",function(){if(a){a=!1,document.body.style.cursor="",document.body.style.userSelect="";const r=e.style.width;r&&localStorage.setItem("sql-challenge-left-width",r)}});const c=localStorage.getItem("sql-challenge-left-width");c&&(e.style.width=c,s.style.width=100-parseFloat(c)+"%")}function M(){const t=document.getElementById("vertical-resize-handle"),e=document.getElementById("editor-section"),s=document.getElementById("result-section"),o=document.querySelector(".sql-editor-section");if(!t||!e||!s)return;let a=!1,d=0,n=0;t.addEventListener("mousedown",function(r){a=!0,d=r.clientY,n=e.offsetHeight,document.body.style.cursor="row-resize",document.body.style.userSelect="none",r.preventDefault()}),document.addEventListener("mousemove",function(r){if(!a)return;const l=o.offsetHeight,f=(n+(r.clientY-d))/l*100;f>=20&&f<=80&&(e.style.height=f+"%",s.style.height=100-f-2+"%",i&&setTimeout(()=>{i.refresh(),document.activeElement&&document.activeElement.classList.contains("CodeMirror")&&i.focus()},10))}),document.addEventListener("mouseup",function(){if(a){a=!1,document.body.style.cursor="",document.body.style.userSelect="";const r=e.style.height;r&&localStorage.setItem("sql-challenge-editor-height",r)}});const c=localStorage.getItem("sql-challenge-editor-height");c&&(e.style.height=c,s.style.height=100-parseFloat(c)-2+"%")}async function R(){const t=document.getElementById("challenge-id").value,e=document.getElementById("historyModal"),s=document.getElementById("submission-history-content");if(!(!e||!s)){e.classList.add("show"),e.style.display="block",e.setAttribute("aria-hidden","false"),document.body.classList.add("modal-open"),s.innerHTML=`
-
-
-
Loading submission history...
-
- `;try{const a=await(await fetch(`/api/v1/challenges/${t}/sql-submissions`)).json();if(a.success&&a.data)if(a.data.length===0)s.innerHTML=`
-
-
-
No Submissions Yet
-
You haven't submitted any solutions for this challenge yet.
-
- `;else{let d='';a.data.forEach((n,c)=>{const l=new Date(n.date).toLocaleString("ko-KR",{year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",second:"2-digit",timeZone:"Asia/Seoul",hour12:!1}),u=n.type==="correct"?'
':'
',f=n.type==="correct"?"correct":"incorrect",b=n.type==="correct"?"Correct":"Incorrect",h=a.data.length-c,y=`collapse${n.id}`,m=`heading${n.id}`,g=c===0,E=g?"show":"";d+=`
-
-
-
-
-
- ${I(n.submission||"")}
-
-
-
-
- `}),d+="
",s.innerHTML=d}else s.innerHTML=`
-
-
- Failed to load submission history. Please try again.
-
- `}catch(o){console.error("Error loading submission history:",o),s.innerHTML=`
-
-
- An error occurred while loading submission history.
-
- `}}}function x(){const t=document.getElementById("deadline-time");if(!t)return;const e=t.getAttribute("data-deadline");if(!e)return;const s=new Date(e),o=new Date,a=s-o;if(a<=0){t.textContent="Expired",t.closest(".deadline-display").classList.add("deadline-danger");return}const d=Math.floor(a/(1e3*60*60*24)),n=Math.floor(a%(1e3*60*60*24)/(1e3*60*60)),c=Math.floor(a%(1e3*60*60)/(1e3*60)),r=Math.floor(a%(1e3*60)/1e3);let l="";d>0?l=`${d}d ${n}h ${c}m`:n>0?l=`${n}h ${c}m ${r}s`:c>0?l=`${c}m ${r}s`:l=`${r}s`,t.textContent=l+" remaining";const u=t.closest(".deadline-display");u.classList.remove("deadline-warning","deadline-danger"),d===0&&n<1?u.classList.add("deadline-danger"):d===0&&n<6&&u.classList.add("deadline-warning")}function H(){console.log("=== LocalStorage Debug ==="),console.log("All localStorage keys:",Object.keys(localStorage)),console.log("SQL related keys:");for(let t in localStorage)t.includes("sql-challenge")&&console.log(` ${t}: ${localStorage[t].substring(0,50)}...`);console.log("========================")}window.testSaveCode=function(t){const e=document.getElementById("challenge-id").value,o=`sql-challenge-user${CTFd&&CTFd.user?CTFd.user.id:"test"}-ch${e}-code`;localStorage.setItem(o,t||"SELECT * FROM test;"),console.log(`Saved to ${o}`),H()};window.testLoadCode=function(){const t=document.getElementById("challenge-id").value,s=`sql-challenge-user${CTFd&&CTFd.user?CTFd.user.id:"test"}-ch${t}-code`,o=localStorage.getItem(s);return console.log(`Loading from ${s}:`,o),o&&i&&(i.setValue(o),console.log("Code loaded into editor")),o};document.addEventListener("DOMContentLoaded",function(){const t=document.querySelector(".challenge-panel");t&&(t.addEventListener("contextmenu",function(n){return n.preventDefault(),!1}),t.addEventListener("selectstart",function(n){return n.preventDefault(),!1}),t.addEventListener("keydown",function(n){if((n.ctrlKey||n.metaKey)&&(n.keyCode===67||n.keyCode===65||n.keyCode===88))return n.preventDefault(),!1}),t.addEventListener("dragstart",function(n){return n.preventDefault(),!1}),t.addEventListener("mousedown",function(n){if(n.detail>1)return n.preventDefault(),!1})),v(),setTimeout(()=>{i||v()},500),setTimeout(()=>{i||v()},1e3),F(),M(),x(),setInterval(x,1e3);const e=document.getElementById("challenge-reset"),s=document.getElementById("challenge-execute"),o=document.getElementById("challenge-submit"),a=document.getElementById("challenge-history");e&&e.addEventListener("click",B),s&&s.addEventListener("click",$),o&&o.addEventListener("click",k),a&&a.addEventListener("click",R),document.querySelectorAll('[data-bs-dismiss="modal"]').forEach(n=>{n.addEventListener("click",function(){const c=this.closest(".modal");c&&(c.classList.remove("show"),c.style.display="none",c.setAttribute("aria-hidden","true"),document.body.classList.remove("modal-open"))})})});window.addEventListener("load",function(){i||v()});
diff --git a/platform/CTFd/themes/ddps/static/manifest.json b/platform/CTFd/themes/ddps/static/manifest.json
index 14d26e955..8be5a2412 100644
--- a/platform/CTFd/themes/ddps/static/manifest.json
+++ b/platform/CTFd/themes/ddps/static/manifest.json
@@ -127,7 +127,7 @@
"isEntry": true
},
"assets/js/sql_challenge.js": {
- "file": "assets/sql_challenge.d0862715.js",
+ "file": "assets/sql_challenge.1f1e9271.js",
"src": "assets/js/sql_challenge.js",
"isEntry": true
},