手机百度贴吧自动展开楼层

有时候用手机的浏览器打开百度贴吧,只想看一眼就走,并不想打开APP,这个脚本用于帮助用户自动展开楼层。注意:只支持手机浏览器,测试环境为Iceraven+Tampermonkey

目前为 2022-06-18 提交的版本。查看 最新版本

// ==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: "&times;", 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;
        }

    })()
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址