/*! * Khazna AI Assistant — embeddable widget * Usage (LibGuides "Media / Widget" box, or any page): *
* * Options (data-* on the div): * data-api backend base URL (default: origin this script was loaded from) * data-scope initial tab: all | publications | persons | organisations | datasets * data-height box height in px (default 680; "auto" = grow with page, used by the Space's own page) * data-scopes comma list of tabs to show (default all five) * data-model gpt | claude */ (function () { "use strict"; var SCRIPT = document.currentScript; var DEFAULT_API = SCRIPT ? new URL(SCRIPT.src, location.href).origin : ""; var SCOPE_LABELS = { all: "Everything", publications: "Publications", theses: "Theses", persons: "Profiles", organisations: "Departments", projects: "Projects", equipment: "Equipment", collaborations: "Collaborations", datasets: "Datasets" }; var EXAMPLES = { all: ["KU research on hydrogen storage since 2022", "Who at KU works on agricultural robotics?", "How do I submit my thesis to Khazna?"], publications: ["Machine learning for corrosion prediction", "KU journal articles on 6G networks", "How many KU publications on COVID-19?"], theses: ["PhD theses on solar-powered desalination", "Master's theses on cybersecurity", "Theses supervised in robotics since 2022"], persons: ["Experts in lithium-ion batteries", "Who works on Arabic natural language processing?", "Find a supervisor for 6G wireless research"], organisations: ["Research centres working on space technology", "Department of Mechanical Engineering", "Labs focused on water and environment"], projects: ["Funded projects on water security", "Research projects on autonomous vehicles", "Projects funded by ASPIRE on AI"], equipment: ["Which labs have an electron microscope?", "Mass spectrometry facilities", "3D printing equipment"], datasets: ["Datasets on air quality in the UAE", "Seismic or geophysics datasets", "Medical imaging datasets"] }; var CSS = [ ".khz{--b:#0b3a63;--a:#b98a2e;--ink:#1b2430;--mu:#5d6876;--ln:#e1dfd8;--bg:#f7f6f2;--pn:#fff;--oa:#1f7a4d;--ch:#eef2f6;", "font:15px/1.55 'IBM Plex Sans',-apple-system,'Segoe UI',Roboto,Arial,sans-serif;color:var(--ink);background:var(--bg);", "border:1px solid var(--ln);border-radius:12px;display:flex;flex-direction:column;overflow:hidden;text-align:left;box-sizing:border-box}", ".khz *,.khz *:before,.khz *:after{box-sizing:border-box}", ".khz p,.khz ul{margin:0 0 8px}.khz ul{padding-left:20px}.khz li{margin:2px 0}", ".khz a{color:var(--b)}", ".khz button{font:inherit;line-height:1.3;cursor:pointer;text-transform:none;letter-spacing:normal;box-shadow:none}", ".khz-hd{display:flex;align-items:center;gap:10px;padding:12px 14px;background:var(--pn);border-bottom:1px solid var(--ln)}", ".khz-mk{flex:0 0 32px;height:32px;border-radius:8px;background:var(--b);color:#fff;display:flex;align-items:center;justify-content:center;font:600 16px Georgia,serif}", ".khz-hd b{display:block;font-size:15px}.khz-hd small{color:var(--mu);font-size:12px}", ".khz-tabs{display:flex;gap:4px;padding:8px 10px 0;background:var(--pn);border-bottom:1px solid var(--ln);overflow-x:auto;scrollbar-width:none}", ".khz-tab{background:none;border:0;border-bottom:2px solid transparent;color:var(--mu);padding:7px 10px;white-space:nowrap;border-radius:0}", ".khz-tab[aria-selected=true]{color:var(--b);border-bottom-color:var(--b);font-weight:600}", ".khz-log{flex:1;overflow-y:auto;padding:14px}", ".khz-intro{color:var(--mu);margin:0 0 12px}", ".khz-ex{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:6px}", ".khz-ex button{background:var(--pn);border:1px solid var(--ln);color:var(--ink);border-radius:999px;padding:5px 11px;font-size:13px}", ".khz-ex button:hover{border-color:var(--b)}", ".khz-m{margin:12px 0}.khz-u{display:flex;justify-content:flex-end}", ".khz-u span{background:var(--b);color:#fff;border-radius:14px 14px 4px 14px;padding:8px 13px;max-width:85%;white-space:pre-wrap}", ".khz-ans{background:var(--pn);border:1px solid var(--ln);border-radius:4px 14px 14px 14px;padding:12px 14px}", ".khz-ans p:last-child,.khz-ans ul:last-child{margin-bottom:0}", ".khz-wait{color:var(--mu);font-style:italic}", ".khz-cite{text-decoration:none;font-weight:600;font-size:11px;vertical-align:super;margin-left:1px}", ".khz-sr{margin:7px 2px 0;font-size:12px;color:var(--mu);display:flex;flex-wrap:wrap;gap:5px;align-items:center}", ".khz-chip{background:var(--ch);border-radius:5px;padding:1px 7px;font:12px ui-monospace,Menlo,Consolas,monospace;color:var(--ink)}", ".khz-cards{display:grid;gap:8px;margin-top:9px}", ".khz-c{background:var(--pn);border:1px solid var(--ln);border-left:3px solid var(--b);border-radius:8px;padding:10px 13px}", ".khz-c.p{border-left-color:var(--a)}.khz-c.o{border-left-color:#5b6b7d}.khz-c.d{border-left-color:var(--oa)}", ".khz-c.fl{outline:2px solid var(--a)}", ".khz-c .t{font-weight:600;color:var(--ink);text-decoration:none}.khz-c .t:hover{text-decoration:underline}", ".khz-c .n{color:var(--mu);font-weight:500;margin-right:5px}", ".khz-c .k{font-size:10.5px;text-transform:uppercase;letter-spacing:.05em;color:var(--mu);margin-left:6px}", ".khz-c .mt{color:var(--mu);font-size:13px;margin-top:2px}.khz-c .mt b{color:var(--ink);font-weight:500}", ".khz-c .ln{margin-top:5px;display:flex;gap:10px;flex-wrap:wrap;font-size:12.5px}", ".khz-bd{font-size:11px;border-radius:4px;padding:0 6px;border:1px solid currentColor;color:var(--oa)}", ".khz-more{background:none;border:1px dashed var(--ln);color:var(--mu);border-radius:8px;padding:7px;width:100%}", ".khz-fb{margin-top:6px;font-size:12px;color:var(--mu);display:flex;gap:6px;align-items:center}", ".khz-fb button{background:none;border:1px solid var(--ln);border-radius:6px;padding:1px 8px;color:var(--mu)}", ".khz-ft{border-top:1px solid var(--ln);background:var(--pn);padding:10px}", ".khz-f{display:flex;gap:8px}", ".khz-f textarea{flex:1;resize:none;border:1px solid var(--ln);background:var(--bg);color:var(--ink);border-radius:10px;padding:9px 11px;font:inherit;min-height:42px;max-height:120px;margin:0}", ".khz-f textarea:focus{outline:2px solid var(--b);outline-offset:-1px}", ".khz-f button{background:var(--b);color:#fff;border:0;border-radius:10px;padding:0 16px;font-weight:600}", ".khz-f button:disabled{opacity:.5;cursor:default}", ".khz-nt{color:var(--mu);font-size:11px;margin:6px 2px 0}", ".khz-nt a{color:inherit}", "@media (max-width:520px){.khz-u span{max-width:94%}.khz-hd small{display:none}}" ].join(""); function esc(s) { return String(s == null ? "" : s).replace(/[&<>"']/g, function (c) { return { "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }[c]; }); } function safeUrl(u) { return /^https?:\/\//i.test(u || "") ? esc(u) : "#"; } function md(text, t) { var h = esc(text) .replace(/\*\*(.+?)\*\*/g, "$1") .replace(/(https?:\/\/[^\s<)]+)/g, '$1') .replace(/\[(\d+(?:\s*,\s*\d+)*)\]/g, function (m, g) { return g.split(",").map(function (n) { n = n.trim(); return '[' + n + "]"; }).join(""); }); var out = [], list = false; h.split("\n").forEach(function (line) { var l = line.trim(); if (/^[-•*]\s+/.test(l)) { if (!list) { out.push(""); list = false; } if (l) out.push("

" + l + "

"); }); if (list) out.push(""); return out.join(""); } function card(r, t) { var id = ' data-id="' + t + "-" + r.n + '"', num = r.n ? '' + r.n + "" : ""; var link = ''; if (r.kind === "person") { var units = (r.organisations || []).map(function (o) { return o.name; }).filter(Boolean).join("; "); var ev = (r.evidence || []).map(function (e) { return '' + esc(e.title) + "" + (e.year ? " (" + e.year + ")" : ""); }).join("
"); return '
" + num + link + esc(r.name) + 'Profile
' + ((r.job_titles || []).length || units ? '
' + esc([(r.job_titles || []).join(", "), units].filter(Boolean).join(" · ")) + "
" : "") + ((r.keywords || []).length ? '
Expertise: ' + esc(r.keywords.slice(0, 6).join(", ")) + "
" : "") + (r.matching_outputs ? '
' + r.matching_outputs + " matching output" + (r.matching_outputs > 1 ? "s" : "") + "" + (ev ? "
" + ev : "") + "
" : "") + '
View profile in Khazna' + (r.orcid ? 'ORCID' : "") + "
"; } if (r.kind === "organisation") { return '
" + num + link + esc(r.name) + '' + esc(r.type || "Unit") + "
" + ((r.parents || []).length ? '
Part of ' + esc(r.parents.join(", ")) + "
" : "") + '
View unit, people & outputs
'; } var ku = {}; (r.ku_authors || []).forEach(function (a) { ku[a.name] = 1; }); var au = (r.authors || []).slice(0, 8).map(function (a) { return ku[a] ? "" + esc(a) + "" : esc(a); }).join(", ") + ((r.authors || []).length > 8 ? " et al." : ""); var meta = [r.year, r.type, r.venue].filter(Boolean).map(esc).join(" · "); return '
" + num + link + esc(r.title) + "" + (r.kind === "dataset" ? 'Dataset' : r.kind === "thesis" ? 'Thesis' : "") + "
" + '
' + (au || "—") + "
" + (meta ? '
' + meta + "
" : "") + '
View in Khazna' + (r.doi_url ? 'DOI' : "") + (r.open_access ? 'Open access' : "") + "
"; } function mount(root) { if (root.__khz) return; root.__khz = 1; var api = (root.getAttribute("data-api") || DEFAULT_API).replace(/\/$/, ""); var height = root.getAttribute("data-height") || "680"; var scopes = (root.getAttribute("data-scopes") || "all,publications,theses,persons,organisations,projects,equipment").split(",").map(function (s) { return s.trim(); }).filter(function (s) { return SCOPE_LABELS[s]; }); var scope = root.getAttribute("data-scope") || scopes[0] || "all"; var model = root.getAttribute("data-model") || "gpt"; var history = [], turn = 0, busy = false, uid = Math.random().toString(36).slice(2, 7); root.innerHTML = '
' + '
K
Khazna AI AssistantAsk in plain language — searches KU\'s research repository
' + '
' + scopes.map(function (s) { return '"; }).join("") + "
" + '

I turn your question into searches on Khazna and summarise what KU has published, who works on it, and where, citing every claim.

' + '
' + '

AI-generated from public Khazna records; answers may be incomplete. Need help? KU Library Research Support

'; var log = root.querySelector(".khz-log"), ex = root.querySelector(".khz-ex"), ta = root.querySelector("textarea"), btn = root.querySelector(".khz-f button"); function drawExamples() { if (!ex.isConnected) return; ex.innerHTML = (EXAMPLES[scope] || EXAMPLES.all).map(function (e) { return ""; }).join(""); ex.querySelectorAll("button").forEach(function (b) { b.onclick = function () { ask(b.textContent); }; }); } drawExamples(); root.querySelectorAll(".khz-tab").forEach(function (b) { b.onclick = function () { scope = b.getAttribute("data-s"); root.querySelectorAll(".khz-tab").forEach(function (x) { x.setAttribute("aria-selected", x === b); }); ta.placeholder = { persons: "e.g. Who at KU works on desalination membranes?", organisations: "e.g. Which centres work on robotics?", datasets: "e.g. Datasets on UAE groundwater" }[scope] || "e.g. What has KU published on 6G networks?"; drawExamples(); ta.focus(); }; }); function add(html, cls) { var d = document.createElement("div"); d.className = "khz-m " + cls; d.innerHTML = html; log.appendChild(d); log.scrollTop = log.scrollHeight; return d; } // Free HF Spaces sleep when idle; the first request after a sleep can take ~30-60 s or return 502/503. function post(body, onWake) { var tries = 0; function go() { tries++; return fetch(api + "/agent", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body) }) .then(function (r) { if ((r.status === 502 || r.status === 503 || r.status === 504) && tries < 6) { onWake(); return new Promise(function (ok) { setTimeout(ok, 8000); }).then(go); } return r.json(); }) .catch(function (e) { if (tries < 6) { onWake(); return new Promise(function (ok) { setTimeout(ok, 8000); }).then(go); } throw e; }); } return go(); } function ask(question) { if (busy) return; busy = true; btn.disabled = true; if (ex.parentNode) ex.parentNode.removeChild(ex); var t = uid + (++turn); add("" + esc(question) + "", "khz-u"); var p = add('
Searching Khazna…
', "khz-b"); post({ question: question, history: history, model: model, scope: scope }, function () { p.innerHTML = '
Waking up the assistant — this can take up to a minute after a quiet period…
'; }).then(function (d) { var h = '
' + md(d.answer || "No answer.", t) + "
"; var seenQ = {}, qs = (d.trace || []).filter(function (x) { var k = (x.query || "").toLowerCase(); if (!k || seenQ[k]) return false; seenQ[k] = 1; return true; }).map(function (x) { return '' + esc(x.query) + ""; }).join(""); if (qs) h += '
Searched: ' + qs + (d.portal_search_url ? ' · open in Khazna' : "") + "
"; var cards = (d.person ? [card(d.person, t)] : []).concat((d.search_results || []).map(function (r) { return card(r, t); })); if (cards.length) h += '
' + cards.join("") + "
"; if ((d.more_results || []).length) h += '
"; if (d.intent && !/social|rate_limited|empty/.test(d.intent)) h += '
Helpful?
'; p.innerHTML = h; var more = p.querySelector(".khz-more"); if (more) more.onclick = function () { more.nextElementSibling.hidden = false; more.parentNode.removeChild(more); }; p.querySelectorAll(".khz-fb button").forEach(function (b) { b.onclick = function () { fetch(api + "/feedback", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ question: question, rating: b.getAttribute("data-r") }) }); b.parentNode.textContent = "Thanks for the feedback."; }; }); p.querySelectorAll(".khz-cite").forEach(function (a) { a.onclick = function (e) { e.preventDefault(); var el = root.querySelector('[data-id="' + a.getAttribute("data-c") + '"]'); if (!el) return; if (el.parentNode.hidden) el.parentNode.hidden = false; el.scrollIntoView({ behavior: "smooth", block: "center" }); el.classList.add("fl"); setTimeout(function () { el.classList.remove("fl"); }, 1400); }; }); history.push({ role: "user", content: question }, { role: "assistant", content: d.answer || "" }); history = history.slice(-8); }).catch(function () { p.innerHTML = '
Sorry — the assistant is unavailable right now. You can search directly at khazna.ku.ac.ae.
'; }).then(function () { busy = false; btn.disabled = false; log.scrollTop = log.scrollHeight; }); } root.querySelector("form").onsubmit = function (e) { e.preventDefault(); var q = ta.value.trim(); if (!q) return; ta.value = ""; ask(q); }; ta.onkeydown = function (e) { if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); root.querySelector("form").requestSubmit ? root.querySelector("form").requestSubmit() : root.querySelector("form").onsubmit(e); } }; fetch(api + "/health").catch(function () {}); // wake a sleeping Space as soon as the guide loads } function init() { if (!document.getElementById("khz-css")) { var st = document.createElement("style"); st.id = "khz-css"; st.textContent = CSS; document.head.appendChild(st); } document.querySelectorAll("[data-khazna-assistant]").forEach(mount); } if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", init); else init(); })();