UNPKG

@caspingus/lt

Version:

A utility library of helpers and extensions useful when working with Learnosity APIs.

717 lines (716 loc) 17.2 kB
import { n as e, t } from "../../player-BsvIsWXI.js"; import { r as n, t as r } from "../../extensionsFactory-hk5ijx1G.js"; import { networkStatus as i } from "./networkStatus.js"; //#region src/utils/userAgent.js var a = { edge: /Edg(?:A|iOS)?\/([\d.]+)/, opr: /OPR\/([\d.]+)/, samsung: /SamsungBrowser\/([\d.]+)/, chrome: /Chrome\/([\d.]+)/, firefox: /Firefox\/([\d.]+)/, safariTail: /\bSafari\/[\d.]+$/, version: /Version\/([\d.]+)/, win: /Windows NT ([\d.]+)/, mac: /Mac OS X (\d+[_.]\d+(?:[_.]\d+)?)/, ios: /(?:iPhone|iPad|iPod).*OS (\d+[_.]\d+(?:[_.]\d+)?)/, android: /Android ([\d.]+)/, cros: /CrOS [\w_]+ ([\d.]+)/, linux: /\bLinux\b/, mobileHint: /\bMobi\b/, ipad: /\biPad\b/, tablet: /\bTablet\b/, bot: /\b(bot|crawler|spider|crawling)\b/i, webkit: /AppleWebKit\/([\d.]+)/ }; function o(e) { return a.edge.test(e) ? _({ name: "Edge", version: m(a.edge, e) }) : a.opr.test(e) ? _({ name: "Opera", version: m(a.opr, e) }) : a.samsung.test(e) ? _({ name: "Samsung Internet", version: m(a.samsung, e) }) : /Chrome\//.test(e) && !a.edge.test(e) && !a.opr.test(e) && !a.samsung.test(e) ? _({ name: "Chrome", version: m(a.chrome, e) }) : a.firefox.test(e) ? _({ name: "Firefox", version: m(a.firefox, e) }) : !/Chrome|Chromium|Edg|OPR|SamsungBrowser/.test(e) && a.safariTail.test(e) ? _({ name: "Safari", version: m(a.version, e) }) : {}; } function s(e) { return /Macintosh/.test(e) && /Mobile\//.test(e) && /Safari/.test(e) ? { name: "iOS", version: void 0 } : a.win.test(e) ? { name: "Windows", version: m(a.win, e) || void 0 } : /iPhone|iPad|iPod/.test(e) ? { name: "iOS", version: g(m(a.ios, e)) } : a.android.test(e) ? { name: "Android", version: m(a.android, e) } : a.cros.test(e) ? { name: "Chrome OS", version: m(a.cros, e) } : a.mac.test(e) ? { name: "macOS", version: g(m(a.mac, e)) } : a.linux.test(e) ? { name: "Linux", version: void 0 } : {}; } function c(e) { return a.bot.test(e) ? "bot" : a.ipad.test(e) || a.tablet.test(e) ? "tablet" : a.mobileHint.test(e) || /Android/.test(e) && !/Tablet|iPad/.test(e) ? "mobile" : "desktop"; } function l(e) { return a.webkit.test(e) ? { name: "WebKit", version: m(a.webkit, e) } : /Gecko\/\d/.test(e) && /Firefox\/([\d.]+)/.test(e) ? { name: "Gecko", version: m(a.firefox, e) } : /Chrome\/|Edg\/|OPR\/|SamsungBrowser\//.test(e) ? { name: "Blink" } : {}; } function u(e, t) { try { let n = t && (t.brands || t.getBrands?.()); if (!n || !n.length) return e; let r = n.filter((e) => !/Not.?A.?Brand/i.test(e.brand)).sort((e, t) => parseInt(t.version, 10) - parseInt(e.version, 10))[0]; if (r && r.brand) return _({ name: r.brand, version: e.version }); } catch {} return e; } function d(e) { return e ||= "", { ua: e, browser: o(e), os: s(e), deviceType: c(e), engine: l(e), reducedUA: /\bChrome\/\d+\.0\.0\.0\b/.test(e) || /\bEdg\/\d+\.0\.0\.0\b/.test(e) || !1 }; } function f() { let e = typeof navigator < "u" && navigator.userAgent ? navigator.userAgent : "", t = typeof navigator < "u" ? navigator.userAgentData : void 0, n = d(e); return t && (t.brands || t.getBrands) && (n.browser = u(n.browser, t)), n.meta = { source: t && (t.brands || t.getBrands) ? "ua-ch-low" : "ua", usedHighEntropy: !1, isReducedUA: h(e) }, delete n.reducedUA, n; } async function p() { let e = typeof navigator < "u" && navigator.userAgent ? navigator.userAgent : "", t = typeof navigator < "u" ? navigator.userAgentData : void 0; if (t && typeof t.getHighEntropyValues == "function") try { let n = await t.getHighEntropyValues([ "platform", "platformVersion", "uaFullVersion", "architecture", "bitness", "model", "fullVersionList" ]), r = n.fullVersionList || t.brands || t.getBrands?.() || [], i = [ "Chrome", "Google Chrome", "Microsoft Edge", "Edge", "Chromium", "Opera", "OPR", "Samsung Internet", "Firefox" ], a = r.find((e) => i.some((t) => e.brand.toLowerCase().includes(t.toLowerCase()))) || r.find((e) => !/Not.?A.?Brand/i.test(e.brand)) || null, o = a?.brand || r[0] && r[0].brand || void 0, s = a?.version || n.uaFullVersion || void 0, c = n.platform || void 0, l = n.platformVersion || void 0, u; return /Android/i.test(c) ? u = "mobile" : /Windows|macOS|Chrome OS|Linux|iOS/i.test(c) && (u = "desktop"), { ua: e, browser: _({ name: o, version: s }), os: { name: c, version: l }, deviceType: u, engine: void 0, arch: n.architecture, bitness: n.bitness, model: n.model, meta: { source: "ua-ch-high", usedHighEntropy: !0, isReducedUA: h(e) } }; } catch {} let n = f(); return n.meta.source = n.meta && n.meta.source || "ua", n; } function m(e, t) { let n = t.match(e); return n ? n[1] : void 0; } function h(e) { return /\bChrome\/\d+\.0\.0\.0\b/.test(e) || /\bEdg\/\d+\.0\.0\.0\b/.test(e); } function g(e) { return e ? e.replace(/_/g, ".") : void 0; } function _(e) { if (e && e.version && typeof e.version == "string") { let t = e.version.split(".")[0]; t && (e.versionMajor = t); } return e; } //#endregion //#region src/assessment/extensions/events/index.js var v = { initialised: !1, events: { events: [], network: { speed: null, status: "online" }, session: null, user: null } }, y = null, b = /* @__PURE__ */ new Set(), x = { lastX: null, lastY: null, prevX: null, prevY: null, hasTouch: !1 }; function S() { if (r.isVerticalLayout()) { r.utils.logger.warn("Event log is not currently supported in vertical layout."); return; } v.initialised ||= (C(), r.eventBus.on("item:load", w, "events"), t() || queueMicrotask(w), E(), T(), M(), A(), j(), k(), !0); } function C() { t() ? N({ type: "test:ready", timestamp: L() }) : (N({ type: "test:start", timestamp: L() }), e() && N({ type: "test:reading:start", timestamp: L() })); } function w() { let e = r.itemReference(); if (!e || e === y) return; y = e; let t = L(); P(t), b.has(e) || (b.add(e), D(e, t), O(e)); } async function T() { v.events.user = r.userId(), v.events.session = r.sessionId(), v.events.environment = await R(), v.events.network.speed = i.checkSpeed(); } function E() { r.eventBus.on("test:start", () => { N({ type: "test:start", timestamp: L() }); }), r.eventBus.on("unfocused", () => { N({ type: "unfocused", item: r.itemReference(), timestamp: L() }); }), r.eventBus.on("focused", () => { N({ type: "focused", item: r.itemReference(), timestamp: L() }); }), r.eventBus.on("test:reading:start", () => { N({ type: "test:reading:start", timestamp: L() }); }), r.eventBus.on("test:reading:end", () => { N({ type: "test:reading:end", timestamp: L() }); }), r.eventBus.on("item:warningOnChange", () => { N({ type: "item:warningOnChange", item: r.itemReference(), timestamp: L() }); }), r.eventBus.on("items:fetch:done", () => { N({ type: "items:fetch:done", item: r.itemReference(), timestamp: L() }); }), r.eventBus.on("section:changed", () => { N({ type: "section:changed", timestamp: L() }); }), r.eventBus.on("test:panel:show", async () => { let e = await F(); ["dialog:pause"].includes(e) || N({ type: e, timestamp: L() }); }), r.eventBus.on("test:pause", () => { N({ type: "test:pause", timestamp: L() }); }), r.eventBus.on("test:resume", () => { N({ type: "test:resume", timestamp: L() }); }), r.eventBus.on("test:save", () => { N({ type: "test:save", timestamp: L() }); }), r.eventBus.on("test:save:success", () => { N({ type: "test:save:success", timestamp: L() }); }), r.eventBus.on("test:save:error", () => { N({ type: "test:save:error", timestamp: L() }); }), r.eventBus.on("test:submit", () => { N({ type: "test:submit", timestamp: L() }); }), r.eventBus.on("test:submit:success", () => { N({ type: "test:submit:success", timestamp: L() }); }), r.eventBus.on("test:submit:error", () => { N({ type: "test:submit:error", timestamp: L() }); }), r.eventBus.on("test:finished:save", () => { N({ type: "test:finished:save", timestamp: L() }); }), r.eventBus.on("test:finished:submit", () => { N({ type: "test:finished:submit", timestamp: L() }); }), r.eventBus.on("test:finished:discard", () => { N({ type: "test:finished:discard", timestamp: L() }); }), r.eventBus.on("time:end", () => { N({ type: "time:end", timestamp: L() }); }); } function D(e, t) { let n = [ "audio", "chemistryessayV2", "drawing", "fileupload", "formulaessayV2", "imageupload", "longtext", "longtextV2", "plaintext", "video" ], i = [ "association", "bowtie", "classification", "clozeassociation", "clozedropdown", "clozetext", "graphplotting", "gridded", "hotspot", "imageclozeassociation", "imageclozedropdown", "imageclozetext", "numberline", "numberlineplot", "orderlist", "shorttext", "simplechart", "simpleshading", "tokenhighlight" ], a = {}, o = [ "formulaessayV2", "longtextV2", "plaintext", "shorttext" ], s = {}, c = {}, l = ["mcq"], u = r.item(e); (u && u.questions ? u.questions.map((e) => e.response_id) : []).forEach((u) => { let d = r.itemsApp().question(u), f = d.getQuestion(), p = f.type, m = f.metadata.widget_reference; if (["audio", "video"].includes(p) && (d.on("recording:started", () => { N({ type: "recording:started", item: e, question: m, timestamp: L() }); }), d.on("recording:paused", () => { N({ type: "recording:paused", item: e, question: m, timestamp: L() }); }), d.on("recording:resumed", () => { N({ type: "recording:resumed", item: e, question: m, timestamp: L() }); }), d.on("recording:stopped", () => { N({ type: "recording:stopped", item: e, question: m, timestamp: L() }); })), o.includes(p)) { s[u] = { wordCount: 0, timestamp: L() }; let t = document.getElementById(u); t && t.addEventListener("paste", () => { N({ type: "question:paste", item: e, question: m, responseId: u, timestamp: L() }); }); } if (l.includes(p)) { let t = document.getElementById(u); t && t.addEventListener("click", (t) => { if (t.detail === 0) return; if (x.lastX === null) { x.hasTouch || queueMicrotask(() => { N({ type: "question:suspiciousClick", item: e, question: m, responseId: u, data: { reason: "noMouseMovement" }, timestamp: L() }); }); return; } let n = Math.round(Math.sqrt((t.clientX - x.lastX) ** 2 + (t.clientY - x.lastY) ** 2)) <= 20; if (x.prevX === null && n && !x.hasTouch) { queueMicrotask(() => { N({ type: "question:suspiciousClick", item: e, question: m, responseId: u, data: { reason: "noMouseMovement" }, timestamp: L() }); }); return; } if (x.prevX !== null && n) { let t = Math.round(Math.sqrt((x.lastX - x.prevX) ** 2 + (x.lastY - x.prevY) ** 2)); t > 150 && queueMicrotask(() => { N({ type: "question:suspiciousClick", item: e, question: m, responseId: u, data: { reason: "mouseTeleport", jumpDistance: t }, timestamp: L() }); }); } }); } d.on("changed", () => { let l = a[u] || 0, { revision: d, value: f } = r.questionResponse(u); if (n.includes(p) || i.includes(p) ? L() - l >= 3e4 && (a[u] = L(), N({ type: "question:changed", item: e, question: m, responseId: u, data: i.includes(p) ? { revision: d, value: f } : {}, timestamp: L() })) : N({ type: "question:changed", item: e, question: m, responseId: u, data: { revision: d, value: f }, timestamp: L() }), !c[u]) { c[u] = !0; let n = L() - t; n < 5e3 && N({ type: "question:suspiciousResponseSpeed", item: e, question: m, responseId: u, data: { reason: "fastResponse", ms: n }, timestamp: L() }); } if (o.includes(p) && f) { let t = I(f, p), n = s[u], r = L() - n.timestamp, i = t - n.wordCount; if (r > 0 && i > 0) { let t = Math.round(i / r * 6e4); t > 300 && N({ type: "question:suspiciousInput", item: e, question: m, responseId: u, data: { wpm: t, wordsDelta: i }, timestamp: L() }); } s[u] = { wordCount: t, timestamp: L() }; } }), d.on("masked", () => { N({ type: "question:masked", item: e, question: m, timestamp: L() }); }), d.on("validated", () => { N({ type: "question:validated", item: e, question: m, timestamp: L() }); }); }); } function O(e) { let t = r.item(e); if (!t) return; let n = [...new Set([...t.feature_ids || [], ...t.simplefeature_ids || []])], i = (t.features || []).reduce((e, t) => (e[t.feature_id] = t.type, e), {}); n.forEach((t) => { let n = r.itemsApp().feature(t), a = ["audio", "video"].includes(i[t]); n && a && (n.on("begin", () => { N({ type: "media:begin", item: e, timestamp: L() }); }), n.on("complete", () => { N({ type: "media:complete", item: e, timestamp: L() }); }), n.on("playback:started", () => { N({ type: "playback:started", item: e, timestamp: L() }); }), n.on("playback:paused", () => { N({ type: "playback:paused", item: e, timestamp: L() }); }), n.on("playback:resumed", () => { N({ type: "playback:resumed", item: e, timestamp: L() }); }), n.on("playback:stopped", () => { N({ type: "playback:stopped", item: e, timestamp: L() }); }), n.on("playback:complete", () => { N({ type: "playback:complete", item: e, timestamp: L() }); })); }); } function k() { if (typeof BroadcastChannel > "u") return; let e = new BroadcastChannel(`lt_session_${r.sessionId()}`); e.onmessage = (t) => { t.data.type === "tab:ping" && e.postMessage({ type: "tab:pong" }), t.data.type === "tab:pong" && N({ type: "tab:duplicate", item: r.itemReference(), timestamp: L() }); }, e.postMessage({ type: "tab:ping" }), window.addEventListener("beforeunload", () => e.close()); } function A() { document.addEventListener("visibilitychange", () => { document.visibilityState === "hidden" ? N({ type: "page:blur", item: r.itemReference(), timestamp: L() }) : N({ type: "page:focus", item: r.itemReference(), timestamp: L() }); }); } function j() { document.addEventListener("mousemove", (e) => { x.prevX = x.lastX, x.prevY = x.lastY, x.lastX = e.clientX, x.lastY = e.clientY; }, { passive: !0 }), document.addEventListener("touchstart", () => { x.hasTouch = !0; }, { once: !0, passive: !0 }); } function M() { document.addEventListener("LTNetworkOnline", () => { v.events.network.status === "offline" && (v.events.network.status = "online", N({ type: "network:online", item: r.itemReference(), timestamp: L() })); }), document.addEventListener("LTNetworkOffline", () => { v.events.network.status === "online" && (v.events.network.status = "offline", N({ type: "network:offline", item: r.itemReference(), timestamp: L() })); }); } function N(e) { v.events.events.push(e); } function P(e) { N({ type: "item:load", item: r.itemReference(), data: { num: r.itemPosition() }, timestamp: e }); } function F() { return new Promise((e) => { setTimeout(() => { let t = document.querySelectorAll(".lrn-assess-dialogs > .lrn-dialog-default"), n = "", r = "", i = Array.from(t).filter((e) => e.style.display === "block").map((e) => ({ id: e.id, class: e.className })); if (i.length === 0) { e(""); return; } let a = i[0]; switch (a?.id ? n = a.id.replace(/\d+/g, "") : a?.class.includes("review-screen") && (n = "review-screen"), n) { case "accessibility-panel": r = "dialog:accessibility"; break; case "custom-dialog": r = "dialog:custom-dialog"; break; case "module-load-error-dialog": r = "dialog:module-load-error"; break; case "review-screen": r = "dialog:review-screen"; break; case "test-asset-upload-error-dialog": r = "dialog:asset-upload-error"; break; case "test-error-dialog": r = "dialog:error"; break; case "test-pause-dialog": r = "dialog:pause"; break; case "test-save-submit": r = "dialog:save-submit"; break; default: break; } e(r); }, 500); }); } function I(e, t) { if (!e) return 0; let n = e; return ["formulaessayV2", "longtextV2"].includes(t) && (n = e.replace(/<[^>]+>/g, " ")), n.trim().split(/\s+/).filter((e) => e.length > 0).length; } function L() { return Date.now(); } async function R() { return p(); } function z() { return v.events; } var B = n("events", S, { getEvents: z }); //#endregion export { B as events };