// ==UserScript==
// @name 手机百度贴吧自动展开楼层
// @namespace http://tampermonkey.net/
// @homepage https://gf.qytechs.cn/scripts/445657
// @version 3.1
// @description 有时候用手机的浏览器打开百度贴吧,只想看一眼就走,并不想打开APP,这个脚本用于帮助用户自动展开楼层。注意:只支持手机浏览器,测试环境为Iceraven+Tampermonkey
// @author voeoc
// @match https://tieba.baidu.com/p/*
// @match https://jump2.bdimg.com/p/*
// @match https://tiebac.baidu.com/p/*
// @connect https://tieba.baidu.com/mg/o/getFloorData
// @connect https://jump2.bdimg.com/mg/o/getFloorData
// @connect https://tiebac.baidu.com/mg/o/getFloorData
// @icon https://tieba.baidu.com/favicon.ico
// @resource swal_css https://cdn.jsdelivr.net/npm/@sweetalert2/theme-dark/dark.css
// @grant unsafeWindow
// @grant GM_addStyle
// @grant GM_xmlhttpRequest
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_listValues
// @grant GM_registerMenuCommand
// @grant GM_unregisterMenuCommand
// @grant GM_getResourceText
// @license MIT
// ==/UserScript==
//引用https://cdn.jsdelivr.net/npm/sweetalert2/dist/sweetalert2.min.js
!function (e, t) { "object" == typeof exports && "undefined" != typeof module ? module.exports = t() : "function" == typeof define && define.amd ? define(t) : (e = e || self).Sweetalert2 = t() }(this, function () { "use strict"; const q = "SweetAlert2:", H = e => e.charAt(0).toUpperCase() + e.slice(1), i = e => Array.prototype.slice.call(e), a = e => { console.warn("".concat(q, " ").concat("object" == typeof e ? e.join(" ") : e)) }, l = e => { console.error("".concat(q, " ").concat(e)) }, V = [], N = (e, t) => { e = '"'.concat(e, '" is deprecated and will be removed in the next major release. Please use "').concat(t, '" instead.'), V.includes(e) || (V.push(e), a(e)) }, R = e => "function" == typeof e ? e() : e, F = e => e && "function" == typeof e.toPromise, u = e => F(e) ? e.toPromise() : Promise.resolve(e), U = e => e && Promise.resolve(e) === e, r = { title: "", titleText: "", text: "", html: "", footer: "", icon: void 0, iconColor: void 0, iconHtml: void 0, template: void 0, toast: !1, showClass: { popup: "swal2-show", backdrop: "swal2-backdrop-show", icon: "swal2-icon-show" }, hideClass: { popup: "swal2-hide", backdrop: "swal2-backdrop-hide", icon: "swal2-icon-hide" }, customClass: {}, target: "body", color: void 0, backdrop: !0, heightAuto: !0, allowOutsideClick: !0, allowEscapeKey: !0, allowEnterKey: !0, stopKeydownPropagation: !0, keydownListenerCapture: !1, showConfirmButton: !0, showDenyButton: !1, showCancelButton: !1, preConfirm: void 0, preDeny: void 0, confirmButtonText: "OK", confirmButtonAriaLabel: "", confirmButtonColor: void 0, denyButtonText: "No", denyButtonAriaLabel: "", denyButtonColor: void 0, cancelButtonText: "Cancel", cancelButtonAriaLabel: "", cancelButtonColor: void 0, buttonsStyling: !0, reverseButtons: !1, focusConfirm: !0, focusDeny: !1, focusCancel: !1, returnFocus: !0, showCloseButton: !1, closeButtonHtml: "×", closeButtonAriaLabel: "Close this dialog", loaderHtml: "", showLoaderOnConfirm: !1, showLoaderOnDeny: !1, imageUrl: void 0, imageWidth: void 0, imageHeight: void 0, imageAlt: "", timer: void 0, timerProgressBar: !1, width: void 0, padding: void 0, background: void 0, input: void 0, inputPlaceholder: "", inputLabel: "", inputValue: "", inputOptions: {}, inputAutoTrim: !0, inputAttributes: {}, inputValidator: void 0, returnInputValueOnDeny: !1, validationMessage: void 0, grow: !1, position: "center", progressSteps: [], currentProgressStep: void 0, progressStepsDistance: void 0, willOpen: void 0, didOpen: void 0, didRender: void 0, willClose: void 0, didClose: void 0, didDestroy: void 0, scrollbarPadding: !0 }, W = ["allowEscapeKey", "allowOutsideClick", "background", "buttonsStyling", "cancelButtonAriaLabel", "cancelButtonColor", "cancelButtonText", "closeButtonAriaLabel", "closeButtonHtml", "color", "confirmButtonAriaLabel", "confirmButtonColor", "confirmButtonText", "currentProgressStep", "customClass", "denyButtonAriaLabel", "denyButtonColor", "denyButtonText", "didClose", "didDestroy", "footer", "hideClass", "html", "icon", "iconColor", "iconHtml", "imageAlt", "imageHeight", "imageUrl", "imageWidth", "preConfirm", "preDeny", "progressSteps", "returnFocus", "reverseButtons", "showCancelButton", "showCloseButton", "showConfirmButton", "showDenyButton", "text", "title", "titleText", "willClose"], z = {}, _ = ["allowOutsideClick", "allowEnterKey", "backdrop", "focusConfirm", "focusDeny", "focusCancel", "returnFocus", "heightAuto", "keydownListenerCapture"], K = e => Object.prototype.hasOwnProperty.call(r, e), Y = e => -1 !== W.indexOf(e), Z = e => z[e], J = e => { !e.backdrop && e.allowOutsideClick && a('"allowOutsideClick" parameter requires `backdrop` parameter to be set to `true`'); for (const n in e) t = n, K(t) || a('Unknown parameter "'.concat(t, '"')), e.toast && (t = n, _.includes(t) && a('The parameter "'.concat(t, '" is incompatible with toasts'))), t = n, Z(t) && N(t, Z(t)); var t }; var e = e => { const t = {}; for (const n in e) t[e[n]] = "swal2-" + e[n]; return t }; const p = e(["container", "shown", "height-auto", "iosfix", "popup", "modal", "no-backdrop", "no-transition", "toast", "toast-shown", "show", "hide", "close", "title", "html-container", "actions", "confirm", "deny", "cancel", "default-outline", "footer", "icon", "icon-content", "image", "input", "file", "range", "select", "radio", "checkbox", "label", "textarea", "inputerror", "input-label", "validation-message", "progress-steps", "active-progress-step", "progress-step", "progress-step-line", "loader", "loading", "styled", "top", "top-start", "top-end", "top-left", "top-right", "center", "center-start", "center-end", "center-left", "center-right", "bottom", "bottom-start", "bottom-end", "bottom-left", "bottom-right", "grow-row", "grow-column", "grow-fullscreen", "rtl", "timer-progress-bar", "timer-progress-bar-container", "scrollbar-measure", "icon-success", "icon-warning", "icon-info", "icon-question", "icon-error"]), o = e(["success", "warning", "info", "question", "error"]), m = () => document.body.querySelector(".".concat(p.container)), t = e => { const t = m(); return t ? t.querySelector(e) : null }, n = e => t(".".concat(e)), g = () => n(p.popup), s = () => n(p.icon), X = () => n(p.title), $ = () => n(p["html-container"]), G = () => n(p.image), Q = () => n(p["progress-steps"]), ee = () => n(p["validation-message"]), h = () => t(".".concat(p.actions, " .").concat(p.confirm)), f = () => t(".".concat(p.actions, " .").concat(p.deny)); const d = () => t(".".concat(p.loader)), b = () => t(".".concat(p.actions, " .").concat(p.cancel)), v = () => n(p.actions), te = () => n(p.footer), ne = () => n(p["timer-progress-bar"]), oe = () => n(p.close), ie = () => { const e = i(g().querySelectorAll('[tabindex]:not([tabindex="-1"]):not([tabindex="0"])')).sort((e, t) => { e = parseInt(e.getAttribute("tabindex")), t = parseInt(t.getAttribute("tabindex")); return t < e ? 1 : e < t ? -1 : 0 }); var t = i(g().querySelectorAll('\n a[href],\n area[href],\n input:not([disabled]),\n select:not([disabled]),\n textarea:not([disabled]),\n button:not([disabled]),\n iframe,\n object,\n embed,\n [tabindex="0"],\n [contenteditable],\n audio[controls],\n video[controls],\n summary\n')).filter(e => "-1" !== e.getAttribute("tabindex")); return (t => { const n = []; for (let e = 0; e < t.length; e++)-1 === n.indexOf(t[e]) && n.push(t[e]); return n })(e.concat(t)).filter(e => E(e)) }, ae = () => w(document.body, p.shown) && !w(document.body, p["toast-shown"]) && !w(document.body, p["no-backdrop"]), re = () => g() && w(g(), p.toast); function se(e) { var t = 1 < arguments.length && void 0 !== arguments[1] && arguments[1]; const n = ne(); E(n) && (t && (n.style.transition = "none", n.style.width = "100%"), setTimeout(() => { n.style.transition = "width ".concat(e / 1e3, "s linear"), n.style.width = "0%" }, 10)) } const c = { previousBodyPadding: null }, y = (t, e) => { if (t.textContent = "", e) { const n = new DOMParser, o = n.parseFromString(e, "text/html"); i(o.querySelector("head").childNodes).forEach(e => { t.appendChild(e) }), i(o.querySelector("body").childNodes).forEach(e => { t.appendChild(e) }) } }, w = (t, e) => { if (!e) return !1; var n = e.split(/\s+/); for (let e = 0; e < n.length; e++)if (!t.classList.contains(n[e])) return !1; return !0 }, ce = (t, n) => { i(t.classList).forEach(e => { Object.values(p).includes(e) || Object.values(o).includes(e) || Object.values(n.showClass).includes(e) || t.classList.remove(e) }) }, C = (e, t, n) => { if (ce(e, t), t.customClass && t.customClass[n]) { if ("string" != typeof t.customClass[n] && !t.customClass[n].forEach) return a("Invalid type of customClass.".concat(n, '! Expected string or iterable object, got "').concat(typeof t.customClass[n], '"')); A(e, t.customClass[n]) } }, le = (e, t) => { if (!t) return null; switch (t) { case "select": case "textarea": case "file": return e.querySelector(".".concat(p.popup, " > .").concat(p[t])); case "checkbox": return e.querySelector(".".concat(p.popup, " > .").concat(p.checkbox, " input")); case "radio": return e.querySelector(".".concat(p.popup, " > .").concat(p.radio, " input:checked")) || e.querySelector(".".concat(p.popup, " > .").concat(p.radio, " input:first-child")); case "range": return e.querySelector(".".concat(p.popup, " > .").concat(p.range, " input")); default: return e.querySelector(".".concat(p.popup, " > .").concat(p.input)) } }, ue = e => { var t; e.focus(), "file" !== e.type && (t = e.value, e.value = "", e.value = t) }, de = (e, t, n) => { e && t && (t = "string" == typeof t ? t.split(/\s+/).filter(Boolean) : t).forEach(t => { Array.isArray(e) ? e.forEach(e => { n ? e.classList.add(t) : e.classList.remove(t) }) : n ? e.classList.add(t) : e.classList.remove(t) }) }, A = (e, t) => { de(e, t, !0) }, k = (e, t) => { de(e, t, !1) }, P = (e, t) => { var n = i(e.childNodes); for (let e = 0; e < n.length; e++)if (w(n[e], t)) return n[e] }, pe = (e, t, n) => { (n = n === "".concat(parseInt(n)) ? parseInt(n) : n) || 0 === parseInt(n) ? e.style[t] = "number" == typeof n ? "".concat(n, "px") : n : e.style.removeProperty(t) }, B = function (e) { e.style.display = 1 < arguments.length && void 0 !== arguments[1] ? arguments[1] : "flex" }, x = e => { e.style.display = "none" }, me = (e, t, n, o) => { const i = e.querySelector(t); i && (i.style[n] = o) }, ge = (e, t, n) => { t ? B(e, n) : x(e) }, E = e => !(!e || !(e.offsetWidth || e.offsetHeight || e.getClientRects().length)), he = () => !E(h()) && !E(f()) && !E(b()), fe = e => !!(e.scrollHeight > e.clientHeight), be = e => { const t = window.getComputedStyle(e); var e = parseFloat(t.getPropertyValue("animation-duration") || "0"), n = parseFloat(t.getPropertyValue("transition-duration") || "0"); return 0 < e || 0 < n }, ve = () => "undefined" == typeof window || "undefined" == typeof document, ye = 100, T = {}, we = () => { T.previousActiveElement && T.previousActiveElement.focus ? (T.previousActiveElement.focus(), T.previousActiveElement = null) : document.body && document.body.focus() }, Ce = o => new Promise(e => { if (!o) return e(); var t = window.scrollX, n = window.scrollY; T.restoreFocusTimeout = setTimeout(() => { we(), e() }, ye), window.scrollTo(t, n) }), Ae = '\n <div aria-labelledby="'.concat(p.title, '" aria-describedby="').concat(p["html-container"], '" class="').concat(p.popup, '" tabindex="-1">\n <button type="button" class="').concat(p.close, '"></button>\n <ul class="').concat(p["progress-steps"], '"></ul>\n <div class="').concat(p.icon, '"></div>\n <img class="').concat(p.image, '" />\n <h2 class="').concat(p.title, '" id="').concat(p.title, '"></h2>\n <div class="').concat(p["html-container"], '" id="').concat(p["html-container"], '"></div>\n <input class="').concat(p.input, '" />\n <input type="file" class="').concat(p.file, '" />\n <div class="').concat(p.range, '">\n <input type="range" />\n <output></output>\n </div>\n <select class="').concat(p.select, '"></select>\n <div class="').concat(p.radio, '"></div>\n <label for="').concat(p.checkbox, '" class="').concat(p.checkbox, '">\n <input type="checkbox" />\n <span class="').concat(p.label, '"></span>\n </label>\n <textarea class="').concat(p.textarea, '"></textarea>\n <div class="').concat(p["validation-message"], '" id="').concat(p["validation-message"], '"></div>\n <div class="').concat(p.actions, '">\n <div class="').concat(p.loader, '"></div>\n <button type="button" class="').concat(p.confirm, '"></button>\n <button type="button" class="').concat(p.deny, '"></button>\n <button type="button" class="').concat(p.cancel, '"></button>\n </div>\n <div class="').concat(p.footer, '"></div>\n <div class="').concat(p["timer-progress-bar-container"], '">\n <div class="').concat(p["timer-progress-bar"], '"></div>\n </div>\n </div>\n').replace(/(^|\n)\s*/g, ""), ke = () => { const e = m(); return !!e && (e.remove(), k([document.documentElement, document.body], [p["no-backdrop"], p["toast-shown"], p["has-column"]]), !0) }, S = () => { T.currentInstance.resetValidationMessage() }, Pe = () => { const e = g(), t = P(e, p.input), n = P(e, p.file), o = e.querySelector(".".concat(p.range, " input")), i = e.querySelector(".".concat(p.range, " output")), a = P(e, p.select), r = e.querySelector(".".concat(p.checkbox, " input")), s = P(e, p.textarea); t.oninput = S, n.onchange = S, a.onchange = S, r.onchange = S, s.oninput = S, o.oninput = () => { S(), i.value = o.value }, o.onchange = () => { S(), o.nextSibling.value = o.value } }, Be = e => "string" == typeof e ? document.querySelector(e) : e, xe = e => { const t = g(); t.setAttribute("role", e.toast ? "alert" : "dialog"), t.setAttribute("aria-live", e.toast ? "polite" : "assertive"), e.toast || t.setAttribute("aria-modal", "true") }, Ee = e => { "rtl" === window.getComputedStyle(e).direction && A(m(), p.rtl) }, Te = (e, t) => { if (e instanceof HTMLElement) t.appendChild(e); else if ("object" == typeof e) { var n = e, o = t; if (n.jquery) Se(o, n); else y(o, n.toString()) } else e && y(t, e) }, Se = (t, n) => { if (t.textContent = "", 0 in n) for (let e = 0; e in n; e++)t.appendChild(n[e].cloneNode(!0)); else t.appendChild(n.cloneNode(!0)) }, Le = (() => { if (ve()) return !1; var e = document.createElement("div"), t = { WebkitAnimation: "webkitAnimationEnd", animation: "animationend" }; for (const n in t) if (Object.prototype.hasOwnProperty.call(t, n) && void 0 !== e.style[n]) return t[n]; return !1 })(), Oe = (e, t) => { var n, o, i, a, r, s = v(), c = d(); (t.showConfirmButton || t.showDenyButton || t.showCancelButton ? B : x)(s), C(s, t, "actions"), s = s, n = c, o = t, i = h(), a = f(), r = b(), je(i, "confirm", o), je(a, "deny", o), je(r, "cancel", o), function (e, t, n, o) { if (!o.buttonsStyling) return k([e, t, n], p.styled); A([e, t, n], p.styled), o.confirmButtonColor && (e.style.backgroundColor = o.confirmButtonColor, A(e, p["default-outline"])); o.denyButtonColor && (t.style.backgroundColor = o.denyButtonColor, A(t, p["default-outline"])); o.cancelButtonColor && (n.style.backgroundColor = o.cancelButtonColor, A(n, p["default-outline"])) }(i, a, r, o), o.reverseButtons && (o.toast ? (s.insertBefore(r, i), s.insertBefore(a, i)) : (s.insertBefore(r, n), s.insertBefore(a, n), s.insertBefore(i, n))), y(c, t.loaderHtml), C(c, t, "loader") }; function je(e, t, n) { ge(e, n["show".concat(H(t), "Button")], "inline-block"), y(e, n["".concat(t, "ButtonText")]), e.setAttribute("aria-label", n["".concat(t, "ButtonAriaLabel")]), e.className = p[t], C(e, n, "".concat(t, "Button")), A(e, n["".concat(t, "ButtonClass")]) } const Me = (e, t) => { var n, o, i = m(); i && (o = i, "string" == typeof (n = t.backdrop) ? o.style.background = n : n || A([document.documentElement, document.body], p["no-backdrop"]), o = i, (n = t.position) in p ? A(o, p[n]) : (a('The "position" parameter is not valid, defaulting to "center"'), A(o, p.center)), n = i, (o = t.grow) && "string" == typeof o && (o = "grow-".concat(o)) in p && A(n, p[o]), C(i, t, "container")) }; var L = { awaitingPromise: new WeakMap, promise: new WeakMap, innerParams: new WeakMap, domCache: new WeakMap }; const De = ["input", "file", "range", "select", "radio", "checkbox", "textarea"], Ie = (e, r) => { const s = g(); var t, e = L.innerParams.get(e); const c = !e || r.input !== e.input; De.forEach(e => { var t = p[e]; const n = P(s, t); { var o = r.inputAttributes; const i = le(g(), e); if (i) { qe(i); for (const a in o) i.setAttribute(a, o[a]) } } n.className = t, c && x(n) }), r.input && (c && (e => { if (!O[e.input]) return l('Unexpected type of input! Expected "text", "email", "password", "number", "tel", "select", "radio", "checkbox", "textarea", "file" or "url", got "'.concat(e.input, '"')); const t = Ne(e.input), n = O[e.input](t, e); B(n), setTimeout(() => { ue(n) }) })(r), e = r, t = Ne(e.input), e.customClass && A(t, e.customClass.input)) }, qe = t => { for (let e = 0; e < t.attributes.length; e++) { var n = t.attributes[e].name;["type", "value", "style"].includes(n) || t.removeAttribute(n) } }, He = (e, t) => { e.placeholder && !t.inputPlaceholder || (e.placeholder = t.inputPlaceholder) }, Ve = (e, t, n) => { if (n.inputLabel) { e.id = p.input; const i = document.createElement("label"); var o = p["input-label"]; i.setAttribute("for", e.id), i.className = o, A(i, n.customClass.inputLabel), i.innerText = n.inputLabel, t.insertAdjacentElement("beforebegin", i) } }, Ne = e => { e = p[e] || p.input; return P(g(), e) }, O = {}, Re = (O.text = O.email = O.password = O.number = O.tel = O.url = (e, t) => ("string" == typeof t.inputValue || "number" == typeof t.inputValue ? e.value = t.inputValue : U(t.inputValue) || a('Unexpected type of inputValue! Expected "string", "number" or "Promise", got "'.concat(typeof t.inputValue, '"')), Ve(e, e, t), He(e, t), e.type = t.input, e), O.file = (e, t) => (Ve(e, e, t), He(e, t), e), O.range = (e, t) => { const n = e.querySelector("input"), o = e.querySelector("output"); return n.value = t.inputValue, n.type = t.input, o.value = t.inputValue, Ve(n, e, t), e }, O.select = (e, t) => { if (e.textContent = "", t.inputPlaceholder) { const n = document.createElement("option"); y(n, t.inputPlaceholder), n.value = "", n.disabled = !0, n.selected = !0, e.appendChild(n) } return Ve(e, e, t), e }, O.radio = e => (e.textContent = "", e), O.checkbox = (e, t) => { const n = le(g(), "checkbox"); n.value = "1", n.id = p.checkbox, n.checked = Boolean(t.inputValue); var o = e.querySelector("span"); return y(o, t.inputPlaceholder), e }, O.textarea = (n, e) => { n.value = e.inputValue, He(n, e), Ve(n, n, e); return setTimeout(() => { if ("MutationObserver" in window) { const t = parseInt(window.getComputedStyle(g()).width); new MutationObserver(() => { var e = n.offsetWidth + (e = n, parseInt(window.getComputedStyle(e).marginLeft) + parseInt(window.getComputedStyle(e).marginRight)); e > t ? g().style.width = "".concat(e, "px") : g().style.width = null }).observe(n, { attributes: !0, attributeFilter: ["style"] }) } }), n }, (e, t) => { const n = $(); C(n, t, "htmlContainer"), t.html ? (Te(t.html, n), B(n, "block")) : t.text ? (n.textContent = t.text, B(n, "block")) : x(n), Ie(e, t) }), Fe = (e, t) => { var n = te(); ge(n, t.footer), t.footer && Te(t.footer, n), C(n, t, "footer") }, Ue = (e, t) => { const n = oe(); y(n, t.closeButtonHtml), C(n, t, "closeButton"), ge(n, t.showCloseButton), n.setAttribute("aria-label", t.closeButtonAriaLabel) }, We = (e, t) => { var e = L.innerParams.get(e), n = s(); return e && t.icon === e.icon ? (Ze(n, t), void ze(n, t)) : t.icon || t.iconHtml ? t.icon && -1 === Object.keys(o).indexOf(t.icon) ? (l('Unknown icon! Expected "success", "error", "warning", "info" or "question", got "'.concat(t.icon, '"')), x(n)) : (B(n), Ze(n, t), ze(n, t), void A(n, t.showClass.icon)) : x(n) }, ze = (e, t) => { for (const n in o) t.icon !== n && k(e, o[n]); A(e, o[t.icon]), Je(e, t), _e(), C(e, t, "icon") }, _e = () => { const e = g(); var t = window.getComputedStyle(e).getPropertyValue("background-color"); const n = e.querySelectorAll("[class^=swal2-success-circular-line], .swal2-success-fix"); for (let e = 0; e < n.length; e++)n[e].style.backgroundColor = t }, Ke = '\n <div class="swal2-success-circular-line-left"></div>\n <span class="swal2-success-line-tip"></span> <span class="swal2-success-line-long"></span>\n <div class="swal2-success-ring"></div> <div class="swal2-success-fix"></div>\n <div class="swal2-success-circular-line-right"></div>\n', Ye = '\n <span class="swal2-x-mark">\n <span class="swal2-x-mark-line-left"></span>\n <span class="swal2-x-mark-line-right"></span>\n </span>\n', Ze = (e, t) => { var n; e.textContent = "", t.iconHtml ? y(e, Xe(t.iconHtml)) : "success" === t.icon ? y(e, Ke) : "error" === t.icon ? y(e, Ye) : (n = { question: "?", warning: "!", info: "i" }, y(e, Xe(n[t.icon]))) }, Je = (e, t) => { if (t.iconColor) { e.style.color = t.iconColor, e.style.borderColor = t.iconColor; for (const n of [".swal2-success-line-tip", ".swal2-success-line-long", ".swal2-x-mark-line-left", ".swal2-x-mark-line-right"]) me(e, n, "backgroundColor", t.iconColor); me(e, ".swal2-success-ring", "borderColor", t.iconColor) } }, Xe = e => '<div class="'.concat(p["icon-content"], '">').concat(e, "</div>"), $e = (e, t) => { const n = G(); if (!t.imageUrl) return x(n); B(n, ""), n.setAttribute("src", t.imageUrl), n.setAttribute("alt", t.imageAlt), pe(n, "width", t.imageWidth), pe(n, "height", t.imageHeight), n.className = p.image, C(n, t, "image") }, Ge = (e, o) => { const i = Q(); if (!o.progressSteps || 0 === o.progressSteps.length) return x(i); B(i), i.textContent = "", o.currentProgressStep >= o.progressSteps.length && a("Invalid currentProgressStep parameter, it should be less than progressSteps.length (currentProgressStep like JS arrays starts from 0)"), o.progressSteps.forEach((e, t) => { e = e, n = document.createElement("li"), A(n, p["progress-step"]), y(n, e); var n, e = n; i.appendChild(e), t === o.currentProgressStep && A(e, p["active-progress-step"]), t !== o.progressSteps.length - 1 && (n = (e => { const t = document.createElement("li"); return A(t, p["progress-step-line"]), e.progressStepsDistance && (t.style.width = e.progressStepsDistance), t })(o), i.appendChild(n)) }) }, Qe = (e, t) => { const n = X(); ge(n, t.title || t.titleText, "block"), t.title && Te(t.title, n), t.titleText && (n.innerText = t.titleText), C(n, t, "title") }, et = (e, t) => { var n = m(); const o = g(); t.toast ? (pe(n, "width", t.width), o.style.width = "100%", o.insertBefore(d(), s())) : pe(o, "width", t.width), pe(o, "padding", t.padding), t.color && (o.style.color = t.color), t.background && (o.style.background = t.background), x(ee()); n = o; (n.className = "".concat(p.popup, " ").concat(E(n) ? t.showClass.popup : ""), t.toast) ? (A([document.documentElement, document.body], p["toast-shown"]), A(n, p.toast)) : A(n, p.modal); C(n, t, "popup"), "string" == typeof t.customClass && A(n, t.customClass); t.icon && A(n, p["icon-".concat(t.icon)]) }, tt = (e, t) => { et(e, t), Me(e, t), Ge(e, t), We(e, t), $e(e, t), Qe(e, t), Ue(e, t), Re(e, t), Oe(e, t), Fe(e, t), "function" == typeof t.didRender && t.didRender(g()) }, j = Object.freeze({ cancel: "cancel", backdrop: "backdrop", close: "close", esc: "esc", timer: "timer" }), nt = () => { const e = i(document.body.children); e.forEach(e => { e === m() || e.contains(m()) || (e.hasAttribute("aria-hidden") && e.setAttribute("data-previous-aria-hidden", e.getAttribute("aria-hidden")), e.setAttribute("aria-hidden", "true")) }) }, ot = () => { const e = i(document.body.children); e.forEach(e => { e.hasAttribute("data-previous-aria-hidden") ? (e.setAttribute("aria-hidden", e.getAttribute("data-previous-aria-hidden")), e.removeAttribute("data-previous-aria-hidden")) : e.removeAttribute("aria-hidden") }) }, it = ["swal-title", "swal-html", "swal-footer"], at = e => { const n = {}; return i(e.querySelectorAll("swal-param")).forEach(e => { M(e, ["name", "value"]); var t = e.getAttribute("name"), e = e.getAttribute("value"); "boolean" == typeof r[t] && "false" === e && (n[t] = !1), "object" == typeof r[t] && (n[t] = JSON.parse(e)) }), n }, rt = e => { const n = {}; return i(e.querySelectorAll("swal-button")).forEach(e => { M(e, ["type", "color", "aria-label"]); var t = e.getAttribute("type"); n["".concat(t, "ButtonText")] = e.innerHTML, n["show".concat(H(t), "Button")] = !0, e.hasAttribute("color") && (n["".concat(t, "ButtonColor")] = e.getAttribute("color")), e.hasAttribute("aria-label") && (n["".concat(t, "ButtonAriaLabel")] = e.getAttribute("aria-label")) }), n }, st = e => { const t = {}, n = e.querySelector("swal-image"); return n && (M(n, ["src", "width", "height", "alt"]), n.hasAttribute("src") && (t.imageUrl = n.getAttribute("src")), n.hasAttribute("width") && (t.imageWidth = n.getAttribute("width")), n.hasAttribute("height") && (t.imageHeight = n.getAttribute("height")), n.hasAttribute("alt") && (t.imageAlt = n.getAttribute("alt"))), t }, ct = e => { const t = {}, n = e.querySelector("swal-icon"); return n && (M(n, ["type", "color"]), n.hasAttribute("type") && (t.icon = n.getAttribute("type")), n.hasAttribute("color") && (t.iconColor = n.getAttribute("color")), t.iconHtml = n.innerHTML), t }, lt = e => { const n = {}, t = e.querySelector("swal-input"); t && (M(t, ["type", "label", "placeholder", "value"]), n.input = t.getAttribute("type") || "text", t.hasAttribute("label") && (n.inputLabel = t.getAttribute("label")), t.hasAttribute("placeholder") && (n.inputPlaceholder = t.getAttribute("placeholder")), t.hasAttribute("value") && (n.inputValue = t.getAttribute("value"))); e = e.querySelectorAll("swal-input-option"); return e.length && (n.inputOptions = {}, i(e).forEach(e => { M(e, ["value"]); var t = e.getAttribute("value"), e = e.innerHTML; n.inputOptions[t] = e })), n }, ut = (e, t) => { const n = {}; for (const o in t) { const i = t[o], a = e.querySelector(i); a && (M(a, []), n[i.replace(/^swal-/, "")] = a.innerHTML.trim()) } return n }, dt = e => { const t = it.concat(["swal-param", "swal-button", "swal-image", "swal-icon", "swal-input", "swal-input-option"]); i(e.children).forEach(e => { e = e.tagName.toLowerCase(); -1 === t.indexOf(e) && a("Unrecognized element <".concat(e, ">")) }) }, M = (t, n) => { i(t.attributes).forEach(e => { -1 === n.indexOf(e.name) && a(['Unrecognized attribute "'.concat(e.name, '" on <').concat(t.tagName.toLowerCase(), ">."), "".concat(n.length ? "Allowed attributes are: ".concat(n.join(", ")) : "To set the value, use HTML within the element.")]) }) }; var pt = { email: (e, t) => /^[a-zA-Z0-9.+_-]+@[a-zA-Z0-9.-]+\.[a-zA-Z0-9-]{2,24}$/.test(e) ? Promise.resolve() : Promise.resolve(t || "Invalid email address"), url: (e, t) => /^https?:\/\/(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-z]{2,63}\b([-a-zA-Z0-9@:%_+.~#?&/=]*)$/.test(e) ? Promise.resolve() : Promise.resolve(t || "Invalid URL") }; function mt(e) { (t = e).inputValidator || Object.keys(pt).forEach(e => { t.input === e && (t.inputValidator = pt[e]) }), e.showLoaderOnConfirm && !e.preConfirm && a("showLoaderOnConfirm is set to true, but preConfirm is not defined.\nshowLoaderOnConfirm should be used together with preConfirm, see usage example:\nhttps://sweetalert2.github.io/#ajax-request"), (n = e).target && ("string" != typeof n.target || document.querySelector(n.target)) && ("string" == typeof n.target || n.target.appendChild) || (a('Target parameter is not valid, defaulting to "body"'), n.target = "body"), "string" == typeof e.title && (e.title = e.title.split("\n").join("<br />")); var t, n = e, e = ke(); if (ve()) l("SweetAlert2 requires document to initialize"); else { const o = document.createElement("div"), i = (o.className = p.container, e && A(o, p["no-transition"]), y(o, Ae), Be(n.target)); i.appendChild(o), xe(n), Ee(i), Pe() } } class gt { constructor(e, t) { this.callback = e, this.remaining = t, this.running = !1, this.start() } start() { return this.running || (this.running = !0, this.started = new Date, this.id = setTimeout(this.callback, this.remaining)), this.remaining } stop() { return this.running && (this.running = !1, clearTimeout(this.id), this.remaining -= (new Date).getTime() - this.started.getTime()), this.remaining } increase(e) { var t = this.running; return t && this.stop(), this.remaining += e, t && this.start(), this.remaining } getTimerLeft() { return this.running && (this.stop(), this.start()), this.remaining } isRunning() { return this.running } } const ht = () => { null === c.previousBodyPadding && document.body.scrollHeight > window.innerHeight && (c.previousBodyPadding = parseInt(window.getComputedStyle(document.body).getPropertyValue("padding-right")), document.body.style.paddingRight = "".concat(c.previousBodyPadding + (() => { const e = document.createElement("div"); e.className = p["scrollbar-measure"], document.body.appendChild(e); var t = e.getBoundingClientRect().width - e.clientWidth; return document.body.removeChild(e), t })(), "px")) }, ft = () => { null !== c.previousBodyPadding && (document.body.style.paddingRight = "".concat(c.previousBodyPadding, "px"), c.previousBodyPadding = null) }, bt = () => { if ((/iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream || "MacIntel" === navigator.platform && 1 < navigator.maxTouchPoints) && !w(document.body, p.iosfix)) { var e, t = document.body.scrollTop; document.body.style.top = "".concat(-1 * t, "px"), A(document.body, p.iosfix); { const n = m(); let t; n.ontouchstart = e => { t = vt(e) }, n.ontouchmove = e => { t && (e.preventDefault(), e.stopPropagation()) } } { const o = navigator.userAgent, i = !!o.match(/iPad/i) || !!o.match(/iPhone/i), a = !!o.match(/WebKit/i), r = i && a && !o.match(/CriOS/i); r && (e = 44, g().scrollHeight > window.innerHeight - 44 && (m().style.paddingBottom = "".concat(44, "px"))) } } }, vt = e => { var t, n = e.target, o = m(); return !((t = e).touches && t.touches.length && "stylus" === t.touches[0].touchType || (t = e).touches && 1 < t.touches.length) && (n === o || !(fe(o) || "INPUT" === n.tagName || "TEXTAREA" === n.tagName || fe($()) && $().contains(n))) }, yt = () => { var e; w(document.body, p.iosfix) && (e = parseInt(document.body.style.top, 10), k(document.body, p.iosfix), document.body.style.top = "", document.body.scrollTop = -1 * e) }, wt = 10, Ct = e => { const t = g(); if (e.target === t) { const n = m(); t.removeEventListener(Le, Ct), n.style.overflowY = "auto" } }, At = (e, t) => { Le && be(t) ? (e.style.overflowY = "hidden", t.addEventListener(Le, Ct)) : e.style.overflowY = "auto" }, kt = (e, t, n) => { bt(), t && "hidden" !== n && ht(), setTimeout(() => { e.scrollTop = 0 }) }, Pt = (e, t, n) => { A(e, n.showClass.backdrop), t.style.setProperty("opacity", "0", "important"), B(t, "grid"), setTimeout(() => { A(t, n.showClass.popup), t.style.removeProperty("opacity") }, wt), A([document.documentElement, document.body], p.shown), n.heightAuto && n.backdrop && !n.toast && A([document.documentElement, document.body], p["height-auto"]) }, D = e => { let t = g(); t || new wn, t = g(); var n = d(); if (re()) x(s()); else { var o = t; const i = v(), a = d(); !e && E(h()) && (e = h()); B(i), e && (x(e), a.setAttribute("data-button-to-replace", e.className)); a.parentNode.insertBefore(a, e), A([o, i], p.loading) } B(n), t.setAttribute("data-loading", !0), t.setAttribute("aria-busy", !0), t.focus() }, Bt = (t, n) => { const o = g(), i = e => Et[n.input](o, Tt(e), n); F(n.inputOptions) || U(n.inputOptions) ? (D(h()), u(n.inputOptions).then(e => { t.hideLoading(), i(e) })) : "object" == typeof n.inputOptions ? i(n.inputOptions) : l("Unexpected type of inputOptions! Expected object, Map or Promise, got ".concat(typeof n.inputOptions)) }, xt = (t, n) => { const o = t.getInput(); x(o), u(n.inputValue).then(e => { o.value = "number" === n.input ? parseFloat(e) || 0 : "".concat(e), B(o), o.focus(), t.hideLoading() }).catch(e => { l("Error in inputValue promise: ".concat(e)), o.value = "", B(o), o.focus(), t.hideLoading() }) }, Et = { select: (e, t, i) => { const a = P(e, p.select), r = (e, t, n) => { const o = document.createElement("option"); o.value = n, y(o, t), o.selected = St(n, i.inputValue), e.appendChild(o) }; t.forEach(e => { var t = e[0]; const n = e[1]; if (Array.isArray(n)) { const o = document.createElement("optgroup"); o.label = t, o.disabled = !1, a.appendChild(o), n.forEach(e => r(o, e[1], e[0])) } else r(a, n, t) }), a.focus() }, radio: (e, t, a) => { const r = P(e, p.radio), n = (t.forEach(e => { var t = e[0], e = e[1]; const n = document.createElement("input"), o = document.createElement("label"), i = (n.type = "radio", n.name = p.radio, n.value = t, St(t, a.inputValue) && (n.checked = !0), document.createElement("span")); y(i, e), i.className = p.label, o.appendChild(n), o.appendChild(i), r.appendChild(o) }), r.querySelectorAll("input")); n.length && n[0].focus() } }, Tt = n => { const o = []; return "undefined" != typeof Map && n instanceof Map ? n.forEach((e, t) => { let n = e; "object" == typeof n && (n = Tt(n)), o.push([t, n]) }) : Object.keys(n).forEach(e => { let t = n[e]; "object" == typeof t && (t = Tt(t)), o.push([e, t]) }), o }, St = (e, t) => t && t.toString() === e.toString(); function Lt() { var e, t = L.innerParams.get(this); if (t) { const n = L.domCache.get(this); x(n.loader), re() ? t.icon && B(s()) : (t = n, (e = t.popup.getElementsByClassName(t.loader.getAttribute("data-button-to-replace"))).length ? B(e[0], "inline-block") : he() && x(t.actions)), k([n.popup, n.actions], p.loading), n.popup.removeAttribute("aria-busy"), n.popup.removeAttribute("data-loading"), n.confirmButton.disabled = !1, n.denyButton.disabled = !1, n.cancelButton.disabled = !1 } } var Ot = { swalPromiseResolve: new WeakMap, swalPromiseReject: new WeakMap }; const jt = () => h() && h().click(); const Mt = e => { e.keydownTarget && e.keydownHandlerAdded && (e.keydownTarget.removeEventListener("keydown", e.keydownHandler, { capture: e.keydownListenerCapture }), e.keydownHandlerAdded = !1) }, Dt = (e, t, n) => { const o = ie(); if (o.length) return (t += n) === o.length ? t = 0 : -1 === t && (t = o.length - 1), o[t].focus(); g().focus() }, It = ["ArrowRight", "ArrowDown"], qt = ["ArrowLeft", "ArrowUp"], Ht = (e, n, t) => { var o = L.innerParams.get(e); if (o && (!n.isComposing && 229 !== n.keyCode)) if (o.stopKeydownPropagation && n.stopPropagation(), "Enter" === n.key) e = e, s = n, i = o, R(i.allowEnterKey) && s.target && e.getInput() && s.target.outerHTML === e.getInput().outerHTML && (["textarea", "file"].includes(i.input) || (jt(), s.preventDefault())); else if ("Tab" === n.key) { e = n; var i = o; var a = e.target, r = ie(); let t = -1; for (let e = 0; e < r.length; e++)if (a === r[e]) { t = e; break } e.shiftKey ? Dt(i, t, -1) : Dt(i, t, 1); e.stopPropagation(), e.preventDefault() } else if ([...It, ...qt].includes(n.key)) { var s = n.key; const l = h(), u = f(), d = b(); if ([l, u, d].includes(document.activeElement)) { var c = It.includes(s) ? "nextElementSibling" : "previousElementSibling"; let t = document.activeElement; for (let e = 0; e < v().children.length; e++) { if (!(t = t[c])) return; if (E(t) && t instanceof HTMLButtonElement) break } t instanceof HTMLButtonElement && t.focus() } } else if ("Escape" === n.key) { e = n, n = o, o = t; if (R(n.allowEscapeKey)) { e.preventDefault(); o(j.esc) } } }; function Vt(e, t, n, o) { re() ? Ut(e, o) : (Ce(n).then(() => Ut(e, o)), Mt(T)), /^((?!chrome|android).)*safari/i.test(navigator.userAgent) ? (t.setAttribute("style", "display:none !important"), t.removeAttribute("class"), t.innerHTML = "") : t.remove(), ae() && (ft(), yt(), ot()), k([document.documentElement, document.body], [p.shown, p["height-auto"], p["no-backdrop"], p["toast-shown"]]) } function Nt(e) { e = void 0 !== (n = e) ? Object.assign({ isConfirmed: !1, isDenied: !1, isDismissed: !1 }, n) : { isConfirmed: !1, isDenied: !1, isDismissed: !0 }; const t = Ot.swalPromiseResolve.get(this); var n = (e => { const t = g(); if (!t) return false; const n = L.innerParams.get(e); if (!n || w(t, n.hideClass.popup)) return false; k(t, n.showClass.popup), A(t, n.hideClass.popup); const o = m(); return k(o, n.showClass.backdrop), A(o, n.hideClass.backdrop), Ft(e, t, n), true })(this); this.isAwaitingPromise() ? e.isDismissed || (Rt(this), t(e)) : n && t(e) } const Rt = e => { e.isAwaitingPromise() && (L.awaitingPromise.delete(e), L.innerParams.get(e) || e._destroy()) }, Ft = (e, t, n) => { var o, i, a, r = m(), s = Le && be(t); "function" == typeof n.willClose && n.willClose(t), s ? (s = e, o = t, t = r, i = n.returnFocus, a = n.didClose, T.swalCloseEventFinishedCallback = Vt.bind(null, s, t, i, a), o.addEventListener(Le, function (e) { e.target === o && (T.swalCloseEventFinishedCallback(), delete T.swalCloseEventFinishedCallback) })) : Vt(e, r, n.returnFocus, n.didClose) }, Ut = (e, t) => { setTimeout(() => { "function" == typeof t && t.bind(e.params)(), e._destroy() }) }; function Wt(e, t, n) { const o = L.domCache.get(e); t.forEach(e => { o[e].disabled = n }) } function zt(e, t) { if (!e) return !1; if ("radio" === e.type) { const n = e.parentNode.parentNode, o = n.querySelectorAll("input"); for (let e = 0; e < o.length; e++)o[e].disabled = t } else e.disabled = t } const _t = e => { e.isAwaitingPromise() ? (Kt(L, e), L.awaitingPromise.set(e, !0)) : (Kt(Ot, e), Kt(L, e)) }, Kt = (e, t) => { for (const n in e) e[n].delete(t) }; e = Object.freeze({ hideLoading: Lt, disableLoading: Lt, getInput: function (e) { var t = L.innerParams.get(e || this); return (e = L.domCache.get(e || this)) ? le(e.popup, t.input) : null }, close: Nt, isAwaitingPromise: function () { return !!L.awaitingPromise.get(this) }, rejectPromise: function (e) { const t = Ot.swalPromiseReject.get(this); Rt(this), t && t(e) }, handleAwaitingPromise: Rt, closePopup: Nt, closeModal: Nt, closeToast: Nt, enableButtons: function () { Wt(this, ["confirmButton", "denyButton", "cancelButton"], !1) }, disableButtons: function () { Wt(this, ["confirmButton", "denyButton", "cancelButton"], !0) }, enableInput: function () { return zt(this.getInput(), !1) }, disableInput: function () { return zt(this.getInput(), !0) }, showValidationMessage: function (e) { const t = L.domCache.get(this); var n = L.innerParams.get(this); y(t.validationMessage, e), t.validationMessage.className = p["validation-message"], n.customClass && n.customClass.validationMessage && A(t.validationMessage, n.customClass.validationMessage), B(t.validationMessage); const o = this.getInput(); o && (o.setAttribute("aria-invalid", !0), o.setAttribute("aria-describedby", p["validation-message"]), ue(o), A(o, p.inputerror)) }, resetValidationMessage: function () { var e = L.domCache.get(this); e.validationMessage && x(e.validationMessage); const t = this.getInput(); t && (t.removeAttribute("aria-invalid"), t.removeAttribute("aria-describedby"), k(t, p.inputerror)) }, getProgressSteps: function () { return L.domCache.get(this).progressSteps }, update: function (e) { var t = g(), n = L.innerParams.get(this); if (!t || w(t, n.hideClass.popup)) return a("You're trying to update the closed or closing popup, that won't work. Use the update() method in preConfirm parameter or show a new popup."); t = (t => { const n = {}; return Object.keys(t).forEach(e => { if (Y(e)) n[e] = t[e]; else a('Invalid parameter to update: "'.concat(e, '". Updatable params are listed here: https://github.com/sweetalert2/sweetalert2/blob/master/src/utils/params.js\n\nIf you think this parameter should be updatable, request it here: https://github.com/sweetalert2/sweetalert2/issues/new?template=02_feature_request.md')) }), n })(e), n = Object.assign({}, n, t), tt(this, n), L.innerParams.set(this, n), Object.defineProperties(this, { params: { value: Object.assign({}, this.params, e), writable: !1, enumerable: !0 } }) }, _destroy: function () { var e = L.domCache.get(this); const t = L.innerParams.get(this); t ? (e.popup && T.swalCloseEventFinishedCallback && (T.swalCloseEventFinishedCallback(), delete T.swalCloseEventFinishedCallback), T.deferDisposalTimer && (clearTimeout(T.deferDisposalTimer), delete T.deferDisposalTimer), "function" == typeof t.didDestroy && t.didDestroy(), e = this, _t(e), delete e.params, delete T.keydownHandler, delete T.keydownTarget, delete T.currentInstance) : _t(this) } }); const Yt = (e, t) => { var n = L.innerParams.get(e); if (!n.input) return l('The "input" parameter is needed to be set when using returnInputValueOn'.concat(H(t))); var o = ((e, t) => { const n = e.getInput(); if (!n) return null; switch (t.input) { case "checkbox": return n.checked ? 1 : 0; case "radio": return (o = n).checked ? o.value : null; case "file": return (o = n).files.length ? null !== o.getAttribute("multiple") ? o.files : o.files[0] : null; default: return t.inputAutoTrim ? n.value.trim() : n.value }var o })(e, n); if (n.inputValidator) { var i = e; var a = o; var r = t; const s = L.innerParams.get(i), c = (i.disableInput(), Promise.resolve().then(() => u(s.inputValidator(a, s.validationMessage)))); c.then(e => { i.enableButtons(), i.enableInput(), e ? i.showValidationMessage(e) : ("deny" === r ? Zt : $t)(i, a) }) } else e.getInput().checkValidity() ? ("deny" === t ? Zt : $t)(e, o) : (e.enableButtons(), e.showValidationMessage(n.validationMessage)) }, Zt = (t, n) => { const e = L.innerParams.get(t || void 0); if (e.showLoaderOnDeny && D(f()), e.preDeny) { L.awaitingPromise.set(t || void 0, !0); const o = Promise.resolve().then(() => u(e.preDeny(n, e.validationMessage))); o.then(e => { !1 === e ? (t.hideLoading(), Rt(t)) : t.closePopup({ isDenied: !0, value: void 0 === e ? n : e }) }).catch(e => Xt(t || void 0, e)) } else t.closePopup({ isDenied: !0, value: n }) }, Jt = (e, t) => { e.closePopup({ isConfirmed: !0, value: t }) }, Xt = (e, t) => { e.rejectPromise(t) }, $t = (t, n) => { const e = L.innerParams.get(t || void 0); if (e.showLoaderOnConfirm && D(), e.preConfirm) { t.resetValidationMessage(), L.awaitingPromise.set(t || void 0, !0); const o = Promise.resolve().then(() => u(e.preConfirm(n, e.validationMessage))); o.then(e => { E(ee()) || !1 === e ? (t.hideLoading(), Rt(t)) : Jt(t, void 0 === e ? n : e) }).catch(e => Xt(t || void 0, e)) } else Jt(t, n) }, Gt = (n, e, o) => { e.popup.onclick = () => { var e, t = L.innerParams.get(n); t && ((e = t).showConfirmButton || e.showDenyButton || e.showCancelButton || e.showCloseButton || t.timer || t.input) || o(j.close) } }; let Qt = !1; const en = t => { t.popup.onmousedown = () => { t.container.onmouseup = function (e) { t.container.onmouseup = void 0, e.target === t.container && (Qt = !0) } } }, tn = t => { t.container.onmousedown = () => { t.popup.onmouseup = function (e) { t.popup.onmouseup = void 0, e.target !== t.popup && !t.popup.contains(e.target) || (Qt = !0) } } }, nn = (n, o, i) => { o.container.onclick = e => { var t = L.innerParams.get(n); Qt ? Qt = !1 : e.target === o.container && R(t.allowOutsideClick) && i(j.backdrop) } }, on = e => "object" == typeof e && e.jquery, an = e => e instanceof Element || on(e); const rn = () => { if (T.timeout) { { const n = ne(); var e = parseInt(window.getComputedStyle(n).width), t = (n.style.removeProperty("transition"), n.style.width = "100%", parseInt(window.getComputedStyle(n).width)), e = e / t * 100; n.style.removeProperty("transition"), n.style.width = "".concat(e, "%") } return T.timeout.stop() } }, sn = () => { var e; if (T.timeout) return e = T.timeout.start(), se(e), e }; let cn = !1; const ln = {}; const un = t => { for (let e = t.target; e && e !== document; e = e.parentNode)for (const o in ln) { var n = e.getAttribute(o); if (n) return void ln[o].fire({ template: n }) } }; var dn = Object.freeze({ isValidParameter: K, isUpdatableParameter: Y, isDeprecatedParameter: Z, argsToParams: n => { const o = {}; return "object" != typeof n[0] || an(n[0]) ? ["title", "html", "icon"].forEach((e, t) => { t = n[t]; "string" == typeof t || an(t) ? o[e] = t : void 0 !== t && l("Unexpected type of ".concat(e, '! Expected "string" or "Element", got ').concat(typeof t)) }) : Object.assign(o, n[0]), o }, isVisible: () => E(g()), clickConfirm: jt, clickDeny: () => f() && f().click(), clickCancel: () => b() && b().click(), getContainer: m, getPopup: g, getTitle: X, getHtmlContainer: $, getImage: G, getIcon: s, getInputLabel: () => n(p["input-label"]), getCloseButton: oe, getActions: v, getConfirmButton: h, getDenyButton: f, getCancelButton: b, getLoader: d, getFooter: te, getTimerProgressBar: ne, getFocusableElements: ie, getValidationMessage: ee, isLoading: () => g().hasAttribute("data-loading"), fire: function () { for (var e = arguments.length, t = new Array(e), n = 0; n < e; n++)t[n] = arguments[n]; return new this(...t) }, mixin: function (n) { class e extends this{ _main(e, t) { return super._main(e, Object.assign({}, n, t)) } } return e }, showLoading: D, enableLoading: D, getTimerLeft: () => T.timeout && T.timeout.getTimerLeft(), stopTimer: rn, resumeTimer: sn, toggleTimer: () => { var e = T.timeout; return e && (e.running ? rn : sn)() }, increaseTimer: e => { if (T.timeout) return e = T.timeout.increase(e), se(e, !0), e }, isTimerRunning: () => T.timeout && T.timeout.isRunning(), bindClickHandler: function () { var e = 0 < arguments.length && void 0 !== arguments[0] ? arguments[0] : "data-swal-template"; ln[e] = this, cn || (document.body.addEventListener("click", un), cn = !0) } }); let pn; class I { constructor() { if ("undefined" != typeof window) { pn = this; for (var e = arguments.length, t = new Array(e), n = 0; n < e; n++)t[n] = arguments[n]; var o = Object.freeze(this.constructor.argsToParams(t)), o = (Object.defineProperties(this, { params: { value: o, writable: !1, enumerable: !0, configurable: !0 } }), this._main(this.params)); L.promise.set(this, o) } } _main(e) { var t = 1 < arguments.length && void 0 !== arguments[1] ? arguments[1] : {}, e = (J(Object.assign({}, t, e)), T.currentInstance && (T.currentInstance._destroy(), ae() && ot()), T.currentInstance = this, gn(e, t)), t = (mt(e), Object.freeze(e), T.timeout && (T.timeout.stop(), delete T.timeout), clearTimeout(T.restoreFocusTimeout), hn(this)); return tt(this, e), L.innerParams.set(this, e), mn(this, t, e) } then(e) { const t = L.promise.get(this); return t.then(e) } finally(e) { const t = L.promise.get(this); return t.finally(e) } } const mn = (l, u, d) => new Promise((e, t) => { const n = e => { l.closePopup({ isDismissed: !0, dismiss: e }) }; var o, i, a; Ot.swalPromiseResolve.set(l, e), Ot.swalPromiseReject.set(l, t), u.confirmButton.onclick = () => { var e = l, t = L.innerParams.get(e); e.disableButtons(), t.input ? Yt(e, "confirm") : $t(e, !0) }, u.denyButton.onclick = () => { var e = l, t = L.innerParams.get(e); e.disableButtons(), t.returnInputValueOnDeny ? Yt(e, "deny") : Zt(e, !1) }, u.cancelButton.onclick = () => { var e = l, t = n; e.disableButtons(), t(j.cancel) }, u.closeButton.onclick = () => n(j.close), e = l, t = u, a = n, L.innerParams.get(e).toast ? Gt(e, t, a) : (en(t), tn(t), nn(e, t, a)), o = l, e = T, t = d, i = n, Mt(e), t.toast || (e.keydownHandler = e => Ht(o, e, i), e.keydownTarget = t.keydownListenerCapture ? window : g(), e.keydownListenerCapture = t.keydownListenerCapture, e.keydownTarget.addEventListener("keydown", e.keydownHandler, { capture: e.keydownListenerCapture }), e.keydownHandlerAdded = !0), a = l, "select" === (t = d).input || "radio" === t.input ? Bt(a, t) : ["text", "email", "number", "tel", "textarea"].includes(t.input) && (F(t.inputValue) || U(t.inputValue)) && (D(h()), xt(a, t)); { var r = d; const s = m(), c = g(); "function" == typeof r.willOpen && r.willOpen(c), e = window.getComputedStyle(document.body).overflowY, Pt(s, c, r), setTimeout(() => { At(s, c) }, wt), ae() && (kt(s, r.scrollbarPadding, e), nt()), re() || T.previousActiveElement || (T.previousActiveElement = document.activeElement), "function" == typeof r.didOpen && setTimeout(() => r.didOpen(c)), k(s, p["no-transition"]) } fn(T, d, n), bn(u, d), setTimeout(() => { u.container.scrollTop = 0 }) }), gn = (e, t) => { var n = (e => { e = "string" == typeof e.template ? document.querySelector(e.template) : e.template; if (!e) return {}; e = e.content, dt(e), e = Object.assign(at(e), rt(e), st(e), ct(e), lt(e), ut(e, it)); return e })(e); const o = Object.assign({}, r, t, n, e); return o.showClass = Object.assign({}, r.showClass, o.showClass), o.hideClass = Object.assign({}, r.hideClass, o.hideClass), o }, hn = e => { var t = { popup: g(), container: m(), actions: v(), confirmButton: h(), denyButton: f(), cancelButton: b(), loader: d(), closeButton: oe(), validationMessage: ee(), progressSteps: Q() }; return L.domCache.set(e, t), t }, fn = (e, t, n) => { var o = ne(); x(o), t.timer && (e.timeout = new gt(() => { n("timer"), delete e.timeout }, t.timer), t.timerProgressBar && (B(o), C(o, t, "timerProgressBar"), setTimeout(() => { e.timeout && e.timeout.running && se(t.timer) }))) }, bn = (e, t) => { if (!t.toast) return R(t.allowEnterKey) ? void (vn(e, t) || Dt(t, -1, 1)) : yn() }, vn = (e, t) => t.focusDeny && E(e.denyButton) ? (e.denyButton.focus(), !0) : t.focusCancel && E(e.cancelButton) ? (e.cancelButton.focus(), !0) : !(!t.focusConfirm || !E(e.confirmButton)) && (e.confirmButton.focus(), !0), yn = () => { document.activeElement instanceof HTMLElement && "function" == typeof document.activeElement.blur && document.activeElement.blur() }, wn = (Object.assign(I.prototype, e), Object.assign(I, dn), Object.keys(e).forEach(e => { I[e] = function () { if (pn) return pn[e](...arguments) } }), I.DismissReason = j, I.version = "11.4.8", I); return wn.default = wn }), void 0 !== this && this.Sweetalert2 && (this.swal = this.sweetAlert = this.Swal = this.SweetAlert = this.Sweetalert2);
(function () {
'use strict';
class SettingValue {
#key;
get key() { return this.#key; }
#value;
set value(newValue) {
GM_setValue(this.#key, newValue);
}
get value() { return this.#value; }
#defaultValue;
get defaultValue() { return this.#defaultValue; }
#range = {
min: undefined,
max: undefined,
}
get range() { return this.#range; }
checkRange(value, self = this) {
if (!self.#range || !self.#range.min || !self.#range.max) {
return true;
}
return self.#range.min <= value && value <= self.#range.max;
}
constructor(key, defaultValue, range = undefined) {
let self = this;
self.#key = key;
self.#value = "";
self.#defaultValue = defaultValue;
self.#range = range;
self.loadValue();
}
loadValue(newKey = undefined, self = this) {
if (newKey) {
self.#key = newKey;
}
let gValue = GM_getValue(self.#key, self.#defaultValue);
if (typeof self.#defaultValue !== typeof gValue ||
Number.isNaN(gValue) ||
!self.checkRange(gValue)) {
// 存储的值格式不对,执行重设
self.value = self.#defaultValue;
} else {
// 读取正常值
self.#value = gValue;
}
}
}
const IS_DEBUG = new SettingValue("VOEOC_GMKEY_IS_DEBUG", false).value; // 调试信息开关。需要手动编辑油猴插件的存储数据
const settingsData = {
isFixedTitle: new SettingValue("VOEOC_GMKEY_isFixedTitle", false), // 是否将标题置顶,TODO:暂不支持
isLongClickToOpenLzlPage: new SettingValue("VOEOC_GMKEY_isLongClickToOpenLzlPage", true), // 是否开启展开按钮的长按事件,长按展开按钮时直接切换到对应的楼中楼页
isAutoExpand: new SettingValue("VOEOC_GMKEY_isAutoExpand", true), // 自动展开的开关
isRemaindAutoExpand: new SettingValue("VOEOC_GMKEY_isRemaindAutoExpand", true), // 剩余评论过少时自动展开的开关
eachExpandSize: new SettingValue("VOEOC_GMKEY_eachExpandSize", 12, { min: 10, max: 30 }), // 每次展开的评论数量,至少为10,少于10按10计算
remaindAutoExpandSize: new SettingValue("VOEOC_GMKEY_remaindAutoExpandSize", 7, { min: 0, max: 20 }), // 当剩余评论少于这个数时,执行自动展开
}
function reloadSettingsData() {
for (let key in settingsData) {
settingsData[key].loadValue();
}
settingsData.isFixedTitle.value = false;
}
settingsData.isFixedTitle.value = false;
const STR_ID_LZLPAGE = "VOEOC-ID-LZLPAGE";
const STR_ID_LZLPAGEIFRAME = "VOEOC-ID-LZLPAGEIFRAME";
const STR_ID_LZLPAGEBACKGROUND = "VOEOC-ID-LZLPAGEBACKGROUND";
const STR_VOEOCMARK = "VOEOCMARK"; // 临时标记
const HTML_SVG_CLOSE_BTN = `<svg t="1655203632724" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2322" width="22" height="22"><path d="M576 512l277.333333 277.333333-64 64-277.333333-277.333333L234.666667 853.333333 170.666667 789.333333l277.333333-277.333333L170.666667 234.666667 234.666667 170.666667l277.333333 277.333333L789.333333 170.666667 853.333333 234.666667 576 512z" fill="#444444" p-id="2323"></path></svg>`;
const HTML_SVG_RELOAD_BTN = `<svg t="1655490109919" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2737" width="22" height="22"><path d="M901.1 629.4c-51.6 159-203.4 274.1-382.7 274.1-221.8 0-401.5-176.2-401.5-393.6 0-217.4 179.8-393.6 401.5-393.6 131.4 0 248 61.8 321.2 157.4H679c-14.8 0-26.8 11.8-26.8 26.2s12 26.2 26.8 26.2h214.1c14.8 0 26.8-11.7 26.8-26.2V90.1c0-14.5-12-26.2-26.8-26.2s-26.8 11.7-26.8 26.2v132.4c-83.5-97-208.4-158.7-348-158.7-251.2 0.1-455 199.8-455 446.2 0 246.4 203.7 446.1 455.1 446.1 207.3 0 382.2-135.9 437.1-321.7-15.9-14.2-39-20.7-54.4-5z" fill="#040404" p-id="2738"></path></svg>`;
const HISTORY_STATE_LZL_PAGE = {
title: "楼中楼回复",
id: "1"
}
const VOEOC_REG = { // 自定义正则
POSTPAGE: RegExp(`postPage\?(?=.*tid\=)(?=.*postAuthorId\=)(?=.*forumId\=)`, 'i'), // 评论页url
FLOORREPLAYPAGE: RegExp(`lzlPage\?(?=.*floor\=)(?=.*pid\=)`, 'i'), // 楼中楼页url
JSON_FLOORDATA: RegExp(`getFloorData\?(?=.*pn\=)(?=.*rn\=)(?=.*tid\=)(?=.*pid\=)`, 'i'), // 楼中楼json数据url
POSTPBDATA: RegExp(`getPbData\?(?=.*pn\=)(?=.*rn\=)(?=.*only_post\=)(?=.*kz\=)`, 'i'), // 评论页数据url
MAINPBDATA: RegExp(`getPbData\?(?=.*eqid\=)(?=.*refer\=)(?=.*pn\=)(?=.*rn\=)(?=.*format\=)(?=.*obj_param2\=)(?=.*kz\=)`, 'i'), // 主页数据url
PBDATA: RegExp(`getPbData\?.*pn\=.*`, 'i'), // 通用页面数据url
}
const PAGE_TYPE = { // 页面类型
UNKNOW: -1, // 未知
MAINPAGE: 0, // 主页
POSTPAGE: 1, // 评论页
FLOORREPLAYPAGE: 2, // 楼中楼页
};
let tieNode = undefined; // 一楼
let tiebaNameNode = undefined; // 吧名
let lzId = ""; // 楼主id
let currentHash = unsafeWindow.location.hash; // 存储当前页的Hash,页面变动的依据
let currentScrollYPos = undefined; // 存储当前滚动位置
let floorDataList = {} // 存储所有楼层数据以便搜索,索引为楼层数字符串,值为抓取的json。主要信息为pid,获取路径floorDataList[floor].id
let someKey = { // 一些用于网络请求的关键字,页面加载或变动时自自动更新
host: "tieba.baidu.com",
tid: "",
postAuthorId: "",
forumId: "",
}
// 获取屏幕DPI
const DPI = (function() {
let DPI = {
x: 160,
y: 160,
};
if ( window.screen.deviceXDPI) {
DPI.x = window.screen.deviceXDPI;
DPI.y = window.screen.deviceYDPI;
}
else {
let tmpNode = document.createElement( "DIV" );
tmpNode.style.cssText = "width:1in;height:1in;position:absolute;left:0px;top:0px;z-index:99;visibility:hidden";
document.body.appendChild( tmpNode );
DPI.x = parseInt( tmpNode.offsetWidth );
DPI.y = parseInt( tmpNode.offsetHeight );
tmpNode.parentNode.removeChild( tmpNode );
}
return DPI;
})();
// 将厘米转换为像素
function cm2pxX(cm) {
return (cm*DPI.x)/25.4;
}
function cm2pxY(cm) {
return (cm*DPI.y)/25.4;
}
function ignoreError(func) {
try {
func();
} catch (e) {
DEBUGLOG(e, STR_DEBUG_LABEL_ERROR);
}
}
function assert(condition, msg) {
if (!condition) {
throw new Error(`${msg}`)
}
}
const STR_DEBUG_LABEL_ERROR = "error";
function DEBUGLOG(msg, label = "") {
if (!IS_DEBUG) {
return;
}
let outputFunc = console.log;
if (label == STR_DEBUG_LABEL_ERROR) {
outputFunc = console.error;
}
outputFunc(`voeoc(DEBUG)<${label}>: ${msg}`);
}
// 阻止事件冒泡
function stopPropagation(event) {
event = event || unsafeWindow.event;
if (event.stopPropagation) {
event.stopPropagation();
} else {
event.cancelBubble = true;
}
return false;
}
function waitElementLoadedAsync(selector, TIME_OUT = 30, searchFunc = undefined) {
return new Promise((resolve, reject) => {
let findTimeNum = 0; // 记录查找的次数
let timer = setInterval(() => {
let element = undefined;
if (searchFunc) {
element = searchFunc(selector);
} else {
element = document.querySelector(selector);
}
DEBUGLOG(`${selector}=${element}`, "waitElementLoaded");
if (element != null) {
// 清除定时器
clearInterval(timer);
resolve(element);
} else {
findTimeNum++;
if (TIME_OUT < findTimeNum) {
// 清除定时器
clearInterval(timer);
reject(new Error(`${selector}=${element}`));
}
}
}, 200);
});
}
function waitElementLoaded(selector, func, TIME_OUT = 30, searchFunc = undefined, finalFunc = undefined) {
waitElementLoadedAsync(selector, TIME_OUT, searchFunc).then(func, function (error) {
if (finalFunc) {
finalFunc;
}
});
}
// 获取url参数
function getUrlAttr(url, attrName) {
return RegExp(`${attrName}=([^&]*)&?`, 'i').exec(url)[1].trim();
}
// 简单判断当前页面的类型
function getPageType(hash = unsafeWindow.location.hash) {
if (hash === "" || hash === "#/") {
return PAGE_TYPE.MAINPAGE;
} else if (VOEOC_REG.POSTPAGE.test(hash)) {
return PAGE_TYPE.POSTPAGE;
} else if (VOEOC_REG.FLOORREPLAYPAGE.test(hash)) {
return PAGE_TYPE.FLOORREPLAYPAGE;
}
return PAGE_TYPE.UNKNOW;
}
class CustomLzlExpandManager { // 实现楼中楼展开的逻辑管理器
static #STR_NEWOPENLZLTEXT = "展开评论"; // 打开楼中楼按钮的文本
static #STR_REMAINDOPENLZLTEXT = function (num) {
return `剩余${num}个评论`;
}
static #LZL_CONTENT_TYPE = { // 楼中楼评论内容元素类型
TEXT: 0, // 文本
EMOJI: 2, // 表情
USERNAME: 4, // 用户名,一般用作回复
};
#enable; // 按钮开关
#currentPageNum; // 当前展开页,用于网络请求
#pageSize; // 单个页面评论数量,至少为10
#originItemNodeList; // 原始楼中楼评论显示节点
#sampleItemNode; // 原始楼中楼评论样本
#lzTagHTML; // 一个楼主方框标记
#data_v_a; // 评论中的第一个dataset数据(data-v-***),用于还原样式
#data_v_b; // 评论中的第二个dataset数据(data-v-***),用于还原样式
#pid; // 楼层id
#floorNum; // 当前楼层的楼层数
get floorNum() { return this.#floorNum; }
// 存储的网页节点
#floorNode; // 当前楼层
#lzlNode; // 楼中楼
#expandBtnNode; // 楼中楼展开按钮
#expandBtnTextNode; // 楼中楼展开按钮的文本
#expandTimeoutTimer; // 超时处理器
constructor(floorNode, expandBtnNode) {
assert(floorNode, "floorNode is null");
assert(expandBtnNode, "expandBtnNode is null");
let self = this;
self.#enable = true;
self.#currentPageNum = 1;
self.#pageSize = settingsData.eachExpandSize.value < 10 ? 10 : settingsData.eachExpandSize.value; // 单个展开的页面评论数量,至少为10
self.#floorNode = floorNode; // 当前楼层节点
self.#expandBtnNode = expandBtnNode; // 楼中楼展开按钮
self.#lzlNode = self.#floorNode.querySelector("div.lzl-post");
self.#originItemNodeList = self.#lzlNode.getElementsByClassName("lzl-post-item");
self.#sampleItemNode = self.#originItemNodeList[0].cloneNode(true);
// 读取复制data-v
let dvlist = [];
for (let dv in self.#originItemNodeList[0].querySelector(".thread-text").dataset) {
dvlist.push(`data-v-${dv.slice(1).replace('-', '')}`);
}
self.#data_v_a = "data-v-aeeee";
self.#data_v_b = "data-v-beeee";
try {
self.#data_v_a = dvlist[0];
} catch (e) {
DEBUGLOG(e, STR_DEBUG_LABEL_ERROR);
}
try {
self.#data_v_b = dvlist[1];
} catch (e) {
DEBUGLOG(e, STR_DEBUG_LABEL_ERROR);
}
self.#lzTagHTML = `<svg ${self.#data_v_b}="" class="landlord"><use xlink:href="#icon_landlord"></use></svg>`;
let floorinfoNode = floorNode.querySelector(".floor-info"); // 楼层数元素
self.#floorNum = RegExp(`第([0-9]+)楼`, 'i').exec(floorinfoNode.innerHTML)[1].trim(); // 楼层数
self.#pid = floorDataList[self.#floorNum].id; // 楼层id
// 创建新按钮节点
self.#expandBtnTextNode = document.createElement("span");
self.#expandBtnTextNode.className = "open-app-text-real";
self.#expandBtnTextNode.innerHTML = CustomLzlExpandManager.#STR_NEWOPENLZLTEXT;
// 绑定长按事件
if (settingsData.isLongClickToOpenLzlPage.value) {
let timeOutEvent = 0;
const TIME_OUT = 500;
self.#expandBtnNode.ontouchstart = function () {
DEBUGLOG("ontouchstart", "ontouchstart")
timeOutEvent = setTimeout(function () {
timeOutEvent = 0;
// 执行长按
self.openLzlPage();
}, TIME_OUT);
return false;
}
self.#expandBtnNode.ontouchend = function () {
DEBUGLOG("ontouchend", "ontouchend")
clearTimeout(timeOutEvent);
if (timeOutEvent != 0) {
// 判断为单击
self.expandLzl();
}
return false;
}
self.#expandBtnNode.ontouchmove = function () {
DEBUGLOG("ontouchmove", "ontouchmove")
clearTimeout(timeOutEvent);
timeOutEvent = 0;
}
} else {
// 绑定展开按钮点击事件
self.#expandBtnNode.onclick = function () {
self.expandLzl();
}
}
// 替换新按钮
self.#expandBtnNode.insertBefore(self.#expandBtnTextNode, self.#expandBtnNode.children[0]);
}
/**
* 原地展开楼中楼评论
* @param {Boolean} isTheLast 是否为最后一次展开,避免无限递归
* @param {this} self *
*/
expandLzl(isTheLast = false, self = this) {
assert(self.#enable, `尝试展开不存在的评论区,楼层号${self.#floorNum}`);
DEBUGLOG(self.#floorNum, "expandLzl");
let url = `${unsafeWindow.origin}/mg/o/getFloorData?pn=${self.#currentPageNum}&rn=${self.#pageSize}&tid=${someKey.tid}&pid=${self.#pid}`;
DEBUGLOG(url, "expandLzl");
let abortFunc = GM_xmlhttpRequest({
method: "get",
url: url,
onload: function (details) {
self.#endExpandAnimation();
// 爬取解析楼中楼评论数据
let floorData = undefined;
let subpostlist = undefined;
try {
floorData = JSON.parse(details.responseText);
subpostlist = floorData.data.sub_post_list; // 评论列表
if (!subpostlist || subpostlist.length == 0) {
throw ("sub_post_list为空");
}
} catch (e) {
// 无法获取楼中楼数据
DEBUGLOG(`无法获取楼中楼数据,url:${url}\n错误:${e}`, STR_DEBUG_LABEL_ERROR);
self.#showError(true);
return;
}
// 复原颜色
self.#showError(false);
// 去掉前两个评论
if (self.#currentPageNum == 1) {
try {
for (let i = self.#originItemNodeList.length - 1; i > -1; i--) {
self.#lzlNode.removeChild(self.#originItemNodeList[i]);
}
} catch (e) {
DEBUGLOG(e, STR_DEBUG_LABEL_ERROR);
}
}
subpostlist.forEach(function (subpost) { // 遍历每一行评论
let contentHTML = ""; // 单行评论的HTML
subpost.content.forEach(function (subContent) { // 遍历单行评论的每一个元素
let itemHTML = ""; // 元素的HTML
switch (subContent.type) {
case CustomLzlExpandManager.#LZL_CONTENT_TYPE.EMOJI:
itemHTML = `<img ${self.#data_v_a}="" src="${subContent.src}" alt="${subContent.text}"class="emotion-img">`;
break;
case CustomLzlExpandManager.#LZL_CONTENT_TYPE.USERNAME:
if (subContent.uid == lzId) {
itemHTML = `<span ${self.#data_v_b}="" class="link username"> ${subContent.text} ${self.#lzTagHTML} </span>`;
} else {
itemHTML = `<span ${self.#data_v_a}="" class="user rich-link-disabled"> ${subContent.text} </span>`;
}
break;
case CustomLzlExpandManager.#LZL_CONTENT_TYPE.TEXT:
default:
// 如有其他的类型暂时用文本代替
itemHTML = `<span ${self.#data_v_a}="" class="text-content">${subContent.text}</span>`;
break;
}
contentHTML += itemHTML;
})
let newItemNode = self.#sampleItemNode.cloneNode(true); // 新的评论行
newItemNode.querySelector(".username").innerHTML = `${subpost.author.show_nickname} ${(lzId == subpost.author.id) ? self.#lzTagHTML : ""}:`;
newItemNode.querySelector(".thread-text").innerHTML = contentHTML;
self.#lzlNode.insertBefore(newItemNode, self.#expandBtnNode);
});
// 展开结束后处理剩余评论
let pageinfo = floorData.data.page; // 楼中楼信息,包括楼层数、页面大小、页面数量
let total_page = parseInt(pageinfo.total_page); // 总页数
if (total_page > self.#currentPageNum) { // 仍有剩余评论未展开
self.#currentPageNum++;
let total_num = parseInt(pageinfo.total_num);
let remaind_num = total_num - self.#pageSize * (self.#currentPageNum - 1);
self.#expandBtnNode.children[0].innerHTML = CustomLzlExpandManager.#STR_REMAINDOPENLZLTEXT(remaind_num);
if (settingsData.isRemaindAutoExpand.value && settingsData.remaindAutoExpandSize.value > remaind_num) { // 当剩余评论过少时自动展开
if (!isTheLast) { // 检查当前是否强制设置为为最后一次展开
self.expandLzl(true);
}
}
} else { // 所有评论已展开时隐藏展开按钮
self.#destroy.apply(self);
}
},
onerror: function (details) {
self.#endExpandAnimation();
self.#showError(true);
DEBUGLOG(`无法加载评论区,爬取的url为${details.responseURL}`, STR_DEBUG_LABEL_ERROR);
},
onabort: onerror,
ontimeout: onerror,
});
// 动画处理
self.#startExpandAnimation(abortFunc);
}
// 另一种打开楼中楼的方法,将页面加载到iframe弹框里
openLzlPage(self = this) {
if (!someKey.tid || !someKey.postAuthorId || !someKey.forumId) {
self.#showError();
return;
}
let newHash = `#/lzlPage?tid=${someKey.tid}&pid=${self.#pid}&floor=${self.floorNum}&postAuthorId=${someKey.postAuthorId}&forumId=${someKey.forumId}`;
DEBUGLOG(newHash, "openLzlPage hash");
LzlPage.getInstance().showAndReload(newHash);
}
#showError(isError = true, self = this) {
if (isError) {
self.#expandBtnTextNode.classList.add("error");
} else {
self.#expandBtnTextNode.classList.remove("error");
}
}
// 开始动画
#startExpandAnimation(abortFunc, self = this) {
self.#expandBtnNode.disabled = true;
self.#expandBtnNode.classList.add("loading");
self.#expandTimeoutTimer = setTimeout(function () {
self.#endExpandAnimation();
self.#showError(true);
DEBUGLOG(`加载异常,并且超时未处理`, STR_DEBUG_LABEL_ERROR);
abortFunc();
}, 5000);
}
// 结束动画
#endExpandAnimation(self = this) {
self.#expandBtnNode.disabled = false;
self.#expandBtnNode.classList.remove("loading");
if (self.#expandTimeoutTimer) {
clearTimeout(self.#expandTimeoutTimer);
self.#expandTimeoutTimer = undefined;
}
}
#destroy(self = this) {
try {
self.#enable = false;
self.click = undefined;
self.#expandBtnNode.style.display = "none";
self.#expandBtnNode.parentNode.removeChild(self.#expandBtnNode);
} finally {
delete this;
}
}
}
class LzlPageSlideDownController {
static #MAX_SLIDE_DISTANCE = cm2pxY(20); // 滑动距离大于此值,则隐藏窗口,单位为像素
#iframeWindow;
#minTouchY = undefined; // (当数字与undefined比较时,始终为false)
#lastTouchY = 0;
onslidedown;
constructor(onslidedown = undefined) {
let self = this;
self.onslidedown = onslidedown;
}
setIframeWindow(iframeWindow, self = this) {
self.#iframeWindow = iframeWindow;
iframeWindow.ontouchend = function (event) {
self.#touchend(event);
};
iframeWindow.ontouchmove = function (event) {
self.#touchmove(event);
};
}
#triggerSlideAbort(self = this) {
self.#minTouchY = undefined;
self.#lastTouchY = 0;
}
#touchmove(event, self = this) {
if (event.touches.length != 1) { // 只响应单指滑动事件
return stopPropagation(event);
}
if (event.changedTouches.length != 1) { // 并不会触发
self.#triggerSlideAbort();
return stopPropagation(event);
}
let touchY = event.changedTouches[0].screenY;
if (self.#iframeWindow.scrollY == 0 && self.#lastTouchY < touchY) { // 在顶部继续下滑
if (!(self.#minTouchY <= touchY)) { // 存储最小的touchY
self.#minTouchY = touchY;
}
} else { // 正常滚动
}
self.#lastTouchY = touchY;
return true;
}
#touchend(event, self = this) {
try {
let touchY = event.changedTouches[0].screenY;
DEBUGLOG(`${touchY} - ${self.#minTouchY} > ${LzlPageSlideDownController.#MAX_SLIDE_DISTANCE}`, "touchend");
if (touchY - self.#minTouchY > LzlPageSlideDownController.#MAX_SLIDE_DISTANCE) {
self.onslidedown();
return stopPropagation(event);
}
} finally {
self.#triggerSlideAbort();
return true;
}
}
}
class LzlPage {
#lzlPageSlideDownController = new LzlPageSlideDownController(this.hide.bind(this));
// HTML节点
#lzlPageNode;
#lzlPageIframeNode;
#reloadBtnNode;
#closeBtnNode;
#lzlPageBackgroundNode;
// 单例模式
static #instance = undefined;
static #IS_NOW_CREATE_SINGLETON = false;
static getInstance() {
if (!LzlPage.#instance) {
LzlPage.#IS_NOW_CREATE_SINGLETON = true;
return LzlPage.#instance = new LzlPage();
}
return LzlPage.#instance;
}
constructor() {
assert(LzlPage.#IS_NOW_CREATE_SINGLETON && !LzlPage.#instance, "非法构建,只允许一个实例,请调用getInstance()获取对象");
assert(!document.getElementById(STR_ID_LZLPAGEIFRAME), "已存在楼中楼弹框id,请检查代码");
LzlPage.#IS_NOW_CREATE_SINGLETON = false;
let self = this;
// 楼中楼展示页
self.#lzlPageNode = document.createElement("div");
self.#lzlPageNode.id = STR_ID_LZLPAGE;
self.#lzlPageNode.ontouchmove = stopPropagation;
self.#lzlPageNode.onscroll = stopPropagation;
// 刷新按钮
self.#reloadBtnNode = document.createElement("div");
self.#reloadBtnNode.className = "lzl-nav-btn lzl-reload-btn";
self.#reloadBtnNode.innerHTML = HTML_SVG_RELOAD_BTN;
self.#reloadBtnNode.reloadTimeOut = undefined;
self.#reloadBtnNode.startLoading = function () {
self.#reloadBtnNode.classList.add("loading");
self.#reloadBtnNode.reloadTimeOut = setTimeout(function () {
self.#reloadBtnNode.classList.remove("loading");
self.#reloadBtnNode.classList.add("error");
}, 3000)
}
self.#reloadBtnNode.finishLoading = function () {
self.#reloadBtnNode.classList.remove("loading");
clearTimeout(self.#reloadBtnNode.reloadTimeOut);
self.#reloadBtnNode.reloadTimeOut = undefined;
self.#reloadBtnNode.classList.remove("error");
}
// 关闭按钮
self.#closeBtnNode = document.createElement("div");
self.#closeBtnNode.className = "lzl-nav-btn";
self.#closeBtnNode.style.cssText = "margin-right:.1rem;";
self.#closeBtnNode.innerHTML = HTML_SVG_CLOSE_BTN;
// 用于加载实际页面的iframe
self.#lzlPageIframeNode = undefined;
// 背景板
self.#lzlPageBackgroundNode = document.createElement("div");
self.#lzlPageBackgroundNode.id = STR_ID_LZLPAGEBACKGROUND;
// 用于拦截滚动链
let lzlPageScrollContentNode = document.createElement("div");
lzlPageScrollContentNode.style.cssText = "height:101%;";
// 点击事件
function hide() {
self.hide();
}
self.#lzlPageBackgroundNode.onclick = hide;
lzlPageScrollContentNode.onclick = hide;
self.#reloadBtnNode.onclick = function() {
self.reload();
};
self.#closeBtnNode.onclick = hide;
// 添加元素到页面
self.#lzlPageNode.appendChild(self.#lzlPageBackgroundNode);
self.#lzlPageNode.appendChild(lzlPageScrollContentNode);
self.#lzlPageNode.appendChild(self.#reloadBtnNode);
document.body.insertBefore(self.#lzlPageNode, document.body.children[0]);
// 监听后退按钮
unsafeWindow.addEventListener("popstate", function (event) {
DEBUGLOG(unsafeWindow.history.state.id)
if (self.isShown()) {
self.hide();
}
}, false);
}
show(self = this) {
if (!self.isShown()) {
self.#lzlPageNode.classList.add("show");
unsafeWindow.history.pushState(HISTORY_STATE_LZL_PAGE, HISTORY_STATE_LZL_PAGE.title);
}
}
hide(self = this) {
if (self.isShown()) {
if(unsafeWindow.history.state.id == HISTORY_STATE_LZL_PAGE.id) { // 当前历史记录的state没有改变,说明事件不是后退键触发的
window.history.back(); // 模拟后退键,改变当前页面的state
return;
}
// 正式隐藏
self.#lzlPageNode.classList.remove("show");
}
}
isShown(self = this) {
return self.#lzlPageNode.classList.contains("show");
}
#createNewLzlPageIframe(src = "", self = this) {
// 楼中楼iframe加载器
if (self.#lzlPageIframeNode) {
self.#lzlPageIframeNode.src = "";
self.#lzlPageIframeNode.parentNode.removeChild(self.#lzlPageIframeNode);
self.#lzlPageIframeNode = undefined;
}
self.#lzlPageIframeNode = document.createElement("iframe");
self.#lzlPageIframeNode.id = STR_ID_LZLPAGEIFRAME;
self.#lzlPageIframeNode.setAttribute("src", src);
self.#lzlPageIframeNode.setAttribute("frameborder", "0");
self.#lzlPageNode.insertBefore(self.#lzlPageIframeNode, self.#lzlPageBackgroundNode);
}
#lastLoadHash;
#reloadNum;
async reload(hash, isBanReload = false, self = this) {
if (isBanReload) { // 禁止重复加载同一页面
try {
if (self.#lzlPageIframeNode.contentWindow.location.hash == hash) {
return;
}
} catch (error) {
DEBUGLOG(error, STR_DEBUG_LABEL_ERROR);
}
}
// 递归(刷新)次数限制
if (self.#reloadNum > 2) {
return;
}
// 检查重复
if (!hash) {
hash = self.#lastLoadHash;
} else {
if (hash == self.#lastLoadHash) {
self.#reloadNum++;
} else {
self.#reloadNum = 0;
}
}
// 开始载入
self.#createNewLzlPageIframe(hash);
self.#lastLoadHash = hash;
// 获取载入结果
self.#reloadBtnNode.startLoading();
let navbar = undefined;
try {
navbar = await waitElementLoadedAsync(".nav-bar-top", 5,
function (selector) { // 搜索函数
return document.getElementById(STR_ID_LZLPAGEIFRAME).contentDocument.querySelector(selector);
});
} catch (error) { // 查找失败,说明没有加载成功
DEBUGLOG(error, STR_DEBUG_LABEL_ERROR);
self.reload(); // 递归刷新
return;
}
// 处理载入
try {
let iframeWindow = self.#lzlPageIframeNode.contentWindow;
// 拦截滚动链
iframeWindow.document.getElementsByTagName("HTML")[0].style.overscrollBehavior = "contain";
// 添加下滑隐藏功能
iframeWindow.onscroll = stopPropagation;
self.#lzlPageSlideDownController.setIframeWindow(iframeWindow);
// 隐藏多余按钮
let backBtnNode = navbar.querySelector(".logo-wrapper");
let openAppBtnNode = navbar.querySelector(".more-btn-desc");
backBtnNode.disabled = true;
backBtnNode.style.visibility = "hidden";
openAppBtnNode.style.display = "none";
// 添加关闭按钮
navbar.replaceChild(self.#closeBtnNode, openAppBtnNode);
} finally {
// 结束动画。若无法执行到此处,则会自动超时显示变成错误状态
self.#reloadBtnNode.finishLoading();
}
}
showAndReload(hash, self = this) {
// 设置评论页为显示
self.show();
// 载入,并禁止重复载入同一hash
self.reload(hash, true);
}
}
// 开始监听楼层改变
function startListenFloorParentNodeChange(floorParentNode) {
// 当有新楼层加载时调用
function onNewFloorAdded(floorNode) {
if (floorNode.classList.contains(STR_VOEOCMARK)) { // 已被打上标记
return;
}
floorNode.classList.add(STR_VOEOCMARK); // 手动标记,避免重复操作
try {
let expandBtnNode = floorNode.querySelector(".open-app-guide"); // 楼中楼展开按钮
if (expandBtnNode) {
let newCustomLzlExpandManager = new CustomLzlExpandManager(floorNode, expandBtnNode);
if (settingsData.isAutoExpand.value) {
DEBUGLOG(newCustomLzlExpandManager.floorNum, "AutoExpand");
newCustomLzlExpandManager.expandLzl();
}
}
} catch (e) {
DEBUGLOG(e, STR_DEBUG_LABEL_ERROR);
}
};
// 使用新按钮刷新楼层
function searchAndUpdatePostPage() {
// 遍历所有新加的楼层元素
let floorNodeList = floorParentNode.querySelectorAll(`div.post-item:not(.${STR_VOEOCMARK})`);
floorNodeList.forEach(onNewFloorAdded);
}
// 注册(不可用)楼层元素添加事件
let observer = new MutationObserver(function (mutationList) {
searchAndUpdatePostPage();
});
observer.observe(floorParentNode, {
attributes: false,
childList: true,
characterData: false,
subtree: false,
});
searchAndUpdatePostPage();
}
// 检测URL Hash变化,当force为true时,无论是否变化均执行后续任务
function checkUrlHashChange(force = false) {
if (currentHash != unsafeWindow.location.hash) {
currentHash = unsafeWindow.location.hash;
} else {
if (!force) {
return false;
}
}
let pageType = getPageType();
if (pageType == PAGE_TYPE.POSTPAGE) { // 页面变动为评论页
// 收集url数据
someKey = {
host: unsafeWindow.location.hostname,
tid: getUrlAttr(currentHash, "tid"),
postAuthorId: getUrlAttr(currentHash, "postAuthorId"),
forumId: getUrlAttr(currentHash, "forumId"),
}
// 当页面变动时,刷新展开楼层的按钮
waitElementLoaded(".post-page-list", (postpagelist) => { // 等待页面加载完成
startListenFloorParentNodeChange(postpagelist);
}, 10);
// 恢复一楼显示
restore();
// 页面切换后恢复滚动位置
scrollTo(currentScrollYPos);
} else if (pageType == PAGE_TYPE.MAINPAGE) { // 页面变动为主页
if (tieNode) {
// 当页面变动时,刷新展开楼层的按钮
waitElementLoaded(".pb-page-wrapper", (pbpageNode) => { // 等待页面加载完成
startListenFloorParentNodeChange(pbpageNode);
}, 10);
// 将剪切走的一楼复制回来
waitElementLoaded("#replySwitch", (splitlineNode) => { // 等待页面加载完成
splitlineNode.parentNode.insertBefore(tieNode, splitlineNode);
}, 10);
scrollTo(0);
}
}
return true;
}
// 滚动到指定y坐标(如果当前楼层数比较大,只能滚动到贴末尾的最大加载位置)
function scrollTo(yPos) {
waitElementLoaded(".post-page", (_) => { // 等待页面加载完成
document.documentElement.scrollTop = yPos;
// 在一定时间内维持滚动位置
setTimeout(function () {
document.documentElement.scrollTop = yPos;
DEBUGLOG(yPos, "scrollTo");
}, 200);
});
}
// 显示一楼的内容
function restore() {
DEBUGLOG("restore")
waitElementLoaded(".text", (titletextNode) => { // 等待标题位置加载
// 显示贴吧名
try {
let tiebaNameCloneNode = tiebaNameNode.cloneNode(true);
titletextNode.parentNode.replaceChild(tiebaNameCloneNode, titletextNode);
// 关联点击贴吧名的事件
tiebaNameCloneNode.onclick = function () {
tiebaNameNode.click();
}
} catch (e) { DEBUGLOG(e, STR_DEBUG_LABEL_ERROR); }
// 显示楼主发帖层
try {
// 复原样式丢失
tieNode.style.cssText = `
margin-left: 0.12rem;
margin-right: 0.12rem;
margin-bottom: 0.25rem;
`
// 尝试找回楼主丢失的头像
try {
let lzavatarNode = tieNode.querySelector(".avatar");
lzavatarNode.style.backgroundImage = `url("${lzavatarNode.getAttribute("data-src")}")`
} catch (e) { DEBUGLOG(e, STR_DEBUG_LABEL_ERROR); }
// 尝试复原发帖内容的字体样式
try {
let textContentNode = tieNode.querySelector(".thread-text"); // 一楼的文字内容
textContentNode.style.cssText = `
margin-top: 0.18rem;
font-size: 0.16rem;
line-height: 0.28rem;
`
} catch (e) { DEBUGLOG(e, STR_DEBUG_LABEL_ERROR); }
let replySwitchNode = document.querySelector("#replySwitch"); // 标题下方的分割
replySwitchNode.parentNode.insertBefore(tieNode, replySwitchNode);
} catch (e) { DEBUGLOG(e, STR_DEBUG_LABEL_ERROR); }
// 尝试复原标题样式
try {
let threadtitleNode = document.querySelector(".thread-title");
let isTopTitle = true;
if (!threadtitleNode) {
threadtitleNode = document.querySelector(".bottom-thread-title");
isTopTitle = false;
}
threadtitleNode.style.cssText = `
margin-bottom: 0.13rem;
font-size: 0.22rem;
font-weight: 700;
line-height: 0.33rem;
`
// 置顶标题显示
if (settingsData.isFixedTitle.value && isTopTitle) {
let threadtitleCloneNode = threadtitleNode.cloneNode(true)
threadtitleNode.style.visibility = "hidden";
threadtitleCloneNode.style.cssText += `
position: fixed !important;
z-index: 99 !important;
opacity: 0.8 !important;
background-color: #FFFFFF !important;
`
threadtitleNode.parentNode.insertBefore(threadtitleCloneNode, threadtitleNode)
}
} catch (e) { DEBUGLOG(e, STR_DEBUG_LABEL_ERROR); }
// 初始化楼中楼弹出框
LzlPage.getInstance();
})
}
// 解析传过来的PBDATA的json
function parsePbData(responseText, responseURL) {
let data = undefined;
try {
if (typeof responseText == "object") {
data = responseText.data;
} else {
data = JSON.parse(responseText).data;
}
} catch (e) {
DEBUGLOG(e, STR_DEBUG_LABEL_ERROR);
return;
}
let post_list = data.post_list;
// 获取楼主id
try {
if (getUrlAttr(responseURL, "pn") == "1") { // 楼主id存在1楼数据中
lzId = post_list[0].author.id;
DEBUGLOG(lzId, "lzId");
}
} catch (e) {
DEBUGLOG("无法获取楼主id", STR_DEBUG_LABEL_ERROR);
}
// 获取内部参数
try {
someKey.tid = data.forum.id;
} catch (e) {
DEBUGLOG("该json并暂无tid信息", STR_DEBUG_LABEL_ERROR);
}
// 获取楼层信息
for (let i = 0; i < post_list.length; i++) {
let d = post_list[i];
floorDataList[d.floor] = d;
}
}
function registerMenuCommand() {
function generateCheckboxHTML(label, settingValue, onChangeHTML = "") {
return `
<input id="${settingValue.key}" class="voeoc-swal-input-checkbox" type="checkbox" ${settingValue.value ? "checked" : ""} value="" onload="onchange()" onchange="${onChangeHTML}">
<label for="${settingValue.key}">${label}</label>
<br>`;
}
function generateNumberInputboxHTML(label, settingValue, disabled = false) {
let id_input = settingValue.key;
let id_show_value = `${settingValue.key}-show`;
let min = settingValue.range.min;
let max = settingValue.range.max;
return `
<span>${label}(<span id='${id_show_value}'>${settingValue.value}</span>):</span>
<br>
<input class="voeoc-swal-range" id="${id_input}" ${disabled ? "disabled='true'" : ""} type="range" min="${min}" max="${max}" step="1" value="${settingValue.value}" onload="onchange()" oninput="onchange()" onchange="document.getElementById('${id_show_value}').innerHTML=this.value;">
<br>`;
}
function getCheckboxValue(settingValue) {
ignoreError(function () {
let newValue = Boolean(document.getElementById(`${settingValue.key}`).checked);
DEBUGLOG(newValue, "newValue");
settingValue.value = newValue;
});
}
function getNumberInputboxValue(settingValue) {
ignoreError(function () {
let newValue = parseInt(document.getElementById(`${settingValue.key}`).value);
DEBUGLOG(newValue, "newValue");
settingValue.value = newValue;
});
}
let menuId = GM_registerMenuCommand(`设置`,
async function () {
const Toast = Swal.mixin({
position: 'bottom',
animation: false,
})
const { value: result } = await Toast.fire({
html: `
<div style="text-align: left;">
${generateCheckboxHTML("长按打开楼中楼页", settingsData.isLongClickToOpenLzlPage)}
${generateCheckboxHTML("楼中楼自动展开", settingsData.isAutoExpand)}
${generateCheckboxHTML("楼中楼评论过少时展开", settingsData.isRemaindAutoExpand, `document.getElementById('${settingsData.remaindAutoExpandSize.key}').disabled=!this.checked;`)}
${generateNumberInputboxHTML("楼中楼展开的大小", settingsData.eachExpandSize)}
${generateNumberInputboxHTML("自动展开剩余评论", settingsData.remaindAutoExpandSize, !settingsData.isRemaindAutoExpand.value)}
</div>`,
focusConfirm: true,
showCancelButton: true,
confirmButtonText: "保存",
cancelButtonText: "取消",
width: "100%",
padding: '0.03rem',
background: "#19191acc",
preConfirm: () => {
getCheckboxValue(settingsData.isLongClickToOpenLzlPage);
getCheckboxValue(settingsData.isAutoExpand);
getCheckboxValue(settingsData.isRemaindAutoExpand);
getNumberInputboxValue(settingsData.eachExpandSize);
getNumberInputboxValue(settingsData.remaindAutoExpandSize);
reloadSettingsData();
return true;
}
})
if (!result) { // 点击了取消按钮
}
}, "VOEOC_MENU_ACCESS_KEY_SETTINGS");
}
(function main() {
GM_listValues();
GM_addStyle(GM_getResourceText("swal_css"));
registerMenuCommand();
const STR_LZL_PAGE_TRANSITION_DURATION = "0.2s";
GM_addStyle(`
.comment-box, .only-lz, .nav-bar-bottom, .open-app, .more-image-desc {
display: none !important;
}
.logo-wrapper {
visibility: hidden !important;
pointer-events: none !important;
height: 0;
}
.open-app-text {
display: none !important;
}
.open-app-text-real {
display: block !important;
-webkit-box-flex: 0;
-webkit-flex: none;
-ms-flex: none;
flex: none;
font-size: .13rem;
color: #614ec2;
}
.open-app-text-real.error {
color: #ff3366 !important;
text-decoration: line-through;
}
@keyframes rotate3d {
0%{-webkit-transform:rotate3d(1, 0, 0, 0deg);}
25%{-webkit-transform:rotate3d(1, 0, 0, 90deg);}
50%{-webkit-transform:rotate3d(1, 0, 0, 180deg);}
75%{-webkit-transform:rotate3d(1, 0, 0, 270deg);}
100%{-webkit-transform:rotate3d(1, 0, 0, 360deg);}
}
.open-app-guide.loading {
animation: rotate3d 0.5s linear infinite;
pointer-events: none;
}
.voeoc-swal-input {
position: center;
}
.voeoc-swal-input-checkbox {
height: 0.15rem;
width: 0.15rem;
}
.voeoc-swal-range {
width: 100%;
}
.swal2-html-container {
font-size: 0.15rem !important;
line-height: 0.5rem !important;
}
.swal2-styled {
font-size: 0.15rem !important;
}
.swal2-container {
max_width: 640px !important;
}
#${STR_ID_LZLPAGE} {
position: fixed;
overscroll-behavior: none;
width: 100%;
z-index: 999;
height: 0;
visibility: hidden;
transition: visibility ${STR_LZL_PAGE_TRANSITION_DURATION};
overflow: scroll;
}
#${STR_ID_LZLPAGE}::-webkit-scrollbar { width: 0 !important }
#${STR_ID_LZLPAGEBACKGROUND} {
position: fixed;
width: 100%;
height: 200%;
background-color: #00000077;
z-index: -1;
opacity: 0;
transition: opacity ${STR_LZL_PAGE_TRANSITION_DURATION};
}
#${STR_ID_LZLPAGEIFRAME} {
position: fixed;
width: 100%;
height: 0%;
background-color: #ffffff;
bottom: 0;
transition: height ${STR_LZL_PAGE_TRANSITION_DURATION};
overflow: hidden;
}
.lzl-nav-btn.lzl-reload-btn {
position:fixed;
margin: 0.1rem;
top:20%;
margin-left: -0.5rem;
transition: margin-left ${STR_LZL_PAGE_TRANSITION_DURATION};
}
#${STR_ID_LZLPAGE}.show {
display: block;
height: 100%;
visibility: visible;
}
#${STR_ID_LZLPAGE}.show > #${STR_ID_LZLPAGEIFRAME} {
height: 80%;
}
#${STR_ID_LZLPAGE}.show > #${STR_ID_LZLPAGEBACKGROUND} {
opacity: 1;
}
#${STR_ID_LZLPAGE}.show > .lzl-reload-btn {
margin-left: 0.1rem;
}
.lzl-nav-btn {
width: .32rem;
height: .32rem;
display: flex;
background-color:#d0d0d0d1;
border-radius:50%;
}
.lzl-nav-btn > svg{
position: relative;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
@keyframes rotate {
0%{-webkit-transform:rotate(0deg);}
25%{-webkit-transform:rotate(90deg);}
50%{-webkit-transform:rotate(180deg);}
75%{-webkit-transform:rotate(270deg);}
100%{-webkit-transform:rotate(360deg);}
}
.lzl-nav-btn.loading {
animation: rotate 0.5s linear infinite;
pointer-events: none;
}
.lzl-reload-btn.error {
background-color: #ff000070 !important;
}
`);
// 监听楼层加载的网络事件
(function () {
let oldXHR = unsafeWindow.XMLHttpRequest;
unsafeWindow.XMLHttpRequest = function () {
let realXHR = new oldXHR();
realXHR.addEventListener('readystatechange', function () {
DEBUGLOG(realXHR.responseURL, "realXHR.responseURL");
if (VOEOC_REG.PBDATA.test(realXHR.responseURL) && realXHR.response != "") {
parsePbData(realXHR.response, realXHR.responseURL);
}
}, false);
return realXHR;
}
})();
// 通过监听页面滚动变化获取页面变动情况
unsafeWindow.onscroll = function () {
checkUrlHashChange();
if (getPageType() == PAGE_TYPE.POSTPAGE) { // 只会记录评论页的滚动位置
if (unsafeWindow.pageYOffset != 0) {
// 记录当前滚动位置
currentScrollYPos = unsafeWindow.pageYOffset;
//DEBUGLOG(scrollPos, "scrollPos");
}
}
}
// 页面变动时触发检查
unsafeWindow.onhashchange = function (e) {
checkUrlHashChange();
}
// 首次进入,对于不同的页面采取不同的行为
switch (getPageType()) {
case PAGE_TYPE.MAINPAGE: // 首次进入主页
waitElementLoaded("div.nav-bar-v2-fixed:nth-child(1)", (navbarfixed) => {
tiebaNameNode = document.querySelector(".forum-block"); // 获取吧名
tieNode = document.querySelector(".main-thread-content"); // 获取楼主发帖内容
let postbtn = document.querySelector(".post-page-entry-btn"); // 展开评论页的按钮
// 点击展开按钮
try {
postbtn.click();
// 手动触发页面刷新检测
checkUrlHashChange();
} catch (e) { // 展开按钮不存在,可能是楼层太少了
DEBUGLOG(postbtn, "postbtn");
// 页面开始加载时监听会大概率失效,所以这里只能主动触发抓取楼层信息的请求,发起后交给监听程序
let url = `${unsafeWindow.origin}/mg/p/getPbData?kz=${RegExp(`${unsafeWindow.origin}/p/([0-9]*)?#?/?`, 'i').exec(unsafeWindow.location.href)[1].trim()}&obj_param2=firefox&format=json&eqid=&refer=&pn=1&rn=5`;
DEBUGLOG(url, "url");
GM_xmlhttpRequest({
method: "get",
url: url,
onload: function (details) {
DEBUGLOG(details.responseText, "GM_xmlhttpRequest onload");
parsePbData(details.responseText, url);
// 强制手动触发页面刷新
checkUrlHashChange(true);
},
onerror: function (details) {
DEBUGLOG(`获取主页数据失败,url:${details.responseURL}`, STR_DEBUG_LABEL_ERROR);
},
});
}
})
break;
case PAGE_TYPE.POSTPAGE:
// 如果当前刷新加载的是评论页,则需要先打开主页面获取数据
unsafeWindow.location.hash = "";
unsafeWindow.location.reload();
break;
case PAGE_TYPE.FLOORREPLAYPAGE:
break;
default:
break;
}
})()
})();