1(function(){var t,o,n=null;function e(e){if(!e)return"";var t=document.createElement("div");return t.textContent=e,t.innerHTML}function s(e){return(e||"").replace(/_/g," ")}async function c(){var n,a,r,c,l,d,u,o=document.getElementById("tickets-content");if(!o)return;if(l=window._siteFeatures||{},l.tickets===!1){o.innerHTML="<p>Support tickets are not available.</p>";return}if(!t||!t.isLoggedIn()){window.location.href="/login/?redirect="+encodeURIComponent(window.location.pathname);return}if(d=new URLSearchParams(window.location.search),r=d.get("id"),r){o.style.display="none",a=document.getElementById("ticket-detail-content"),a&&(a.style.display="block",await i(a,r));return}try{u=await t._json("/api/tickets"),c=u.tickets||[],n='<div style="margin-bottom:1.5rem;">',n+='<a href="/tickets/new/" class="btn-brand">New Ticket</a>',n+="</div>",c.length===0?n+="<p>No support tickets yet.</p>":(n+='<div class="orders-list">',c.forEach(function(t){n+=`<div class="order-card" style="cursor:pointer;" onclick="window.location.href='/tickets/?id=`+t.id+`'">`,n+='<div class="order-header">',n+='<span class="order-number">'+e(t.ticket_number)+"</span>",n+='<span class="order-status status-ticket-'+e(t.status)+'">'+e(s(t.status))+"</span>",n+="</div>",n+='<div class="order-details" style="display:flex;justify-content:space-between;gap:1rem;">',n+="<span>"+e(t.subject)+"</span>",n+='<span style="white-space:nowrap;color:#888;">'+new Date(t.created_at).toLocaleDateString()+"</span>",n+="</div>",n+="</div>"}),n+="</div>"),o.innerHTML=n}catch{o.innerHTML='<p style="color:#dc3545;">Failed to load tickets. Please try again.</p>'}}function r(e){return e<1024?e+" B":e<1024*1024?(e/1024).toFixed(1)+" KB":(e/(1024*1024)).toFixed(1)+" MB"}function a(t){if(!t||t.length===0)return"";var n='<div class="comment-attachments" style="margin-top:0.5rem;">';return t.forEach(function(t){var s=t.mime_type&&t.mime_type.indexOf("image/")===0,o=t.mime_type&&t.mime_type.indexOf("video/")===0,i=s||o?"viewTicketAttachment":"downloadTicketAttachment",a=s?"ð¼ï¸":o?"ð¬":"ð";n+='<a href="javascript:void(0)" onclick="'+i+"('"+e(t.stored_name)+"', '"+e(t.filename)+"', '"+e(t.mime_type||"")+`')" `,n+='class="ticket-attachment-link">',n+="<span>"+a+"</span><span>"+e(t.filename)+"</span>",n+='<span style="color:#888;">('+r(t.size_bytes)+")</span>",n+="</a>"}),n+="</div>",n}window.viewTicketAttachment=async function(n,s,o){try{var i,a,r,l,c=await t._fetch("/api/tickets/attachments/"+n);if(!c.ok){alert("Failed to load attachment");return}l=await c.blob(),a=URL.createObjectURL(l),i=document.getElementById("attachment-modal"),i||(i=document.createElement("div"),i.id="attachment-modal",i.style.cssText="position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.9);z-index:10000;display:flex;align-items:center;justify-content:center;flex-direction:column;",i.onclick=function(e){if(e.target===i){i.style.display="none";var t=i.querySelector("video");t&&t.pause()}},document.body.appendChild(i)),r="",o.indexOf("image/")===0?r='<img src="'+a+'" style="max-width:90%;max-height:80%;object-fit:contain;border-radius:8px;">':o.indexOf("video/")===0&&(r='<video src="'+a+'" controls autoplay style="max-width:90%;max-height:80%;border-radius:8px;"></video>'),i.innerHTML='<div style="color:#fff;margin-bottom:1rem;font-size:1.1rem;">'+e(s)+"</div>"+r+`<div style="margin-top:1rem;"><button onclick="document.getElementById('attachment-modal').style.display='none'" style="background:#333;color:#fff;border:1px solid #555;padding:8px 16px;border-radius:4px;cursor:pointer;margin-right:8px;">Close</button><a href="`+a+'" download="'+e(s)+'" class="btn-brand">Download</a></div>',i.style.display="flex"}catch(e){alert("Failed to load attachment: "+e.message)}},window.downloadTicketAttachment=async function(e,n){try{var o,i,r,a=await t._fetch("/api/tickets/attachments/"+e);if(!a.ok){alert("Failed to download attachment");return}r=await a.blob(),i=URL.createObjectURL(r),o=document.createElement("a"),o.href=i,o.download=n,document.body.appendChild(o),o.click(),document.body.removeChild(o),URL.revokeObjectURL(i)}catch(e){alert("Failed to download attachment: "+e.message)}};async function i(n,o){try{var d,u,h,m,f,p,g=await t._json("/api/tickets/"+o),l=g.ticket,v=g.comments||[],c='<div style="margin-bottom:1rem;">';c+='<a href="/tickets/" class="link-brand">← Back to Tickets</a>
1',c+="</div>",c+='<div class="ticket-header">',c+='<h2 style="margin:0;">'+e(l.ticket_number)+" — "+e(l.subject)+"</h2>",c+='<span class="order-status status-ticket-'+e(l.status)+'" id="ticket-status-badge">'+e(s(l.status))+"</span>",c+="</div>",c+='<p style="color:#888;margin-bottom:1rem;">Created '+new Date(l.created_at).toLocaleString()+"</p>",l.status!=="closed"&&(c+='<div style="margin-bottom:1.5rem;padding:1rem;background:rgba(var(--brand-rgb, 144,221,64),0.1);border-radius:8px;">',c+='<label style="color:#ccc;font-size:0.9em;margin-right:0.5rem;">Change Status:</label>',c+='<select id="ticket-status-select" style="padding:6px 12px;border-radius:4px;border:1px solid #444;background:#1a1a2e;color:#fff;">',c+='<option value="">Keep current ('+e(s(l.status))+")</option>",l.status!=="resolved"&&(c+='<option value="resolved">Mark as Resolved</option>'),c+='<option value="closed">Close Ticket</option>',c+="</select>",c+='<button type="button" id="update-status-btn" class="btn-brand" style="margin-left:0.5rem;">Update</button>',c+='<span id="status-update-message" style="margin-left:1rem;font-size:0.9em;"></span>',c+="</div>"),l.attachments&&l.attachments.length>0&&(c+='<div style="margin-bottom:1rem;">',c+='<strong style="color:#888;font-size:0.9em;">Attachments:</strong>',c+=a(l.attachments),c+="</div>"),c+='<div class="ticket-comments">',v.forEach(function(t){c+='<div class="ticket-comment">',c+='<div class="comment-header">',c+="<strong>"+e(t.author_name)+"</strong>",c+='<span style="color:#888;font-size:0.85em;">'+new Date(t.created_at).toLocaleString()+"</span>",c+="</div>",c+='<div class="comment-body">'+e(t.body)+"</div>",t.attachments&&t.attachments.length>0&&(c+=a(t.attachments)),c+="</div>"}),c+="</div>",l.status!=="closed"?(c+='<div class="ticket-reply-form">',c+="<h3>Reply</h3>",c+='<form id="ticket-reply-form">',c+='<div class="form-group">',c+='<textarea id="reply-body" name="body" required class="input-field" rows="4" placeholder="Type your reply..."></textarea>
1',c+="</div>",c+='<div class="form-group">',c+='<label style="color:#ccc;font-size:0.9em;">Attachments (optional)</label>',c+='<input type="file" id="reply-attachments" name="attachments" multiple style="display:block;margin-top:0.5rem;color:#ccc;">',c+='<div id="selected-files" style="margin-top:0.5rem;font-size:0.85em;color:#888;"></div>',c+="</div>",c+='<div class="auth-message" id="reply-message"></div>',c+='<button type="submit" class="btn-brand">Send Reply</button>',c+="</form>",c+="</div>"):c+='<p style="color:#888;margin-top:1rem;">This ticket is closed.</p>',n.innerHTML=c,u=document.getElementById("update-status-btn"),f=document.getElementById("ticket-status-select"),d=document.getElementById("status-update-message"),u&&f&&u.addEventListener("click",async function(){var e=f.value;if(!e){d&&(d.textContent="Please select a status.",d.style.color="#dc3545");return}u.disabled=!0,u.textContent="Updating...";try{await t._json("/api/tickets/"+o,{method:"PUT",body:JSON.stringify({status:e})}),d&&(d.textContent="Status updated!",d.style.color="var(--brand, #90DD40)"),setTimeout(function(){i(n,o)},500)}catch(e){d&&(d.textContent=e.message||"Failed to update status.",d.style.color="#dc3545"),u.disabled=!1,u.textContent="Update"}}),h=document.getElementById("reply-attachments"),m=document.getElementById("selected-files"),h&&m&&h.addEventListener("change",function(){var e,n,t=h.files;if(t.length===0)m.textContent="";else{for(n=[],e=0;e<t.length;e++)n.push(t[e].name+" ("+r(t[e].size)+")");m.textContent="Selected: "+n.join(", ")}}),p=document.getElementById("ticket-reply-form"),p&&p.addEventListener("submit",async function(e){e.preventDefault();var s,r,c,l,d,u,h,a=document.getElementById("reply-message"),m=document.getElementById("reply-body"),f=m.value.trim();if(!f)return;try{if(a.textContent="",a.className="auth-message",r=await t._json("/api/tickets/"+o+"/comments",{method:"POST",body:JSON.stringify({body:f})}),s=document.getElementById("reply-attachments"),s&&s.files.length>0){if(!r||!r.comment)throw console.error("Comment response missing:",r),new Error("Failed to create comment - no response received");for(d=r.comment.id,c=new FormData,l=0;l<s.files.length;l++)c.append("files",s.files[l]);if(d&&c.append("comment_id",String(d)),u=await t._fetch("/api/tickets/"+o+"/attachments",{method:"POST",body:c}),!u.ok){h=await u.text(),console.error("Attachment upload failed:",h),a.textContent="Comment saved, but attachment upload failed: "+h,a.className="auth-message error";return}}m.value="",s&&(s.value=""),await i(n,o)}catch(e){a.textContent=e.message||"Failed to send reply",a.className="auth-message error"}})}catch{n.innerHTML='<p style="color:#dc3545;">Failed to load ticket. <a href="/tickets/" class="link-brand">Back to tickets</a></p>'}}o=!1;function l(e){if(o)return;o=!0,window.addEventListener("message",function(t){if(t.origin!==e)return;t.data&&t.data.token&&(document.querySelectorAll('input[name="mcaptcha__token"]').forEach(function(e){e.value=t.data.token}),console.log("mCaptcha: Token received"))})}async function d(s){if(!t)return;try{if(n=await t.getCaptchaConfig(),n&&n.enabled){try{var o,i,a=new URL(n.widget_url);l(a.origin)}catch{console.error("Invalid widget URL:",n.widget_url);return}o=s.querySelector('button[type="submit"]'),o&&(i=document.createElement("div"),i.className="form-group captcha-container",i.innerHTML='<input type="hidden" name="mcaptcha__token" id="mcaptcha__token" value="" /><iframe src="'+e(n.widget_url)+'" style="width: 304px; height: 78px; border: none; display: block; margin: 10px 0;" title="mCaptcha" frameborder="0"></iframe>',o.parentNode.insertBefore(i,o))}}catch(e){console.error("Failed to load captcha config:",e)}}function u(){var e=document.getElementById("mcaptcha__token");return e?e.value:""}async function h(e){if(!e)return;try{var s=await fetch("/api/ticket-categories"),t=await s.json(),n=t.categories||[];e.innerHTML='<option value="">Please select a category...</option>',n.forEach(function(t){var n=document.createElement("option");n.value=t.id,n.textContent=t.name,e.appendChild(n)})}catch(t){console.error("Failed to load ticket categories:",t),e.innerHTML='<option value="">Failed to load categories</option>'}}function m(){var s,e=document.getElementById("ticket-form");if(!e)return;if(!t||!t.isLoggedIn()){window.location.href="/login/?redirect="+encodeURIComponent(window.location.pathname);return}s=document.getElementById("ticket-category"),s&&h(s),d(e),e.addEventListener("submit",async function(s){s.preventDefault();var i,a,r,c,l,o=document.getElementById("ticket-form-message");if(o.textContent="",o.className="auth-message",r=e.querySelector("[name=category_id]")?.value,c=e.querySelector("[name=subject]").value.trim(),l=e.querySelector("[name=message]").value.trim(),!r){o.textContent="Please select a category",o.className="auth-message error";return}if(!c||!l){o.textContent="Subject and message are required",o.className="auth-message error";return}if(a="",n&&n.enabled&&(a=u(),!a)){o.textContent="Please complete the captcha verification",o.className="auth-message error";return}i=e.querySelector("button[type=submit]"),i.disabled=!0,i.textContent="Submitting...";try{await t._json("/api/tickets",{method:"POST",body:JSON.stringify({subject:c,message:l,category_id:parseInt(r,10),captcha_token:a})}),window.location.href="/tickets/"}catch(e){o.textContent=e.message||"Failed to create ticket",o.className="auth-message error",i.disabled=!1,i.textContent="Submit Ticket"}})}document.addEventListener("DOMContentLoaded",function(){if(t=window.furiAPI,!t)return;c(),m()})})()
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.