(function (global) { var definitions = { download: { endpoint: "/ui/dialog/download", elementId: "downloadAppDialog", css: "/static/css/downloadAppDialog.css?v=2", js: "/static/js/pages/downloadAppDialog.js?v=4", open: "openDownloadAppDialog", }, login: { endpoint: "/ui/dialog/login", elementId: "loginDialog", css: "/static/css/loginDialog.css?v=2", js: "/static/js/pages/loginDialog.js?v=3", open: "openLoginDialog", }, register: { endpoint: "/ui/dialog/register", elementId: "registerDialog", css: "/static/css/registerDialog.css?v=2", js: "/static/js/pages/registerDialog.js?v=2", open: "openRegisterDialog", }, }; var pending = Object.create(null); var stubOpeners = Object.create(null); function hasRealOpener(name) { var definition = definitions[name]; return ( !!definition && typeof global[definition.open] === "function" && global[definition.open] !== stubOpeners[name] ); } function loadStylesheet(href) { var existing = document.querySelector('link[rel="stylesheet"][href="' + href + '"]'); if (existing) return Promise.resolve(); return new Promise(function (resolve, reject) { var link = document.createElement("link"); var settled = false; function finish(ok, err) { if (settled) return; settled = true; if (ok) resolve(); else reject(err || new Error("dialog stylesheet failed")); } link.rel = "stylesheet"; link.href = href; link.onload = function () { finish(true); }; link.onerror = function () { finish(false, new Error("dialog stylesheet failed")); }; document.head.appendChild(link); // 部分浏览器对缓存 CSS 不触发 onload,避免一直卡住弹窗 requestAnimationFrame(function () { try { if (link.sheet) finish(true); } catch (e) {} }); setTimeout(function () { finish(true); }, 1500); }); } function loadScript(src) { var existing = document.querySelector('script[src="' + src + '"]'); if (existing) { if (existing.getAttribute("data-loaded") === "1") return Promise.resolve(); return new Promise(function (resolve, reject) { existing.addEventListener("load", resolve, { once: true }); existing.addEventListener("error", function () { reject(new Error("dialog script failed")); }, { once: true }); }); } return new Promise(function (resolve, reject) { var script = document.createElement("script"); script.src = src; script.async = true; script.onload = function () { script.setAttribute("data-loaded", "1"); resolve(); }; script.onerror = function () { reject(new Error("dialog script failed")); }; document.head.appendChild(script); }); } function fetchFragment(definition) { if (document.getElementById(definition.elementId)) { return Promise.resolve(); } return fetch(definition.endpoint, { credentials: "same-origin", headers: { "X-Requested-With": "XMLHttpRequest" }, }).then(function (response) { if (!response.ok) throw new Error("dialog fragment failed"); return response.text(); }).then(function (html) { if (document.getElementById(definition.elementId)) return; var host = document.createElement("div"); host.innerHTML = html; var dialog = host.querySelector("#" + definition.elementId); if (!dialog) throw new Error("dialog fragment is invalid"); document.body.appendChild(dialog); }); } function ensureDialog(name) { var definition = definitions[name]; if (!definition) return Promise.reject(new Error("unknown dialog")); if (document.getElementById(definition.elementId) && hasRealOpener(name)) { return Promise.resolve(); } if (pending[name]) return pending[name]; pending[name] = loadStylesheet(definition.css) .then(function () { return fetchFragment(definition); }) .then(function () { if (hasRealOpener(name)) return; return loadScript(definition.js); }) .then(function () { if (!document.getElementById(definition.elementId) || !hasRealOpener(name)) { throw new Error("dialog opener is unavailable"); } }) .catch(function (error) { delete pending[name]; throw error; }); return pending[name]; } function loginFallbackURL() { var redirect = ""; try { redirect = global.location.pathname || "/"; } catch (e) { redirect = "/"; } var url = "/login/"; if ( typeof redirect === "string" && redirect.charAt(0) === "/" && redirect.charAt(1) !== "/" && redirect !== "/" && redirect.indexOf("/?auth=") !== 0 ) { url = "/login/return" + redirect; } return url; } function openDialog(name, value) { return ensureDialog(name) .then(function () { var opener = global[definitions[name].open]; if (typeof opener !== "function" || opener === stubOpeners[name]) { throw new Error("dialog opener is unavailable"); } return opener(value); }) .catch(function (error) { if (name === "login") { try { var params = new URLSearchParams(global.location.search || ""); // 已在登录入口仍失败时避免死循环跳转 if (params.get("auth") !== "login") { global.location.href = loginFallbackURL(); } } catch (e) {} } throw error; }); } Object.keys(definitions).forEach(function (name) { var apiName = definitions[name].open; var stub = function (value) { return openDialog(name, value); }; stubOpeners[name] = stub; if (typeof global[apiName] !== "function") global[apiName] = stub; }); function triggerName(target) { if (!target || !target.closest) return ""; if (target.closest("[data-download-app-open]")) return "download"; if (target.closest("[data-register-dialog-open], a[href=\"#register\"], a[href=\"/register/\"]")) { return "register"; } if (target.closest("[data-login-dialog-open], a[href=\"#login\"], a[href=\"/login/\"]")) { return "login"; } return ""; } function isMobileViewport() { return typeof global.matchMedia === "function" ? global.matchMedia("(max-width: 767px)").matches : (global.innerWidth || 0) <= 767; } document.addEventListener("click", function (event) { var name = triggerName(event.target); if (!name) return; // 移动端下载入口保留原生链接行为,直接访问 appAddr,不加载下载弹窗。 if (name === "download" && isMobileViewport()) return; event.preventDefault(); event.stopPropagation(); if (event.stopImmediatePropagation) event.stopImmediatePropagation(); openDialog(name).catch(function () {}); }, true); function openFromLocation() { var params = new URLSearchParams(global.location.search || ""); var auth = params.get("auth"); if (auth === "login" || auth === "register") { openDialog(auth).catch(function () {}); return; } if (global.location.hash === "#login") openDialog("login").catch(function () {}); if (global.location.hash === "#register") openDialog("register").catch(function () {}); } if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", openFromLocation, { once: true }); } else { openFromLocation(); } global.ensureDialog = ensureDialog; })(window);